Skip to content

Commit

Permalink
Merge pull request #30 from CartoDB/feature/ch120584/animate-category…
Browse files Browse the repository at this point in the history
…-widget-values

Animate CategoryWidget values
  • Loading branch information
neokore authored Nov 26, 2020
2 parents 3a7af1f + 6c3f203 commit 5b7f61c
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Not released
- Fix CategoryWidgetUI displaying no data while loading [#26](https://github.com/CartoDB/carto-react-lib/pull/26)
- Animate CategoryWidget values [#30](https://github.com/CartoDB/carto-react-lib/pull/30)
- Make OAuthLogin component responsive [#28](https://github.com/CartoDB/carto-react-lib/pull/28)

## 1.0.0-beta5 (2020-11-25)
Expand Down
53 changes: 50 additions & 3 deletions src/ui/widgets/CategoryWidgetUI.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import {
Button,
Expand Down Expand Up @@ -102,6 +102,14 @@ const useStyles = makeStyles((theme) => ({
},
}));

function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}

const REST_CATEGORY = '__rest__';

const SearchIcon = () => (
Expand All @@ -122,6 +130,8 @@ function CategoryWidgetUI(props) {
const [searchValue, setSearchValue] = useState('');
const [blockedCategories, setBlockedCategories] = useState([]);
const [tempBlockedCategories, setTempBlockedCategories] = useState(false);
const [animValues, setAnimValues] = useState([]);
const prevAnimValues = usePrevious(animValues);
const classes = useStyles();

const handleCategorySelected = (category) => {
Expand Down Expand Up @@ -294,6 +304,43 @@ function CategoryWidgetUI(props) {
showAll,
]);

useEffect(() => {
animateValues(prevAnimValues || [], sortedData, 500, (val) => setAnimValues(val));
}, [sortedData]);

const animateValues = (start, end, duration, drawFrame) => {
const isEqual = start.length === end.length && start.every((val, i) => val.value === end[i].value);
if (isEqual) return;

let currentValues = end.map((elem, i) => start[i] && start[i].category === elem.category
? { ...elem, value: start[i].value }
: elem
);
let currentFrame = 0;

const ranges = currentValues.map((elem, i) => end[i].value - elem.value);
const noChanges = ranges.every((val) => val === 0);
if (noChanges) {
drawFrame(end);
return;
}

const frames = (duration / 1000) * 60;
const steps = ranges.map((val) => val / frames);

const animate = () => {
if (currentFrame < frames) {
currentValues = currentValues.map((elem, i) => ({...elem, value: elem.value + steps[i]}) );
drawFrame(currentValues);
currentFrame++;
requestAnimationFrame(animate);
} else {
drawFrame(end);
}
};
requestAnimationFrame(animate);
}

// Separated to simplify the widget layout but inside the main component to avoid passing all dependencies
const CategoryItem = (props) => {
const { data, onCategoryClick } = props;
Expand Down Expand Up @@ -438,8 +485,8 @@ function CategoryWidgetUI(props) {
</Grid>
)}
<Grid container item className={classes.categoriesWrapper}>
{sortedData.length
? sortedData.map((d, i) => (
{animValues.length
? animValues.map((d, i) => (
<CategoryItem
key={i}
data={d}
Expand Down

0 comments on commit 5b7f61c

Please sign in to comment.