mirror of
https://github.com/Vikeo/LifeTrinket.git
synced 2025-11-14 15:07:59 +00:00
create health component
This commit is contained in:
@@ -127,6 +127,10 @@ export const CommanderDamage = ({
|
|||||||
const [timeoutFinished, setTimeoutFinished] = useState(false);
|
const [timeoutFinished, setTimeoutFinished] = useState(false);
|
||||||
const [hasPressedDown, setHasPressedDown] = useState(false);
|
const [hasPressedDown, setHasPressedDown] = useState(false);
|
||||||
|
|
||||||
|
const isSide =
|
||||||
|
player.settings.rotation === Rotation.Side ||
|
||||||
|
player.settings.rotation === Rotation.SideFlipped;
|
||||||
|
|
||||||
const handleCommanderDamageChange = (
|
const handleCommanderDamageChange = (
|
||||||
index: number,
|
index: number,
|
||||||
increment: number,
|
increment: number,
|
||||||
@@ -189,9 +193,9 @@ export const CommanderDamage = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const opponentIndex = opponent.index;
|
const opponentIndex = opponent.index;
|
||||||
const fontSize = '6vmin';
|
const fontSize = isSide ? '4vmax' : '7vmin';
|
||||||
const fontWeight = 'bold';
|
const fontWeight = 'bold';
|
||||||
const strokeWidth = '0.5vmin';
|
const strokeWidth = isSide ? '0.4vmax' : '0.7vmin';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CommanderDamageContainer
|
<CommanderDamageContainer
|
||||||
|
|||||||
@@ -29,24 +29,6 @@ export const StyledExtraCounterButton = styled.button`
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const CenteredText = styled.div`
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
font-size: 6vmin;
|
|
||||||
font-weight: bold;
|
|
||||||
-webkit-text-stroke: 0.4vmin #ffffff;
|
|
||||||
-webkit-text-fill-color: #000000;
|
|
||||||
color: #b5b2b2;
|
|
||||||
user-select: none;
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
-moz-user-select: -moz-none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const IconContainer = styled.div<{
|
const IconContainer = styled.div<{
|
||||||
rotation: number;
|
rotation: number;
|
||||||
}>`
|
}>`
|
||||||
@@ -64,6 +46,13 @@ const IconContainer = styled.div<{
|
|||||||
}}
|
}}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const TextContainer = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
translate: -50%;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
`;
|
||||||
|
|
||||||
type ExtraCounterProps = {
|
type ExtraCounterProps = {
|
||||||
Icon: ReactNode;
|
Icon: ReactNode;
|
||||||
counterTotal?: number;
|
counterTotal?: number;
|
||||||
@@ -83,6 +72,9 @@ const ExtraCounter = ({
|
|||||||
const [timeoutFinished, setTimeoutFinished] = useState(false);
|
const [timeoutFinished, setTimeoutFinished] = useState(false);
|
||||||
const [hasPressedDown, setHasPressedDown] = useState(false);
|
const [hasPressedDown, setHasPressedDown] = useState(false);
|
||||||
|
|
||||||
|
const isSide =
|
||||||
|
rotation === Rotation.Side || rotation === Rotation.SideFlipped;
|
||||||
|
|
||||||
const handleCountChange = (increment: number) => {
|
const handleCountChange = (increment: number) => {
|
||||||
if (!counterTotal) {
|
if (!counterTotal) {
|
||||||
setCounterTotal(increment, type);
|
setCounterTotal(increment, type);
|
||||||
@@ -115,6 +107,10 @@ const ExtraCounter = ({
|
|||||||
setHasPressedDown(false);
|
setHasPressedDown(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fontSize = isSide ? '4vmax' : '7vmin';
|
||||||
|
const fontWeight = 'bold';
|
||||||
|
const strokeWidth = isSide ? '0.4vmax' : '0.7vmin';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExtraCounterContainer>
|
<ExtraCounterContainer>
|
||||||
<StyledExtraCounterButton
|
<StyledExtraCounterButton
|
||||||
@@ -127,13 +123,15 @@ const ExtraCounter = ({
|
|||||||
>
|
>
|
||||||
<IconContainer rotation={rotation}>
|
<IconContainer rotation={rotation}>
|
||||||
{Icon}
|
{Icon}
|
||||||
|
<TextContainer>
|
||||||
<OutlinedText
|
<OutlinedText
|
||||||
fontSize="6vmin"
|
fontSize={fontSize}
|
||||||
fontWeight="bold"
|
fontWeight={fontWeight}
|
||||||
strokeWidth="0.6vmin"
|
strokeWidth={strokeWidth}
|
||||||
>
|
>
|
||||||
{counterTotal ? counterTotal : undefined}
|
{counterTotal ? counterTotal : undefined}
|
||||||
</OutlinedText>
|
</OutlinedText>
|
||||||
|
</TextContainer>
|
||||||
</IconContainer>
|
</IconContainer>
|
||||||
</StyledExtraCounterButton>
|
</StyledExtraCounterButton>
|
||||||
</ExtraCounterContainer>
|
</ExtraCounterContainer>
|
||||||
|
|||||||
@@ -23,10 +23,6 @@ export const StyledLifeCounterButton = styled.button`
|
|||||||
-moz-user-select: -moz-none;
|
-moz-user-select: -moz-none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
-ms-user-select: none;
|
-ms-user-select: none;
|
||||||
@media (orientation: landscape) {
|
|
||||||
max-width: 50vmin;
|
|
||||||
max-height: 50vmax;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const TextContainer = styled.div<{
|
const TextContainer = styled.div<{
|
||||||
@@ -34,7 +30,6 @@ const TextContainer = styled.div<{
|
|||||||
rotation: number;
|
rotation: number;
|
||||||
}>`
|
}>`
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -10%;
|
|
||||||
|
|
||||||
${(props) => {
|
${(props) => {
|
||||||
if (
|
if (
|
||||||
@@ -45,13 +40,11 @@ const TextContainer = styled.div<{
|
|||||||
return css`
|
return css`
|
||||||
rotate: -90deg;
|
rotate: -90deg;
|
||||||
bottom: 25%;
|
bottom: 25%;
|
||||||
left: -10%;
|
|
||||||
top: auto;
|
top: auto;
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
return css`
|
return css`
|
||||||
rotate: -90deg;
|
rotate: -90deg;
|
||||||
left: -10%;
|
|
||||||
top: 25%;
|
top: 25%;
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -117,7 +110,7 @@ const LifeCounterButton = ({
|
|||||||
const fontSize =
|
const fontSize =
|
||||||
rotation === Rotation.SideFlipped || rotation === Rotation.Side
|
rotation === Rotation.SideFlipped || rotation === Rotation.Side
|
||||||
? '8vmax'
|
? '8vmax'
|
||||||
: '16vmin';
|
: '12vmin';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledLifeCounterButton
|
<StyledLifeCounterButton
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Player } from '../../Types/Player';
|
import { Player } from '../../Types/Player';
|
||||||
|
import { WakeLock } from '../../Types/WakeLock';
|
||||||
import LifeCounter from '../LifeCounter/LifeCounter';
|
import LifeCounter from '../LifeCounter/LifeCounter';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ type CountersProps = {
|
|||||||
onPlayerChange: (updatedPlayer: Player) => void;
|
onPlayerChange: (updatedPlayer: Player) => void;
|
||||||
gridAreas: string;
|
gridAreas: string;
|
||||||
resetCurrentGame: () => void;
|
resetCurrentGame: () => void;
|
||||||
|
wakeLock: WakeLock;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Counters = ({
|
const Counters = ({
|
||||||
@@ -47,6 +49,7 @@ const Counters = ({
|
|||||||
onPlayerChange,
|
onPlayerChange,
|
||||||
gridAreas,
|
gridAreas,
|
||||||
resetCurrentGame,
|
resetCurrentGame,
|
||||||
|
wakeLock,
|
||||||
}: CountersProps) => {
|
}: CountersProps) => {
|
||||||
return (
|
return (
|
||||||
<CountersWrapper>
|
<CountersWrapper>
|
||||||
@@ -65,6 +68,7 @@ const Counters = ({
|
|||||||
)}
|
)}
|
||||||
onPlayerChange={onPlayerChange}
|
onPlayerChange={onPlayerChange}
|
||||||
resetCurrentGame={resetCurrentGame}
|
resetCurrentGame={resetCurrentGame}
|
||||||
|
wakeLock={wakeLock}
|
||||||
/>
|
/>
|
||||||
</GridItemContainer>
|
</GridItemContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,15 +11,31 @@ import {
|
|||||||
Poison,
|
Poison,
|
||||||
} from '../../Icons/generated';
|
} from '../../Icons/generated';
|
||||||
|
|
||||||
|
const Container = styled.div<{ rotation: number }>`
|
||||||
|
width: 100%;
|
||||||
|
height: 20vmin;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
${(props) => {
|
||||||
|
if (
|
||||||
|
props.rotation === Rotation.SideFlipped ||
|
||||||
|
props.rotation === Rotation.Side
|
||||||
|
) {
|
||||||
|
return css`
|
||||||
|
height: 100%;
|
||||||
|
width: 9.3vmax;
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
`;
|
||||||
|
|
||||||
const ExtraCountersGrid = styled.div<{ rotation: number }>`
|
const ExtraCountersGrid = styled.div<{ rotation: number }>`
|
||||||
display: flex;
|
display: flex;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
width: 100%;
|
|
||||||
justify-content: space-evenly;
|
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 100%;
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
${(props) => {
|
${(props) => {
|
||||||
@@ -81,11 +97,31 @@ const ExtraCountersBar = ({
|
|||||||
onPlayerChange(updatedPlayer);
|
onPlayerChange(updatedPlayer);
|
||||||
};
|
};
|
||||||
|
|
||||||
const iconSize = '8vmin';
|
const iconSize =
|
||||||
|
player.settings.rotation === Rotation.SideFlipped ||
|
||||||
|
player.settings.rotation === Rotation.Side
|
||||||
|
? '5vmax'
|
||||||
|
: '10vmin';
|
||||||
|
|
||||||
|
const {
|
||||||
|
useCommanderDamage,
|
||||||
|
usePartner,
|
||||||
|
usePoison,
|
||||||
|
useEnergy,
|
||||||
|
useExperience,
|
||||||
|
} = player.settings;
|
||||||
|
|
||||||
|
const hasExtraCounters =
|
||||||
|
useCommanderDamage || usePartner || usePoison || useEnergy || useExperience;
|
||||||
|
|
||||||
|
if (!hasExtraCounters) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Container rotation={player.settings.rotation}>
|
||||||
<ExtraCountersGrid rotation={player.settings.rotation}>
|
<ExtraCountersGrid rotation={player.settings.rotation}>
|
||||||
{player.settings.useCommanderDamage && (
|
{useCommanderDamage && (
|
||||||
<ExtraCounter
|
<ExtraCounter
|
||||||
rotation={player.settings.rotation}
|
rotation={player.settings.rotation}
|
||||||
Icon={<CommanderTax size={iconSize} opacity="0.5" color="black" />}
|
Icon={<CommanderTax size={iconSize} opacity="0.5" color="black" />}
|
||||||
@@ -98,9 +134,7 @@ const ExtraCountersBar = ({
|
|||||||
setCounterTotal={handleCounterChange}
|
setCounterTotal={handleCounterChange}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{Boolean(
|
{Boolean(useCommanderDamage && usePartner) && (
|
||||||
player.settings.useCommanderDamage && player.settings.usePartner
|
|
||||||
) && (
|
|
||||||
<ExtraCounter
|
<ExtraCounter
|
||||||
rotation={player.settings.rotation}
|
rotation={player.settings.rotation}
|
||||||
Icon={<PartnerTax size={iconSize} opacity="0.5" color="black" />}
|
Icon={<PartnerTax size={iconSize} opacity="0.5" color="black" />}
|
||||||
@@ -113,7 +147,7 @@ const ExtraCountersBar = ({
|
|||||||
setCounterTotal={handleCounterChange}
|
setCounterTotal={handleCounterChange}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{player.settings.usePoison && (
|
{usePoison && (
|
||||||
<ExtraCounter
|
<ExtraCounter
|
||||||
rotation={player.settings.rotation}
|
rotation={player.settings.rotation}
|
||||||
Icon={<Poison size={iconSize} opacity="0.5" color="black" />}
|
Icon={<Poison size={iconSize} opacity="0.5" color="black" />}
|
||||||
@@ -125,7 +159,7 @@ const ExtraCountersBar = ({
|
|||||||
setCounterTotal={handleCounterChange}
|
setCounterTotal={handleCounterChange}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{player.settings.useEnergy && (
|
{useEnergy && (
|
||||||
<ExtraCounter
|
<ExtraCounter
|
||||||
rotation={player.settings.rotation}
|
rotation={player.settings.rotation}
|
||||||
Icon={<Energy size={iconSize} opacity="0.5" color="black" />}
|
Icon={<Energy size={iconSize} opacity="0.5" color="black" />}
|
||||||
@@ -137,7 +171,7 @@ const ExtraCountersBar = ({
|
|||||||
setCounterTotal={handleCounterChange}
|
setCounterTotal={handleCounterChange}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{player.settings.useExperience && (
|
{useExperience && (
|
||||||
<ExtraCounter
|
<ExtraCounter
|
||||||
rotation={player.settings.rotation}
|
rotation={player.settings.rotation}
|
||||||
Icon={<Experience size={iconSize} opacity="0.5" color="black" />}
|
Icon={<Experience size={iconSize} opacity="0.5" color="black" />}
|
||||||
@@ -151,6 +185,7 @@ const ExtraCountersBar = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</ExtraCountersGrid>
|
</ExtraCountersGrid>
|
||||||
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
234
src/Components/LifeCounter/Health.tsx
Normal file
234
src/Components/LifeCounter/Health.tsx
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import styled, { css, keyframes } from 'styled-components';
|
||||||
|
import { Player, Rotation } from '../../Types/Player';
|
||||||
|
import LifeCounterButton from '../Buttons/LifeCounterButton';
|
||||||
|
import { OutlinedText } from '../Misc/OutlinedText';
|
||||||
|
|
||||||
|
const LifeCountainer = styled.div<{
|
||||||
|
rotation: Rotation;
|
||||||
|
}>`
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-grow: 1;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
${(props) => {
|
||||||
|
if (
|
||||||
|
props.rotation === Rotation.SideFlipped ||
|
||||||
|
props.rotation === Rotation.Side
|
||||||
|
) {
|
||||||
|
return css`
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const LifeCounterTextContainer = styled.div<{
|
||||||
|
rotation: Rotation;
|
||||||
|
}>`
|
||||||
|
position: absolute;
|
||||||
|
width: 60%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
user-select: none;
|
||||||
|
-moz-user-select: -moz-none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
|
||||||
|
${(props) => {
|
||||||
|
if (
|
||||||
|
props.rotation === Rotation.SideFlipped ||
|
||||||
|
props.rotation === Rotation.Side
|
||||||
|
) {
|
||||||
|
return css`
|
||||||
|
width: 100%;
|
||||||
|
height: 60%;
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TextWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const fadeOut = keyframes`
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
33% {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const RecentDifference = styled.span`
|
||||||
|
position: absolute;
|
||||||
|
top: 40%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
text-shadow: none;
|
||||||
|
background-color: rgba(255, 255, 255, 0.6);
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 5px 10px;
|
||||||
|
font-size: 8vmin;
|
||||||
|
color: #333333;
|
||||||
|
animation: ${fadeOut} 3s 1s ease-out forwards;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type HealthProps = {
|
||||||
|
player: Player;
|
||||||
|
onPlayerChange: (updatedPlayer: Player) => void;
|
||||||
|
differenceKey: number;
|
||||||
|
setDifferenceKey: (key: number) => void;
|
||||||
|
rotation: Rotation;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Health = ({
|
||||||
|
player,
|
||||||
|
onPlayerChange,
|
||||||
|
differenceKey,
|
||||||
|
setDifferenceKey,
|
||||||
|
rotation,
|
||||||
|
}: HealthProps) => {
|
||||||
|
const handleLifeChange = (updatedLifeTotal: number) => {
|
||||||
|
const difference = updatedLifeTotal - player.lifeTotal;
|
||||||
|
const updatedPlayer = {
|
||||||
|
...player,
|
||||||
|
lifeTotal: updatedLifeTotal,
|
||||||
|
hasLost: false,
|
||||||
|
};
|
||||||
|
setRecentDifference(recentDifference + difference);
|
||||||
|
onPlayerChange(updatedPlayer);
|
||||||
|
setDifferenceKey(Date.now());
|
||||||
|
};
|
||||||
|
|
||||||
|
const [recentDifference, setRecentDifference] = useState(0);
|
||||||
|
const [showStartingPlayer, setShowStartingPlayer] = useState(
|
||||||
|
localStorage.getItem('playing') === 'true'
|
||||||
|
);
|
||||||
|
const [fontSize, setFontSize] = useState(16);
|
||||||
|
const textContainerRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
setRecentDifference(0);
|
||||||
|
}, 3_000);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, [recentDifference]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!showStartingPlayer) {
|
||||||
|
const playingTimer = setTimeout(() => {
|
||||||
|
localStorage.setItem('playing', 'true');
|
||||||
|
setShowStartingPlayer(localStorage.getItem('playing') === 'true');
|
||||||
|
}, 3_000);
|
||||||
|
|
||||||
|
return () => clearTimeout(playingTimer);
|
||||||
|
}
|
||||||
|
}, [showStartingPlayer]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!textContainerRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const textContainer = textContainerRef.current;
|
||||||
|
const resizeObserver = new ResizeObserver(() => {
|
||||||
|
const calcFontSize = calculateFontSize(textContainer);
|
||||||
|
setFontSize(calcFontSize);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initially calculate font size
|
||||||
|
const initialCalculation = () => {
|
||||||
|
const calcFontSize = calculateFontSize(textContainer);
|
||||||
|
setFontSize(calcFontSize);
|
||||||
|
};
|
||||||
|
initialCalculation();
|
||||||
|
|
||||||
|
resizeObserver.observe(textContainer);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
// Cleanup: disconnect the ResizeObserver when the component unmounts.
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [textContainerRef]);
|
||||||
|
|
||||||
|
const calculateFontSize = (container: HTMLDivElement) => {
|
||||||
|
const isSide =
|
||||||
|
rotation === Rotation.SideFlipped || rotation === Rotation.Side;
|
||||||
|
|
||||||
|
const widthRatio = isSide ? container.clientHeight : container.clientWidth;
|
||||||
|
|
||||||
|
const heightRatio = isSide ? container.clientWidth : container.clientHeight;
|
||||||
|
|
||||||
|
const minRatio = Math.min(widthRatio, heightRatio);
|
||||||
|
|
||||||
|
const heightIsLarger = heightRatio > widthRatio;
|
||||||
|
|
||||||
|
const scaleFactor = heightIsLarger ? 0.8 : 1;
|
||||||
|
|
||||||
|
return minRatio * scaleFactor * 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LifeCountainer rotation={player.settings.rotation}>
|
||||||
|
<LifeCounterButton
|
||||||
|
lifeTotal={player.lifeTotal}
|
||||||
|
setLifeTotal={handleLifeChange}
|
||||||
|
rotation={player.settings.rotation}
|
||||||
|
operation="subtract"
|
||||||
|
increment={-1}
|
||||||
|
/>
|
||||||
|
<TextWrapper>
|
||||||
|
<LifeCounterTextContainer
|
||||||
|
rotation={player.settings.rotation}
|
||||||
|
ref={textContainerRef}
|
||||||
|
>
|
||||||
|
<OutlinedText
|
||||||
|
fontSize={`${fontSize}px`}
|
||||||
|
strokeWidth={`${fontSize / 16}px`}
|
||||||
|
rotation={player.settings.rotation}
|
||||||
|
>
|
||||||
|
{player.lifeTotal}
|
||||||
|
</OutlinedText>
|
||||||
|
{recentDifference !== 0 && (
|
||||||
|
<RecentDifference key={differenceKey}>
|
||||||
|
{recentDifference > 0 ? '+' : ''}
|
||||||
|
{recentDifference}
|
||||||
|
</RecentDifference>
|
||||||
|
)}
|
||||||
|
</LifeCounterTextContainer>
|
||||||
|
</TextWrapper>
|
||||||
|
<LifeCounterButton
|
||||||
|
lifeTotal={player.lifeTotal}
|
||||||
|
setLifeTotal={handleLifeChange}
|
||||||
|
rotation={player.settings.rotation}
|
||||||
|
operation="add"
|
||||||
|
increment={1}
|
||||||
|
/>
|
||||||
|
</LifeCountainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Health;
|
||||||
@@ -2,13 +2,19 @@ import { useEffect, useState } from 'react';
|
|||||||
import styled, { css, keyframes } from 'styled-components';
|
import styled, { css, keyframes } from 'styled-components';
|
||||||
import { theme } from '../../Data/theme';
|
import { theme } from '../../Data/theme';
|
||||||
import { Player, Rotation } from '../../Types/Player';
|
import { Player, Rotation } from '../../Types/Player';
|
||||||
import LifeCounterButton from '../Buttons/LifeCounterButton';
|
|
||||||
import { LoseGameButton } from '../Buttons/LoseButton';
|
import { LoseGameButton } from '../Buttons/LoseButton';
|
||||||
import SettingsButton from '../Buttons/SettingsButton';
|
import SettingsButton from '../Buttons/SettingsButton';
|
||||||
import CommanderDamageBar from '../Counters/CommanderDamageBar';
|
import CommanderDamageBar from '../Counters/CommanderDamageBar';
|
||||||
import ExtraCountersBar from '../Counters/ExtraCountersBar';
|
import ExtraCountersBar from '../Counters/ExtraCountersBar';
|
||||||
import { OutlinedText } from '../Misc/OutlinedText';
|
|
||||||
import PlayerMenu from '../PlayerMenu/PlayerMenu';
|
import PlayerMenu from '../PlayerMenu/PlayerMenu';
|
||||||
|
import Health from './Health';
|
||||||
|
import { WakeLock } from '../../Types/WakeLock';
|
||||||
|
|
||||||
|
const Lmao = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
const LifeCounterContentWrapper = styled.div<{
|
const LifeCounterContentWrapper = styled.div<{
|
||||||
backgroundColor: string;
|
backgroundColor: string;
|
||||||
@@ -59,29 +65,6 @@ const LifeCounterWrapper = styled.div<{
|
|||||||
}}
|
}}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const LifeCountainer = styled.div<{
|
|
||||||
rotation: Rotation;
|
|
||||||
}>`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-grow: 1;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
${(props) => {
|
|
||||||
if (
|
|
||||||
props.rotation === Rotation.SideFlipped ||
|
|
||||||
props.rotation === Rotation.Side
|
|
||||||
) {
|
|
||||||
return css`
|
|
||||||
flex-direction: column-reverse;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const PlayerNoticeWrapper = styled.div<{
|
const PlayerNoticeWrapper = styled.div<{
|
||||||
rotation: Rotation;
|
rotation: Rotation;
|
||||||
backgroundColor: string;
|
backgroundColor: string;
|
||||||
@@ -128,31 +111,6 @@ const DynamicText = styled.div<{ rotation: Rotation }>`
|
|||||||
}}
|
}}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const LifeCounterTextContainer = styled.p<{
|
|
||||||
rotation: Rotation;
|
|
||||||
}>`
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
user-select: none;
|
|
||||||
-moz-user-select: -moz-none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
${(props) => {
|
|
||||||
if (
|
|
||||||
props.rotation === Rotation.SideFlipped ||
|
|
||||||
props.rotation === Rotation.Side
|
|
||||||
) {
|
|
||||||
return css`
|
|
||||||
rotate: 270deg;
|
|
||||||
margin-right: 25%;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const fadeOut = keyframes`
|
const fadeOut = keyframes`
|
||||||
0% {
|
0% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@@ -180,14 +138,6 @@ export const RecentDifference = styled.span`
|
|||||||
animation: ${fadeOut} 3s 1s ease-out forwards;
|
animation: ${fadeOut} 3s 1s ease-out forwards;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface LifeCounterProps {
|
|
||||||
backgroundColor: string;
|
|
||||||
player: Player;
|
|
||||||
opponents: Player[];
|
|
||||||
onPlayerChange: (updatedPlayer: Player) => void;
|
|
||||||
resetCurrentGame: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasCommanderDamageReached21 = (player: Player) => {
|
const hasCommanderDamageReached21 = (player: Player) => {
|
||||||
const commanderDamageTotals = player.commanderDamage.map(
|
const commanderDamageTotals = player.commanderDamage.map(
|
||||||
(commanderDamage) => commanderDamage.damageTotal
|
(commanderDamage) => commanderDamage.damageTotal
|
||||||
@@ -215,12 +165,22 @@ const playerCanLose = (player: Player) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type LifeCounterProps = {
|
||||||
|
backgroundColor: string;
|
||||||
|
player: Player;
|
||||||
|
opponents: Player[];
|
||||||
|
onPlayerChange: (updatedPlayer: Player) => void;
|
||||||
|
resetCurrentGame: () => void;
|
||||||
|
wakeLock: WakeLock;
|
||||||
|
};
|
||||||
|
|
||||||
const LifeCounter = ({
|
const LifeCounter = ({
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
player,
|
player,
|
||||||
opponents,
|
opponents,
|
||||||
onPlayerChange,
|
onPlayerChange,
|
||||||
resetCurrentGame,
|
resetCurrentGame,
|
||||||
|
wakeLock,
|
||||||
}: LifeCounterProps) => {
|
}: LifeCounterProps) => {
|
||||||
const handleLifeChange = (updatedLifeTotal: number) => {
|
const handleLifeChange = (updatedLifeTotal: number) => {
|
||||||
const difference = updatedLifeTotal - player.lifeTotal;
|
const difference = updatedLifeTotal - player.lifeTotal;
|
||||||
@@ -231,7 +191,7 @@ const LifeCounter = ({
|
|||||||
};
|
};
|
||||||
setRecentDifference(recentDifference + difference);
|
setRecentDifference(recentDifference + difference);
|
||||||
onPlayerChange(updatedPlayer);
|
onPlayerChange(updatedPlayer);
|
||||||
setKey(Date.now());
|
setDifferenceKey(Date.now());
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleGameLost = () => {
|
const toggleGameLost = () => {
|
||||||
@@ -244,7 +204,7 @@ const LifeCounter = ({
|
|||||||
const [showStartingPlayer, setShowStartingPlayer] = useState(
|
const [showStartingPlayer, setShowStartingPlayer] = useState(
|
||||||
localStorage.getItem('playing') === 'true'
|
localStorage.getItem('playing') === 'true'
|
||||||
);
|
);
|
||||||
const [key, setKey] = useState(Date.now());
|
const [differenceKey, setDifferenceKey] = useState(Date.now());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
@@ -268,26 +228,9 @@ const LifeCounter = ({
|
|||||||
player.settings.rotation === Rotation.SideFlipped ||
|
player.settings.rotation === Rotation.SideFlipped ||
|
||||||
player.settings.rotation === Rotation.Side;
|
player.settings.rotation === Rotation.Side;
|
||||||
|
|
||||||
const size =
|
|
||||||
player.settings.rotation === Rotation.SideFlipped ||
|
|
||||||
player.settings.rotation === Rotation.Side
|
|
||||||
? 15
|
|
||||||
: 30;
|
|
||||||
|
|
||||||
const fontSize =
|
|
||||||
player.settings.rotation === Rotation.SideFlipped ||
|
|
||||||
player.settings.rotation === Rotation.Side
|
|
||||||
? `${size}vmax`
|
|
||||||
: `${size}vmin`;
|
|
||||||
|
|
||||||
const strokeWidth =
|
|
||||||
player.settings.rotation === Rotation.SideFlipped ||
|
|
||||||
player.settings.rotation === Rotation.Side
|
|
||||||
? `${size / 20}vmax`
|
|
||||||
: `${size / 20}vmin`;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LifeCounterContentWrapper backgroundColor={backgroundColor}>
|
<LifeCounterContentWrapper backgroundColor={backgroundColor}>
|
||||||
|
<Lmao>
|
||||||
<LifeCounterWrapper rotation={player.settings.rotation}>
|
<LifeCounterWrapper rotation={player.settings.rotation}>
|
||||||
{player.isStartingPlayer && !showStartingPlayer && (
|
{player.isStartingPlayer && !showStartingPlayer && (
|
||||||
<PlayerNoticeWrapper
|
<PlayerNoticeWrapper
|
||||||
@@ -324,36 +267,16 @@ const LifeCounter = ({
|
|||||||
onClick={toggleGameLost}
|
onClick={toggleGameLost}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<LifeCountainer rotation={player.settings.rotation}>
|
<Health
|
||||||
<LifeCounterButton
|
player={player}
|
||||||
lifeTotal={player.lifeTotal}
|
onPlayerChange={onPlayerChange}
|
||||||
setLifeTotal={handleLifeChange}
|
differenceKey={differenceKey}
|
||||||
|
setDifferenceKey={setDifferenceKey}
|
||||||
rotation={player.settings.rotation}
|
rotation={player.settings.rotation}
|
||||||
operation="subtract"
|
|
||||||
increment={-1}
|
|
||||||
/>
|
/>
|
||||||
<LifeCounterTextContainer rotation={player.settings.rotation}>
|
|
||||||
<OutlinedText fontSize={fontSize} strokeWidth={strokeWidth}>
|
|
||||||
{player.lifeTotal}
|
|
||||||
</OutlinedText>
|
|
||||||
{recentDifference !== 0 && (
|
|
||||||
<RecentDifference key={key}>
|
|
||||||
{recentDifference > 0 ? '+' : ''}
|
|
||||||
{recentDifference}
|
|
||||||
</RecentDifference>
|
|
||||||
)}
|
|
||||||
</LifeCounterTextContainer>
|
|
||||||
<LifeCounterButton
|
|
||||||
lifeTotal={player.lifeTotal}
|
|
||||||
setLifeTotal={handleLifeChange}
|
|
||||||
rotation={player.settings.rotation}
|
|
||||||
operation="add"
|
|
||||||
increment={1}
|
|
||||||
/>
|
|
||||||
</LifeCountainer>
|
|
||||||
<ExtraCountersBar player={player} onPlayerChange={onPlayerChange} />
|
<ExtraCountersBar player={player} onPlayerChange={onPlayerChange} />
|
||||||
</LifeCounterWrapper>
|
</LifeCounterWrapper>
|
||||||
|
</Lmao>
|
||||||
{showPlayerMenu && (
|
{showPlayerMenu && (
|
||||||
<PlayerMenu
|
<PlayerMenu
|
||||||
player={player}
|
player={player}
|
||||||
@@ -361,6 +284,7 @@ const LifeCounter = ({
|
|||||||
onPlayerChange={onPlayerChange}
|
onPlayerChange={onPlayerChange}
|
||||||
setShowPlayerMenu={setShowPlayerMenu}
|
setShowPlayerMenu={setShowPlayerMenu}
|
||||||
resetCurrentGame={resetCurrentGame}
|
resetCurrentGame={resetCurrentGame}
|
||||||
|
wakeLock={wakeLock}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</LifeCounterContentWrapper>
|
</LifeCounterContentWrapper>
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
import styled from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
import { theme } from '../../Data/theme';
|
import { theme } from '../../Data/theme';
|
||||||
|
import { Rotation } from '../../Types/Player';
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
`;
|
||||||
|
|
||||||
const CenteredText = styled.div<{
|
const CenteredText = styled.div<{
|
||||||
strokeWidth?: string;
|
strokeWidth?: string;
|
||||||
@@ -7,11 +17,9 @@ const CenteredText = styled.div<{
|
|||||||
fillColor?: string;
|
fillColor?: string;
|
||||||
fontSize?: string;
|
fontSize?: string;
|
||||||
fontWeight?: string;
|
fontWeight?: string;
|
||||||
|
rotation?: Rotation;
|
||||||
}>`
|
}>`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
font-weight: ${(props) => props.fontWeight || ''};
|
font-weight: ${(props) => props.fontWeight || ''};
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@@ -26,6 +34,17 @@ const CenteredText = styled.div<{
|
|||||||
-webkit-text-stroke: ${(props) => props.strokeWidth || '1vmin'} ${(props) => props.strokeColor || theme.palette.common.white};
|
-webkit-text-stroke: ${(props) => props.strokeWidth || '1vmin'} ${(props) => props.strokeColor || theme.palette.common.white};
|
||||||
-webkit-text-fill-color: ${(props) =>
|
-webkit-text-fill-color: ${(props) =>
|
||||||
props.fillColor || theme.palette.common.black};
|
props.fillColor || theme.palette.common.black};
|
||||||
|
|
||||||
|
${(props) => {
|
||||||
|
if (
|
||||||
|
props.rotation === Rotation.SideFlipped ||
|
||||||
|
props.rotation === Rotation.Side
|
||||||
|
) {
|
||||||
|
return css`
|
||||||
|
rotate: 270deg;
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const CenteredTextOutline = styled.span`
|
const CenteredTextOutline = styled.span`
|
||||||
@@ -42,6 +61,7 @@ type OutlinedTextProps = {
|
|||||||
strokeWidth?: string;
|
strokeWidth?: string;
|
||||||
strokeColor?: string;
|
strokeColor?: string;
|
||||||
fillColor?: string;
|
fillColor?: string;
|
||||||
|
rotation?: Rotation;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const OutlinedText: React.FC<OutlinedTextProps> = ({
|
export const OutlinedText: React.FC<OutlinedTextProps> = ({
|
||||||
@@ -51,17 +71,21 @@ export const OutlinedText: React.FC<OutlinedTextProps> = ({
|
|||||||
strokeWidth,
|
strokeWidth,
|
||||||
strokeColor,
|
strokeColor,
|
||||||
fillColor,
|
fillColor,
|
||||||
|
rotation,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
|
<Container>
|
||||||
<CenteredText
|
<CenteredText
|
||||||
fontSize={fontSize}
|
fontSize={fontSize}
|
||||||
fontWeight={fontWeight}
|
fontWeight={fontWeight}
|
||||||
strokeWidth={strokeWidth}
|
strokeWidth={strokeWidth}
|
||||||
strokeColor={strokeColor}
|
strokeColor={strokeColor}
|
||||||
fillColor={fillColor}
|
fillColor={fillColor}
|
||||||
|
rotation={rotation}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<CenteredTextOutline aria-hidden>{children}</CenteredTextOutline>
|
<CenteredTextOutline aria-hidden>{children}</CenteredTextOutline>
|
||||||
</CenteredText>
|
</CenteredText>
|
||||||
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user