强制在React中重新加载GIF图像

huangapple go评论62阅读模式
英文:

Force GIF image reload in React

问题

我需要重新加载一个GIF图像(CHECKMARK_ANIMATION_ICON是源),以便每12秒或每当activeIndex更改时开始GIF动画。

这是我的代码:

   const reloadImgSource = (imgSource) => {
       setTimeout(() => {
       setImgSource("");
       setImgSource(imgSource);
      }, 1);
   };

  const handleActiveDot = (index, item) => {
    setActiveIndex(index);
    setActiveTopBonus(item);
    reloadImgSource(CHECKMARK_ANIMATION_ICON);
  };

  useEffect(() => {
    const activeIndexInterval = setInterval(() => {
      reloadImgSource(CHECKMARK_ANIMATION_ICON);
      if (topBonusList && activeIndex !== topBonusList.length - 1) {
        setActiveIndex(activeIndex + 1);
      } else {
        setActiveIndex(0);
      }
    }, 12000);

    return () => clearInterval(activeIndexInterval);

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [activeIndex, topBonusList]);
<img src={`${imgSource}`} alt="Checkmark Icon" />

这段代码是有效的,但我认为reloadImgSource中的解决方案是一种变通方法。
我将setTimeout设置为1,因为否则动画不会触发,因为源未设置为空字符串,然后再设置回原始值以重新触发GIF。

是否有更好的解决方案可以用于reloadImgSource,允许我在函数触发时触发GIF的加载?

请注意,我不能在图像源中使用哈希,因为GIF不会被缓存,这对我来说是个问题。

英文:

I need to reload a GIF image (CHECKMARK_ANIMATION_ICON is the source) to start the gif animation every 12 seconds or every time the activeIndex is changing.

This is my code:

   const reloadImgSource = (imgSource) =&gt; {
       setTimeout(() =&gt; {
       setImgSource(&quot;&quot;);
       setImgSource(imgSource);
      }, 1);
   };

  const handleActiveDot = (index, item) =&gt; {
    setActiveIndex(index);
    setActiveTopBonus(item);
    reloadImgSource(CHECKMARK_ANIMATION_ICON);
  };

  useEffect(() =&gt; {
    const activeIndexInterval = setInterval(() =&gt; {
      reloadImgSource(CHECKMARK_ANIMATION_ICON);
      if (topBonusList &amp;&amp; activeIndex !== topBonusList.length - 1) {
        setActiveIndex(activeIndex + 1);
      } else {
        setActiveIndex(0);
      }
    }, 12000);

    return () =&gt; clearInterval(activeIndexInterval);

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [activeIndex, topBonusList]);
&lt;img src={`${imgSource}`} alt=&quot;Checkmark Icon&quot; /&gt;

This code is working but I think the solution in reloadImgSource is a workaround.
I used setTimeout set to 1 because otherwise the animation is not triggered as the source is not set to an empty string and then back to the original value to retrigger the GIF.

Is there a better solution I can implement for reloadImgSource that allows me to trigger the load of the GIF when the function is fired?

Note that I cannot use hash in the image source as the gif will not be cached and this is a problem for me

答案1

得分: 0

有一种方法可以做到这一点:

function App() {
  const [activeInbox, setActiveInbox] = useState(0);
  const [animation, setAnimation] = useState(false);

  useEffect(() => {
    setAnimation(true);
    const timeout = setTimeout(() => {
      setAnimation(false);
    }, 1200);

    return () => {
      clearTimeout(timeout);
    };
  }, [activeInbox]);

  return (
    <div className="App">
      {animation && (
        <img
          src={`https://cdn.dribbble.com/users/315048/screenshots/3710002/check.gif?${activeInbox}`}
        />
      )}
      <button onClick={() => setActiveInbox((i) => i + 1)}>
        Change active inbox
      </button>
    </div>
  );
}

但我建议避免这种实现方式,因为GIF文件可能无法正确缓存,客户端将在每次更新收件箱时加载新的GIF。如果可以使用其他类型的动画,请考虑使用SVG。

英文:

There is a way how you can do that

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

function App() {
  const [activeInbox, setActiveInbox] = useState(0);
  const [animation, setAnimation] = useState(false);

  useEffect(() =&gt; {
    setAnimation(true);
    const timeout = setTimeout(() =&gt; {
      setAnimation(false);
    }, 1200);

    return () =&gt; {
      clearTimeout(timeout);
    };
  }, [activeInbox]);

  return (
    &lt;div className=&quot;App&quot;&gt;
      {animation &amp;&amp; (
        &lt;img
          src={`https://cdn.dribbble.com/users/315048/screenshots/3710002/check.gif?${activeInbox}`}
        /&gt;
      )}
      &lt;button onClick={() =&gt; setActiveInbox((i) =&gt; i + 1)}&gt;
        Change active inbox
      &lt;/button&gt;
    &lt;/div&gt;
  );
}

<!-- end snippet -->

But I'd suggest avoiding such kind of implementations because the gif won't be cached properly, and the client will load a new gif every inbox update. If you can use another kind of animation, look at SVG.

答案2

得分: 0

useState钩子不会逐行立即更改状态,而是安排更新状态,这是第一个尝试清空imgSource的setImgSource将被忽略的原因。

尝试:

const reloadImgSource = (imgSource) => {       
   setImgSource("");
   setTimeout(() => {
       setImgSource(imgSource);
   }, 1);
};
英文:

useState hook doesn't change state immediately line by line, but it schedule updating state, it is the reason the first setImgSource which tries to empty imgSource will be ignored.

try:

const reloadImgSource = (imgSource) =&gt; {       
   setImgSource(&quot;&quot;);
   setTimeout(() =&gt; {
       setImgSource(imgSource);
   }, 1);
};

答案3

得分: 0

以下是翻译好的代码部分:

const [imgSource, setImgSource] = useState(ANIMATION_ICON);

const reloadImgSource = async (imgSource) => {
  setImgSource(imgSource);
};

const animationTrigger = async () => {
  await reloadImgSource("");
  await reloadImgSource(ANIMATION_ICON);
};

const handleActiveDot = (index, item) => {
  setActiveIndex(index);
  setActiveTopBonus(item);
  animationTrigger();
};

useEffect(() => {
  const activeIndexInterval = setInterval(() => {
    animationTrigger();
    if (topBonusList && activeIndex !== topBonusList.length - 1) {
      setActiveIndex(activeIndex + 1);
    } else {
      setActiveIndex(0);
    }
  }, 12000);

  return () => clearInterval(activeIndexInterval);

  // eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeIndex, topBonusList]);
<img src={imgSource} alt="Animation Icon" />
英文:

For anyone interested, I arrived at this better solution to remove the workaround with setTimeout, the result is the same but the code is more clean and readable, and avoids all caching issues:

  const [imgSource, setImgSource] = useState(ANIMATION_ICON);

  const reloadImgSource = async (imgSource) =&gt; {
    setImgSource(imgSource);
  };

  const animationTrigger = async () =&gt; {
    await reloadImgSource(&quot;&quot;);
    await reloadImgSource(ANIMATION_ICON);
  };

  const handleActiveDot = (index, item) =&gt; {
    setActiveIndex(index);
    setActiveTopBonus(item);
    animationTrigger();
  };

  useEffect(() =&gt; {
    const activeIndexInterval = setInterval(() =&gt; {
      animationTrigger();
      if (topBonusList &amp;&amp; activeIndex !== topBonusList.length - 1) {
        setActiveIndex(activeIndex + 1);
      } else {
        setActiveIndex(0);
      }
    }, 12000);

    return () =&gt; clearInterval(activeIndexInterval);

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [activeIndex, topBonusList]);
&lt;img src={imgSource} alt=&quot;Animation Icon&quot; /&gt;

huangapple
  • 本文由 发表于 2023年7月17日 18:42:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76703672.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定