trendmicro-frontend / react-liquid-gauge

React Liquid Gauge component
https://trendmicro-frontend.github.io/react-liquid-gauge
MIT License
97 stars 20 forks source link

riseAnimationTime not working in this minimalist example #10

Closed marcmaxson closed 2 years ago

marcmaxson commented 2 years ago

This library seems to do exactly what I want, but I can't get the feature to work.

What I want is a fill-gauge that slowly fills over 10 minutes, like a timer. I removed the text and added the riseAnimationTime parameter, from the API, but the gauge fills instantly.

I'd like to avoid using a callback to update value, but I will try that if there's no obvious bug here.

import { color } from "d3-color";
import { interpolateRgb } from "d3-interpolate";
import ReactDOM from 'react-dom';
import LiquidFillGauge from "react-liquid-gauge";

const TimerGauge = ({ value = 100, ...props }) => {
  const startColor = "#6495ed"; // cornflowerblue
  const endColor = "#dc143c"; // crimson
  const radius = 100;
  const interpolate = interpolateRgb(startColor, endColor);
  const fillColor = interpolate(value / 100);
  const gradientStops = [
    {
      key: "0%",
      stopColor: color(fillColor).darker(0.5).toString(),
      stopOpacity: 1,
      offset: "0%"
    },
    {
      key: "50%",
      stopColor: fillColor,
      stopOpacity: 0.75,
      offset: "50%"
    },
    {
      key: "100%",
      stopColor: color(fillColor).brighter(0.5).toString(),
      stopOpacity: 0.5,
      offset: "100%"
    }
  ];

  return (
    <LiquidFillGauge
      width={radius * 2}
      height={radius * 2}
      value={value}
      textSize={0}
      riseAnimation
      riseAnimationTime={60000}
      innerRadius={0.95}
      gradient
      gradientStops={gradientStops}
    />
  );
};

export default function App() {
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <TimerGauge />
    </div>
  );
}

Also this is in your code samples but I don't understand what it is for. And it gives errors when I include it. Isn't this supposed to use react components (instead of affecting the DOM directly, like D3)?

ReactDOM.render(
  <App />,
  document.getElementById('container')
);

working example: https://codesandbox.io/embed/hardcore-hofstadter-qvomk2?fontsize=14&hidenavigation=1&theme=dark

marcmaxson commented 2 years ago

Update: This version is working in the sandbox, but recreates the error when I try to incorporate gradient and gradientStops:

https://codesandbox.io/embed/hardcore-hofstadter-qvomk2?fontsize=14&hidenavigation=1&theme=dark

It would be nice to be able to start this above 0, and use the color gradient, but I'll try to make do with it as-is, thanks.

Also, simply specifying a circleStyle or waveStyle (e.g. color) to the animation breaks it:

the commented out stuff prevented the gauge from filling dynamically:

const TimerGauge = ({ radius = 100, value = 90 }) => {
  // const edgeColor = color('#00758F').toString()
  // const centerColor = color('#6B5B95').toString()

  return (
    <LiquidFillGauge
      //circleStyle={{
      //  fill: edgeColor
      //}}
      //waveStyle={{
      //  fill: centerColor
      //}}
cheton commented 2 years ago

You can use useEffect and useState Hooks with setInterval to gradually fill the gauge over a period of time.

import "./styles.css";

import { useEffect, useState } from 'react';
import { color } from "d3-color";
import { interpolateRgb } from "d3-interpolate";
import LiquidFillGauge from "react-liquid-gauge";

const TimerGauge = () => {
  const radius = 100;
  const [value, setValue] = useState(10);

  useEffect(() => {
    const startTime = Date.now();
    const endTime = startTime + 10 * 60 * 1000;
    const startValue = 10;
    const endValue = 100;

    let timer = setInterval(() => {
      const currentTime = Date.now();
      if (currentTime >= endTime) {
        clearInterval(timer);
        timer = null;
        return;
      }
      const nextValue = (currentTime - startTime) / (endTime - startTime) * 100;
      setValue(nextValue);
    }, 1000)

    return () => {
      timer && clearInterval(timer);
    };
  }, []);

  return (
    <LiquidFillGauge
      color="green"
      width={radius * 2}
      height={radius * 2}
      value={value}
      textSize={0}
      riseAnimation={true}
      riseAnimationTime={1000}
      innerRadius={0.95}
      waveAnimation={true}
      waveAnimationTime={10000}
      waveAmplitude={2}
      waveFrequency={4}
    />
  );
};

export default function App() {
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <TimerGauge />
    </div>
  );
}
marcmaxson commented 2 years ago

The riseAminationTime parameter is ignored in react-18, but works with react-17.

marcmaxson commented 2 years ago

I'll try your workaround (from 26 days ago) and get back to you if it doesn't work. But it would be helpful to point out in your docs that riseAnimationTime won't work as described in react-18.

marcmaxson commented 2 years ago

Thanks @cheton your react useState workaround solved my problem! I can now control the animation better, and it works in react-18.