Skip to content

Commit

Permalink
Merge pull request #41 from azavea/rm/animate-time-slider
Browse files Browse the repository at this point in the history
Add animated temporal progress bar
  • Loading branch information
rachelekm authored Mar 1, 2023
2 parents e2f0d57 + bcc6e30 commit 2a6f13d
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 61 deletions.
118 changes: 58 additions & 60 deletions src/app/src/components/AnimatedMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,26 @@ import { useMap } from 'react-leaflet';
import {
Heading,
Spacer,
HStack,
Slider,
Box,
SliderTrack,
SliderFilledTrack,
SliderThumb,
SliderMark,
IconButton,
Progress,
Tag,
TagLabel,
} from '@chakra-ui/react';

import L from 'leaflet';
import UsaMapContainer from './UsaMapContainer';
import { StateGeometry, StateProperties } from './states.geojson';

import StatesLayer from './StatesLayer';
import TimeControlIcon from './TimeControlIcon';

import {
MonthlySpendingOverTimeResponse,
SpendingByGeographyAtMonth,
} from '../types/api';
import { spendingDataByMonth } from './dummySpendingDataByMonth';
import AnimatedMapLegend from './AnimatedMapLegend';

export default function AnimatedMap() {
return (
Expand All @@ -31,32 +31,10 @@ export default function AnimatedMap() {
Allocation of announced award funding over time
</Heading>
<Spacer></Spacer>
<AnimatedMapLegend />
<UsaMapContainer>
<StatesAndSliderLayer spending={spendingDataByMonth} />
</UsaMapContainer>
<HStack spacing='0px' border={'1px'}>
<Box w='40px' h='40px' bg='white'></Box>
<Box
w='40px'
h='40px'
bg='#94A4DF'
textAlign={'center'}
color={'white'}
fontSize={'sm'}
>
≥1% BIL
</Box>
<Box
w='40px'
h='40px'
bg='#465EB5'
textAlign={'center'}
color={'white'}
fontSize={'sm'}
>
≥2% BIL
</Box>
</HStack>
</>
);
}
Expand All @@ -66,11 +44,14 @@ function StatesAndSliderLayer({
}: {
spending: MonthlySpendingOverTimeResponse;
}) {
const SLIDER_PRESENT_STEP = 26;
const PROGRESS_FINAL_MONTH = 26;
const map = useMap();
const [timeValue, setTimeValue] = useState(0);
const [spendingAtTimeByState, setSpendingAtTimeByState] = useState(() =>
getSpendingByStateAtTime(1, spending)
);
const [animationEnabled, setAnimationEnabled] = useState(false);
const [restartTimeControl, setRestartTimeControl] = useState(false);

useEffect(() => {
map &&
Expand All @@ -95,8 +76,33 @@ function StatesAndSliderLayer({
});
}, [map, spendingAtTimeByState]);

function onSliderChange(timeValue: number) {
setSpendingAtTimeByState(getSpendingByStateAtTime(timeValue, spending));
useEffect(() => {
(timeValue % 1 === 0) && spending && setSpendingAtTimeByState(getSpendingByStateAtTime(timeValue, spending));
if(timeValue === PROGRESS_FINAL_MONTH){
setAnimationEnabled(false);
setRestartTimeControl(true);
}
}, [timeValue, spending])

useEffect(() => {
if(animationEnabled){
const monthlyInterval = setInterval(() => {
setTimeValue(currentTimeValue => Math.round((currentTimeValue + 0.1)*10)/10);
},
25
);
return () => {
clearInterval(monthlyInterval);
};
}
}, [animationEnabled]);

function onSelectTimeAnimation(){
if(restartTimeControl){
setTimeValue(0);
setRestartTimeControl(false);
}
setAnimationEnabled(true);
}

return (
Expand All @@ -118,34 +124,26 @@ function StatesAndSliderLayer({
});
}}
/>
<Slider
colorScheme={'blackAlpha'}
aria-label='date-time-slider'
defaultValue={0}
min={0}
max={SLIDER_PRESENT_STEP}
onChange={val => onSliderChange(val)}
step={1}
mt='575px'
width='50%'
ml='20%'
>
<SliderThumb ml='50px' />
<SliderMark value={0} mt='-2' mr='15' fontSize='m'>
2021
</SliderMark>
<SliderMark
value={SLIDER_PRESENT_STEP}
mt='-2'
ml='70'
fontSize='m'
>
present
</SliderMark>
<SliderTrack ml='50px'>
<SliderFilledTrack />
</SliderTrack>
</Slider>
<Box mt='575px' textAlign={'center'}>
<IconButton aria-label='Play time progress animation' icon={<TimeControlIcon restart={restartTimeControl} />} mr='25px' background='none' onClick={onSelectTimeAnimation} isDisabled={animationEnabled} />
<Tag width='60%' maxWidth={'750px'} background='none'>
<TagLabel mt='-30px' mr='-35px' overflow={'none'}>2021</TagLabel>
<Progress
value={timeValue}
opacity={100}
colorScheme={'progress'}
aria-label='date-time-progress-bar'
min={0}
max={PROGRESS_FINAL_MONTH}
width='100%'
maxWidth={'750px'}
height='20px'
mt='10px'
display={'inline-block'}
/>
<TagLabel mt='-30px' ml='-35px' overflow={'none'}>Now</TagLabel>
</Tag>
</Box>
</>
);
}
Expand Down
29 changes: 29 additions & 0 deletions src/app/src/components/AnimatedMapLegend.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { HStack, Box } from '@chakra-ui/react';

