parent
26cbef8763
commit
dfd0d71b3b
@ -0,0 +1,9 @@ |
||||
import reduce from 'lodash/reduce' |
||||
|
||||
import type { Events } from 'requests' |
||||
|
||||
export const fullEpisodesDuration = (filters: Events) => reduce( |
||||
filters, |
||||
(acc, filter) => acc + (Number(filter.e) - Number(filter.s)), |
||||
0, |
||||
) |
||||
@ -0,0 +1,72 @@ |
||||
import type { Dispatch, SetStateAction } from 'react' |
||||
import { useEffect } from 'react' |
||||
|
||||
import isEmpty from 'lodash/isEmpty' |
||||
|
||||
import type { Events } from 'requests' |
||||
|
||||
import { fullEpisodesDuration } from './helpers' |
||||
import { Svg, Circle } from './styled' |
||||
|
||||
export type TCircleAnimation = { |
||||
plaingOrder: number, |
||||
playedProgress: number, |
||||
playing: boolean, |
||||
ready: boolean, |
||||
} |
||||
|
||||
export const initialCircleAnimation: TCircleAnimation = { |
||||
plaingOrder: 0, |
||||
playedProgress: 0, |
||||
playing: false, |
||||
ready: false, |
||||
} |
||||
|
||||
type Props = { |
||||
circleAnimation?: TCircleAnimation, |
||||
filteredEvents: Events, |
||||
setWatchAllEpisodesTimer: (showTimer: boolean) => void, |
||||
} |
||||
|
||||
export type TSetCircleAnimation = Dispatch<SetStateAction<TCircleAnimation>> |
||||
|
||||
export const CircleAnimationBar = ({ |
||||
circleAnimation, |
||||
filteredEvents, |
||||
setWatchAllEpisodesTimer, |
||||
}: Props) => { |
||||
const { |
||||
plaingOrder, |
||||
playedProgress, |
||||
playing, |
||||
ready, |
||||
} = circleAnimation! |
||||
const timeOfAllEpisodes = fullEpisodesDuration(filteredEvents) |
||||
const remainingEvents = filteredEvents.slice(plaingOrder - 1) |
||||
const fullTimeOfRemainingEpisodes = !isEmpty(remainingEvents) |
||||
? fullEpisodesDuration(remainingEvents) |
||||
: 0 |
||||
const animationPause = !playing || !ready |
||||
|
||||
const currentAnimationTime = Math.round(fullTimeOfRemainingEpisodes - (playedProgress / 1000)) |
||||
const currentEpisodesPercent = 100 - (100 / (timeOfAllEpisodes / currentAnimationTime)) |
||||
|
||||
useEffect(() => { |
||||
if (currentEpisodesPercent >= 100) { |
||||
setWatchAllEpisodesTimer(false) |
||||
} |
||||
}, [currentEpisodesPercent, setWatchAllEpisodesTimer]) |
||||
|
||||
return ( |
||||
<Svg> |
||||
<Circle |
||||
cx='50%' |
||||
cy='50%' |
||||
r='50%' |
||||
currentAnimationTime={currentAnimationTime} |
||||
animationPause={animationPause} |
||||
currentEpisodesPercent={currentEpisodesPercent} |
||||
/> |
||||
</Svg> |
||||
) |
||||
} |
||||
@ -0,0 +1,43 @@ |
||||
import styled, { keyframes } from 'styled-components/macro' |
||||
|
||||
type TCircle = { |
||||
animationPause?: boolean, |
||||
currentAnimationTime: number, |
||||
currentEpisodesPercent: number, |
||||
} |
||||
|
||||
const strokeDashOffset = 43.5 |
||||
|
||||
const clockAnimation = (currentEpisodesPercent?: number) => keyframes` |
||||
from { |
||||
stroke-dashoffset: ${currentEpisodesPercent}; |
||||
} |
||||
to { |
||||
stroke-dashoffset: 0; |
||||
} |
||||
` |
||||
|
||||
export const Svg = styled.svg` |
||||
background-color: #5EB2FF; |
||||
width: 14px; |
||||
height: 14px; |
||||
position: relative; |
||||
border-radius: 50%; |
||||
` |
||||
|
||||
export const Circle = styled.circle<TCircle>` |
||||
fill: transparent; |
||||
stroke: white; |
||||
stroke-width: 14px; |
||||
stroke-dasharray: ${strokeDashOffset}; |
||||
stroke-dashoffset: ${strokeDashOffset}; |
||||
transform: rotate(-90deg); |
||||
transform-origin: center; |
||||
animation-name: ${({ currentEpisodesPercent }) => ( |
||||
clockAnimation(strokeDashOffset - (strokeDashOffset * currentEpisodesPercent / 100)) |
||||
)}; |
||||
animation-duration: ${({ currentAnimationTime }) => `${currentAnimationTime}s`}; |
||||
animation-play-state: ${({ animationPause }) => (animationPause ? 'paused' : 'running')}; |
||||
animation-timing-function: linear; |
||||
animation-fill-mode: forwards; |
||||
` |
||||
Loading…
Reference in new issue