Skip to content

Commit 49978e1

Browse files
author
Pascal Wegner
authored
Merge pull request #140 from flextremedev/improve-arc-animation
Improve arc animation
2 parents 033339a + 6b7f26f commit 49978e1

File tree

5 files changed

+116
-27
lines changed

5 files changed

+116
-27
lines changed

packages/pwa/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
},
1212
"dependencies": {
1313
"@interval-timer/core": "*",
14+
"@react-spring/web": "^9.4.4",
1415
"@xstate/react": "^0.8.1",
1516
"date-fns": "^2.28.0",
1617
"next": "latest",

packages/pwa/src/components/Arc/Arc.tsx

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1+
import { animated, easings, useSpring } from '@react-spring/web';
12
import * as React from 'react';
23

34
type ArcProps = React.SVGProps<SVGSVGElement> & {
4-
factor: number;
5+
progress: number;
6+
progressPerSecond: number;
57
};
68

7-
export function Arc({ factor, ...restProps }: ArcProps) {
9+
const strokeLength = 877;
10+
11+
export function Arc({ progress, progressPerSecond, ...restProps }: ArcProps) {
12+
const { strokeDashoffset } = useSpring({
13+
from: { strokeDashoffset: (progress / 100) * strokeLength },
14+
strokeDashoffset: (progress / 100 + progressPerSecond / 100) * strokeLength,
15+
config: { duration: 1000, easing: easings.linear },
16+
});
17+
818
return (
919
<svg
1020
fill="none"
@@ -20,15 +30,17 @@ export function Arc({ factor, ...restProps }: ArcProps) {
2030
stroke="#CDD9EE"
2131
strokeWidth={4}
2232
/>
23-
<path
24-
d="M280 141a138.998 138.998 0 01-237.288 98.288A139.01 139.01 0 012 141 138.997 138.997 0 01141 2a138.997 138.997 0 01139 139h0z"
33+
<animated.circle
34+
cx={141}
35+
cy={141}
36+
r={139}
2537
stroke="#0057FF"
2638
strokeWidth={4}
27-
className="origin-center -rotate-90"
2839
style={{
29-
strokeDasharray: 877,
30-
strokeDashoffset: factor * 877,
31-
transition: 'all 1s linear',
40+
transform: 'rotateZ(-90deg)',
41+
transformOrigin: '50% 50%',
42+
strokeDasharray: strokeLength,
43+
strokeDashoffset,
3244
}}
3345
/>
3446
</svg>

packages/pwa/src/components/Counter/Counter.tsx

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,31 @@ export function Counter({
1919
roundsLeft,
2020
rounds,
2121
}: CounterProps) {
22-
const factor =
23-
1 -
24-
(getSeconds(timeLeft) + getMinutes(timeLeft) * SECONDS_PER_MINUTE) /
25-
(getSeconds(timeTotal) + getMinutes(timeTotal) * SECONDS_PER_MINUTE);
22+
const timeLeftInSeconds =
23+
getSeconds(timeLeft) + getMinutes(timeLeft) * SECONDS_PER_MINUTE;
24+
const timeLeftAdvancedByOneInSeconds =
25+
getSeconds(timeTotal) - 1 + getMinutes(timeTotal) * SECONDS_PER_MINUTE;
26+
const timeTotalInSeconds =
27+
getSeconds(timeTotal) + getMinutes(timeTotal) * SECONDS_PER_MINUTE;
2628

27-
const stepLength =
28-
1 -
29-
(getSeconds(timeTotal) - 1 + getMinutes(timeTotal) * SECONDS_PER_MINUTE) /
30-
(getSeconds(timeTotal) + getMinutes(timeTotal) * SECONDS_PER_MINUTE);
29+
const factor = 1 - timeLeftInSeconds / timeTotalInSeconds;
3130

32-
const transitionCompensationBasedFactor = factor + factor * stepLength;
31+
const stepLength = 1 - timeLeftAdvancedByOneInSeconds / timeTotalInSeconds;
3332

3433
return (
3534
<div>
36-
<div className="flex flex-col items-center mb-20 z-[1]">
35+
<div className="flex flex-col items-center mb-8 lg:mb-20 z-[1]">
3736
<span className="text-blue-600 text-lg font-bold">ROUND</span>
3837
<span className="text-4xl" data-testid={'round'}>{`${
3938
rounds - roundsLeft
4039
}/${rounds}`}</span>
4140
</div>
42-
<div className="flex flex-col justify-center items-center relative w-72 h-72">
41+
<div className="flex flex-col justify-center items-center relative w-64 sm:w-72 lg:w-80 h-64 sm:h-72 lg:h-80">
4342
<Arc
43+
key={factor === 0 ? 'arc-from-start' : 'arc-running'}
4444
className="absolute origin-center"
45-
factor={transitionCompensationBasedFactor}
45+
progress={factor * 100}
46+
progressPerSecond={stepLength * 100}
4647
/>
4748
<div className="z-[1]">
4849
<DurationInput value={timeLeft} readOnly dataTestId={'time-left'} />

packages/pwa/src/components/DurationInput/DurationInput.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export function DurationInput({
9292
onFocus={handleMinutesSelect}
9393
onSelect={handleMinutesSelect}
9494
data-testid={dataTestId && `${dataTestId}-minutes`}
95-
className="text-black text-center outline-none w-24"
95+
className="text-black text-center outline-none w-24 bg-transparent"
9696
ref={minutesRef}
9797
readOnly={readOnly}
9898
size={2}
@@ -110,7 +110,7 @@ export function DurationInput({
110110
onFocus={handleSecondsSelect}
111111
onSelect={handleSecondsSelect}
112112
data-testid={dataTestId && `${dataTestId}-seconds`}
113-
className="text-black text-center outline-none w-24"
113+
className="text-black text-center outline-none w-24 bg-transparent"
114114
ref={secondsRef}
115115
readOnly={readOnly}
116116
size={2}

yarn.lock

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3616,6 +3616,52 @@
36163616
sudo-prompt "^9.0.0"
36173617
wcwidth "^1.0.1"
36183618

3619+
"@react-spring/animated@~9.4.4":
3620+
version "9.4.4"
3621+
resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.4.4.tgz#15e21923e55c06ca2bcea432869b91b2f8b07519"
3622+
integrity sha512-e9xnuBaUTD+NolKikUmrGWjX8AVCPyj1GcEgjgq9E+0sXKv46UY7cm2EmB6mUDTxWIDVKebARY++xT4nGDraBQ==
3623+
dependencies:
3624+
"@react-spring/shared" "~9.4.4"
3625+
"@react-spring/types" "~9.4.4"
3626+
3627+
"@react-spring/core@~9.4.4":
3628+
version "9.4.4"
3629+
resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.4.4.tgz#7730988cec7302ba6e0977cf4c08c30249d95622"
3630+
integrity sha512-llgb0ljFyjMB0JhWsaFHOi9XFT8n1jBMVs1IFY2ipIBerWIRWrgUmIpakLPHTa4c4jwqTaDSwX90s2a0iN7dxQ==
3631+
dependencies:
3632+
"@react-spring/animated" "~9.4.4"
3633+
"@react-spring/rafz" "~9.4.4"
3634+
"@react-spring/shared" "~9.4.4"
3635+
"@react-spring/types" "~9.4.4"
3636+
3637+
"@react-spring/rafz@~9.4.4":
3638+
version "9.4.4"
3639+
resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.4.4.tgz#736c9ed1099baebeea20c357b9700b01b83ea9de"
3640+
integrity sha512-5ki/sQ06Mdf8AuFstSt5zbNNicRT4LZogiJttDAww1ozhuvemafNWEHxhzcULgCPCDu2s7HsroaISV7+GQWrhw==
3641+
3642+
"@react-spring/shared@~9.4.4":
3643+
version "9.4.4"
3644+
resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.4.4.tgz#e1ae00a77d170d86d77d9a19dc7015bdddc2d26f"
3645+
integrity sha512-ySVgScDZlhm/+Iy2smY9i/DDrShArY0j6zjTS/Re1lasKnhq8qigoGiAxe8xMPJNlCaj3uczCqHy3TY9bKRtfQ==
3646+
dependencies:
3647+
"@react-spring/rafz" "~9.4.4"
3648+
"@react-spring/types" "~9.4.4"
3649+
3650+
"@react-spring/types@~9.4.4":
3651+
version "9.4.4"
3652+
resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.4.4.tgz#97c69881788e624d7cc68d4385fdaa9b5fd20642"
3653+
integrity sha512-KpxKt/D//q/t/6FBcde/RE36LKp8PpWu7kFEMLwpzMGl9RpcexunmYOQJWwmJWtkQjgE1YRr7DzBMryz6La1cQ==
3654+
3655+
"@react-spring/web@^9.4.4":
3656+
version "9.4.4"
3657+
resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.4.4.tgz#0d13356e61c1d47e83a36112e19e5db691f3fbe5"
3658+
integrity sha512-iJmOLdhcuizriUlu/xqBc5y8KaFts+UI+iC+GxyTwBtzxA9czKiSAZW2ESuhG8stafa3jncwjfTQQp84KN36cw==
3659+
dependencies:
3660+
"@react-spring/animated" "~9.4.4"
3661+
"@react-spring/core" "~9.4.4"
3662+
"@react-spring/shared" "~9.4.4"
3663+
"@react-spring/types" "~9.4.4"
3664+
36193665
"@rollup/plugin-babel@^5.2.0":
36203666
version "5.3.0"
36213667
resolved "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz#9cb1c5146ddd6a4968ad96f209c50c62f92f9879"
@@ -7830,7 +7876,7 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0:
78307876
dependencies:
78317877
once "^1.4.0"
78327878

7833-
enhanced-resolve@^4.3.0:
7879+
enhanced-resolve@^4.1.0, enhanced-resolve@^4.3.0:
78347880
version "4.5.0"
78357881
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec"
78367882
integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==
@@ -15712,7 +15758,7 @@ react-devtools-core@^4.6.0:
1571215758
shell-quote "^1.6.1"
1571315759
ws "^7"
1571415760

15715-
react-dom@^17.0.0, react-dom@^17.0.2:
15761+
react-dom@^17.0.2:
1571615762
version "17.0.2"
1571715763
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
1571815764
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
@@ -15917,7 +15963,7 @@ react-timer-mixin@^0.13.4:
1591715963
resolved "https://registry.yarnpkg.com/react-timer-mixin/-/react-timer-mixin-0.13.4.tgz#75a00c3c94c13abe29b43d63b4c65a88fc8264d3"
1591815964
integrity sha512-4+ow23tp/Tv7hBM5Az5/Be/eKKF7DIvJ09voz5LyHGQaqqz9WV8YMs31eFvcYQs7d451LSg7kDJV70XYN/Ug/Q==
1591915965

15920-
react@^17.0.0, react@^17.0.2:
15966+
react@^17.0.2:
1592115967
version "17.0.2"
1592215968
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
1592315969
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
@@ -18551,7 +18597,7 @@ watchpack-chokidar2@^2.0.1:
1855118597
dependencies:
1855218598
chokidar "^2.1.8"
1855318599

18554-
watchpack@^1.7.4:
18600+
watchpack@^1.6.1, watchpack@^1.7.4:
1855518601
version "1.7.5"
1855618602
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453"
1855718603
integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==
@@ -18681,7 +18727,36 @@ webpack-sources@^1.1.0, webpack-sources@^1.3.0, webpack-sources@^1.4.0, webpack-
1868118727
source-list-map "^2.0.0"
1868218728
source-map "~0.6.1"
1868318729

18684-
webpack@4.43.0, webpack@4.44.2, webpack@~4.44.0:
18730+
webpack@4.43.0:
18731+
version "4.43.0"
18732+
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.43.0.tgz#c48547b11d563224c561dad1172c8aa0b8a678e6"
18733+
integrity sha512-GW1LjnPipFW2Y78OOab8NJlCflB7EFskMih2AHdvjbpKMeDJqEgSx24cXXXiPS65+WSwVyxtDsJH6jGX2czy+g==
18734+
dependencies:
18735+
"@webassemblyjs/ast" "1.9.0"
18736+
"@webassemblyjs/helper-module-context" "1.9.0"
18737+
"@webassemblyjs/wasm-edit" "1.9.0"
18738+
"@webassemblyjs/wasm-parser" "1.9.0"
18739+
acorn "^6.4.1"
18740+
ajv "^6.10.2"
18741+
ajv-keywords "^3.4.1"
18742+
chrome-trace-event "^1.0.2"
18743+
enhanced-resolve "^4.1.0"
18744+
eslint-scope "^4.0.3"
18745+
json-parse-better-errors "^1.0.2"
18746+
loader-runner "^2.4.0"
18747+
loader-utils "^1.2.3"
18748+
memory-fs "^0.4.1"
18749+
micromatch "^3.1.10"
18750+
mkdirp "^0.5.3"
18751+
neo-async "^2.6.1"
18752+
node-libs-browser "^2.2.1"
18753+
schema-utils "^1.0.0"
18754+
tapable "^1.1.3"
18755+
terser-webpack-plugin "^1.4.3"
18756+
watchpack "^1.6.1"
18757+
webpack-sources "^1.4.1"
18758+
18759+
webpack@4.44.2:
1868518760
version "4.44.2"
1868618761
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.2.tgz#6bfe2b0af055c8b2d1e90ed2cd9363f841266b72"
1868718762
integrity sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==

0 commit comments

Comments
 (0)