Skip to content

Commit

Permalink
Merge pull request #332 from wyne/scale-again
Browse files Browse the repository at this point in the history
Scale again
  • Loading branch information
wyne authored Nov 11, 2023
2 parents 97ff8d0 + ce71d8e commit 32e91ef
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 105 deletions.
47 changes: 25 additions & 22 deletions src/components/PlayerTile.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { View, StyleSheet, LayoutChangeEvent, DimensionValue } from 'react-native';
import React from 'react';
import { View, StyleSheet, DimensionValue } from 'react-native';

import AdditionTile from './PlayerTiles/AdditionTile/AdditionTile';
import { selectGameById } from '../../redux/GamesSlice';
Expand All @@ -8,17 +8,28 @@ import { useAppSelector } from '../../redux/hooks';
import { TouchSurface } from './PlayerTiles/AdditionTile/TouchSurface';

interface Props {
index: number;
playerId: string;
color: string;
fontColor: string;
cols: number;
rows: number;
playerId: string;
index: number;
width: number;
height: number;
}

const PlayerTile: React.FunctionComponent<Props> = ({ color, fontColor, cols, rows, playerId, index }) => {
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
const PlayerTile: React.FunctionComponent<Props> = ({
index,
color,
fontColor,
width,
height,
cols,
rows,
playerId
}) => {
// Short circuit if width or height is not yet defined
if (!(width > 0 && height > 0)) return null;

const currentGameId = useAppSelector(state => state.settings.currentGameId);
const currentGame = useAppSelector(state => selectGameById(state, currentGameId));
Expand All @@ -40,30 +51,22 @@ const PlayerTile: React.FunctionComponent<Props> = ({ color, fontColor, cols, ro
const widthPerc: DimensionValue = `${(100 / cols)}%`;
const heightPerc: DimensionValue = `${(100 / rows)}%`;

const layoutHandler = (e: LayoutChangeEvent) => {
const { width, height } = e.nativeEvent.layout;

setWidth(width);
setHeight(height);
};
if (Number.isNaN(width) || Number.isNaN(height)) return null;

return (
<View onLayout={layoutHandler}
style={[
styles.playerCard,
{ backgroundColor: color },
{ width: widthPerc },
{ height: heightPerc },
]}>
<View style={[
styles.playerCard,
{ backgroundColor: color },
{ width: widthPerc },
{ height: heightPerc },]}>
<AdditionTile
totalScore={scoreTotal}
roundScore={scoreRound}
fontColor={fontColor}
playerName={playerName}
maxWidth={width}
maxHeight={height}
index={index}
/>
index={index} />

<TouchSurface
scoreType='increment'
Expand Down
78 changes: 28 additions & 50 deletions src/components/PlayerTiles/AdditionTile/AdditionTile.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import { StyleSheet, LayoutChangeEvent } from 'react-native';
import React, { useEffect } from 'react';
import { StyleSheet } from 'react-native';
import Animated, {
useSharedValue,
useAnimatedStyle,
Expand All @@ -17,8 +17,8 @@ interface Props {
totalScore: number;
roundScore: number;
fontColor: string;
maxWidth: number;
maxHeight: number;
maxWidth: number | null;
maxHeight: number | null;
index: number;
}

Expand All @@ -31,58 +31,27 @@ const AdditionTile: React.FunctionComponent<Props> = ({
maxHeight,
index
}) => {
// Tile width and height
const [tileWidth, setTileWidth] = useState(1);
const [tileHeight, setTileHeight] = useState(1);

if (maxWidth == null || maxHeight == null) return null;

// Animation values
const sharedScale = useSharedValue(1);
const sharedOpacity = useSharedValue(0);

// Animation styles for resizing due to text changes
/**
* Animation styles for resizing due to text changes
*/
const animatedStyles = useAnimatedStyle(() => {
return {
transform: [{ scale: sharedScale.value }],
opacity: sharedOpacity.value,
};
});


// Update tile width and height on layout change
const layoutHandler = (e: LayoutChangeEvent) => {
const { width, height } = e.nativeEvent.layout;
setTileHeight(height);
setTileWidth(width);
};

useEffect(() => {
/*
* Calculate ratio of tile content to max width/height
* determined by the parent container
*/
const horizontalScaleRatio = maxWidth / tileWidth;
const verticalScaleRatio = maxHeight / tileHeight;

// Allow for padding by not scaling to full width/height
const maxTileCoverage = 0.8; // 80%

const minimumScale = Math.min(
horizontalScaleRatio * maxTileCoverage,
verticalScaleRatio * maxTileCoverage
);

if (minimumScale > 0) {
sharedScale.value = withDelay(
animationDuration,
withTiming(
minimumScale, { duration: animationDuration }
)
);
}

// Delay opacity animation to allow for scale animation to finish
// and to allow for the previous tile to finish animating for effect
const animationDelay = index * animationDuration / 2;
const animationDelay = (index + 1) * animationDuration / 2;

sharedOpacity.value = withDelay(
animationDelay,
Expand All @@ -91,30 +60,39 @@ const AdditionTile: React.FunctionComponent<Props> = ({
{ duration: animationDuration * 2 }
)
);
});
return;
}, [
playerName,
totalScore,
roundScore,
maxWidth,
maxHeight,
sharedScale.value
]);

const playerNameFontSize = calculateFontSize(maxWidth, playerName.length);
const containerShortEdge = Math.min(maxWidth, maxHeight);

const containerWidth = Math.min(maxWidth, maxHeight);
const playerNameFontSize = calculateFontSize(maxWidth);

const dynamicPlayerStyles = {
fontSize: playerNameFontSize,
color: fontColor,
};

return (
<Animated.View style={[animatedStyles, { justifyContent: 'center' }]} onLayout={layoutHandler}>
<Animated.View style={[animatedStyles, { justifyContent: 'center' }]}>
<Animated.Text style={[styles.name, dynamicPlayerStyles]} numberOfLines={1}>
{playerName}
</Animated.Text>
<Animated.View
style={styles.scoreLineOne} >
<ScoreBefore containerWidth={containerWidth} roundScore={roundScore} totalScore={totalScore}

<Animated.View style={styles.scoreLineOne}>
<ScoreBefore containerWidth={containerShortEdge} roundScore={roundScore} totalScore={totalScore}
fontColor={fontColor} />
<ScoreRound containerWidth={containerWidth} roundScore={roundScore} totalScore={totalScore}
<ScoreRound containerWidth={containerShortEdge} roundScore={roundScore}
fontColor={fontColor} />
</Animated.View>
<ScoreAfter containerWidth={containerWidth} roundScore={roundScore} totalScore={totalScore}

<ScoreAfter containerWidth={containerShortEdge} roundScore={roundScore} totalScore={totalScore}
fontColor={fontColor} />
</Animated.View>
);
Expand Down
19 changes: 11 additions & 8 deletions src/components/PlayerTiles/AdditionTile/Helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,23 @@ export const exitingAnimation = ZoomOut.duration(animationDuration);
*/
export const layoutAnimation = Layout.easing(Easing.ease).duration(animationDuration);

export const singleLineScoreSizeMultiplier = 1.2;

export const multiLineScoreSizeMultiplier = 0.7;

export const baseScoreFontSize = 40;

export const scoreMathOpacity = 0.75;

/**
* Calculates the font size based on the maximum width and length of the text.
* Calculates the font size based on the maximum width.
* @param containerWidth The maximum width of the text.
* @param stringLength The number of characters in the text.
* @returns The calculated font size.
*/
export const calculateFontSize = (containerWidth: number, stringLength: number) => {
const baseScale: number = Math.min(1 / stringLength * 200, 100);

export const calculateFontSize = (containerWidth: number) => {
let widthFactor: number = containerWidth / 200;

if (Number.isNaN(widthFactor)) { widthFactor = 1; }

return baseScale * widthFactor;
return baseScoreFontSize * widthFactor;
};

/**
Expand Down
8 changes: 5 additions & 3 deletions src/components/PlayerTiles/AdditionTile/ScoreAfter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ interface Props {
}

const ScoreAfter: React.FunctionComponent<Props> = ({ containerWidth, roundScore, totalScore, fontColor }) => {
const fontSize = useSharedValue(calculateFontSize(containerWidth, totalScore.toString().length));
const fontSize = useSharedValue(calculateFontSize(containerWidth));
const opacity = useSharedValue(1);

const animatedStyles = useAnimatedStyle(() => {
Expand All @@ -29,10 +29,12 @@ const ScoreAfter: React.FunctionComponent<Props> = ({ containerWidth, roundScore

useEffect(() => {
fontSize.value = withTiming(
roundScore == 0 ? 1 : calculateFontSize(containerWidth, totalScore.toString().length), { duration: animationDuration },
roundScore == 0 ? 1 : calculateFontSize(containerWidth) * 1.1,
{ duration: animationDuration },
);
opacity.value = withTiming(
roundScore == 0 ? 0 : 1, { duration: animationDuration },
roundScore == 0 ? 0 : 1,
{ duration: animationDuration },
);
}, [roundScore, containerWidth]);

Expand Down
15 changes: 6 additions & 9 deletions src/components/PlayerTiles/AdditionTile/ScoreBefore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
withTiming
} from 'react-native-reanimated';

import { calculateFontSize, animationDuration, enteringAnimation } from './Helpers';
import { calculateFontSize, animationDuration, enteringAnimation, multiLineScoreSizeMultiplier, singleLineScoreSizeMultiplier, scoreMathOpacity } from './Helpers';

interface Props {
roundScore: number;
Expand All @@ -21,14 +21,9 @@ const ScoreBefore: React.FunctionComponent<Props> = ({
totalScore,
fontColor
}) => {
// Determine the length of the first row of the score
const firstRowLength = (
roundScore == 0 ? 0 : roundScore.toString().length + 3
) + totalScore.toString().length;

const scoreBefore = totalScore - roundScore;

const fontSize = useSharedValue(calculateFontSize(containerWidth, firstRowLength));
const fontSize = useSharedValue(calculateFontSize(containerWidth));
const fontOpacity = useSharedValue(100);

const animatedStyles = useAnimatedStyle(() => {
Expand All @@ -39,14 +34,16 @@ const ScoreBefore: React.FunctionComponent<Props> = ({
};
});

const scaleFactor = roundScore == 0 ? singleLineScoreSizeMultiplier : multiLineScoreSizeMultiplier;

useEffect(() => {
fontSize.value = withTiming(
calculateFontSize(containerWidth, firstRowLength),
calculateFontSize(containerWidth) * scaleFactor,
{ duration: animationDuration }
);

fontOpacity.value = withTiming(
roundScore == 0 ? 100 : 75,
roundScore == 0 ? 100 : scoreMathOpacity * 100,
{ duration: animationDuration }
);
}, [roundScore, containerWidth]);
Expand Down
19 changes: 10 additions & 9 deletions src/components/PlayerTiles/AdditionTile/ScoreRound.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,29 @@ import {
withTiming
} from 'react-native-reanimated';

import { calculateFontSize, animationDuration } from './Helpers';
import { calculateFontSize, animationDuration, multiLineScoreSizeMultiplier, scoreMathOpacity } from './Helpers';

interface Props {
roundScore: number;
totalScore: number;
fontColor: string;
containerWidth: number;
}

const ScoreRound: React.FunctionComponent<Props> = ({ containerWidth, roundScore, totalScore, fontColor }) => {
const firstRowLength = (roundScore == 0 ? 0 : roundScore.toString().length + 3) + totalScore.toString().length;
const fontSizeRound = useSharedValue(calculateFontSize(containerWidth, firstRowLength));
const ScoreRound: React.FunctionComponent<Props> = ({ containerWidth, roundScore, fontColor }) => {
const fontSize = useSharedValue(calculateFontSize(containerWidth));

const animatedStyles = useAnimatedStyle(() => {
return {
fontSize: fontSizeRound.value,
fontSize: fontSize.value,
};
});

const d = roundScore;

useEffect(() => {
fontSizeRound.value = withTiming(
calculateFontSize(containerWidth, firstRowLength), { duration: animationDuration }
fontSize.value = withTiming(
calculateFontSize(containerWidth) * multiLineScoreSizeMultiplier,
{ duration: animationDuration }
);

}, [roundScore, containerWidth]);
Expand All @@ -42,7 +42,8 @@ const ScoreRound: React.FunctionComponent<Props> = ({ containerWidth, roundScore
<Animated.Text numberOfLines={1}
style={[animatedStyles, {
fontVariant: ['tabular-nums'],
color: fontColor, opacity: .75
color: fontColor,
opacity: scoreMathOpacity
}]}>
{roundScore > 0 && " + "}
{roundScore < 0 && " - "}
Expand Down
Loading

0 comments on commit 32e91ef

Please sign in to comment.