export function ColorRangeBox({ bg, text }: { bg: string, text?: String }) {
return (
<Box
w='70px'
h='40px'
bg={bg}
textAlign={'center'}
color={'white'}
fontSize={'sm'}
pt='8px'
>
{text}
</Box>
);
}

export default function AnimatedMapLegend() {
return (
<Box width='100%' pl='20%'>
<HStack spacing='0px' border={'1px'} width='210px'>
<ColorRangeBox bg='white' />
<ColorRangeBox bg='#94A4DF' text='≥1% BIL' />
<ColorRangeBox bg='#465EB5' text='≥2% BIL' />
</HStack>
</Box>
);
}
21 changes: 21 additions & 0 deletions src/app/src/components/TimeControlIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Icon } from '@chakra-ui/react';

export default function TimeControlIcon({ restart }: { restart: boolean }) {
return restart ? (
<Icon viewBox='0 0 512 512' color='black' width='45px' height='45px'>
<path
fill='currentColor'
d='M212.333 224.333H12c-6.627 0-12-5.373-12-12V12C0 5.373 5.373 0 12 0h48c6.627 0 12 5.373 12 12v78.112C117.773 39.279 184.26 7.47 258.175 8.007c136.906.994 246.448 111.623 246.157 248.532C504.041 393.258 393.12 504 256.333 504c-64.089 0-122.496-24.313-166.51-64.215-5.099-4.622-5.334-12.554-.467-17.42l33.967-33.967c4.474-4.474 11.662-4.717 16.401-.525C170.76 415.336 211.58 432 256.333 432c97.268 0 176-78.716 176-176 0-97.267-78.716-176-176-176-58.496 0-110.28 28.476-142.274 72.333h98.274c6.627 0 12 5.373 12 12v48c0 6.627-5.373 12-12 12z'
data-attibution='Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc.'
/>
</Icon>
) : (
<Icon viewBox='0 0 384 512' color='black' width='55px' height='55px'>
<path
fill='currentColor'
d='M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z'
data-attibution='Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc.'
/>
</Icon>
);
}
5 changes: 4 additions & 1 deletion src/app/src/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ const theme = extendTheme({
},
colors: {
tooltip: {
500: '#465EB5',
500: '#465EB5',
},
progress: {
500: '#000000',
},
},
});
Expand Down

0 comments on commit 2a6f13d

Please sign in to comment.