【问题标题】:React IconTint (canvas) change color dynamicallyReact IconTint(画布)动态改变颜色
【发布时间】:2021-12-17 00:09:49
【问题描述】:

有没有办法从react-icon-tint 库中动态更改<IconTint /> 元素的颜色? 我需要在 isActive 状态更改时更改颜色,但它看起来不像我预期的那样工作,因为它在引擎盖下使用了 canvas 元素。

我还尝试重新渲染isActive 上的整个元素,像这样改变

{
  isActive ? (
    <IconTint src={getCustomIcon()} color={icon} />
  ) : (
    <IconTint src={getCustomIcon()} color={iconOnBackground} />
  );
}

但它也没有工作。

【问题讨论】:

    标签: javascript html reactjs html5-canvas


    【解决方案1】:

    我查看了源代码,因为它是一个记忆化组件,它不会在父级重新渲染时重新渲染,并且因为在组件内部使用的 useEffect 内部,不包括颜色和图像 src 等依赖项,它确实不重新渲染道具更改,显然这是一个错误或不受欢迎的行为。我不知道为什么你的做法不起作用,因为这两个 IconTint 组件是两个不同的组件!但你可以这样做:

     {isActive && <IconTint src={getCustomIcon()} color='#0000ff' />}
     {!isActive && <IconTint src={getCustomIcon()} color='#b43285' />}
    

    我不认为这是高性能的,源代码是如此简单,所以你可以在你的项目中实现 IconTint 并在 useEffect 依赖数组中添加依赖。这里我提供了修改后的版本。

    import React, { memo, useEffect, useRef, useState } from "react";
    import PropTypes from "prop-types";
    
    const IconTint = ({ fallback = <span />, src, color, maxWidth, maxHeight }) => {
      const canvasRef = useRef(null);
      const [size, setSize] = useState({});
    
      const _scaleImage = (srcWidth, srcHeight, maxWidth, maxHeight) => {
        if (maxWidth && maxHeight) {
          const ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight);
          return { width: srcWidth * ratio, height: srcHeight * ratio };
        }
        if ((maxWidth && !maxHeight) || (!maxWidth && maxHeight)) {
          throw new Error(
            "If you are going to provide width, make sure to provide height as well"
          );
        }
        return { width: srcWidth, height: srcHeight };
      };
    
      useEffect(() => {
        const canvas = canvasRef.current;
        // eslint-disable-next-line no-undef
        const pic = new Image();
        pic.src = src;
        const tintCanvas = document.createElement("canvas");
        const tintCtx = tintCanvas.getContext("2d");
        const ctx = canvas.getContext("2d");
        pic.onload = () => {
          const result = _scaleImage(pic.width, pic.height, maxWidth, maxHeight);
          setSize(result);
          tintCanvas.width = result.width;
          tintCanvas.height = result.height;
          tintCtx.fillStyle = color;
          tintCtx.fillRect(0, 0, result.width, result.height);
          tintCtx.globalCompositeOperation = "destination-atop";
          tintCtx.drawImage(pic, 0, 0, result.width, result.height);
          ctx.globalAlpha = 1;
          ctx.drawImage(tintCanvas, 0, 0, result.width, result.height);
        };
      }, [src,color,maxHeight,maxWidth]);
    
      if (
        typeof window !== "undefined" &&
        window.document &&
        window.document.createElement
      ) {
        return <canvas width={size.width} height={size.height} ref={canvasRef} />;
      }
      return fallback;
    };
    
    IconTint.propTypes = {
      src: PropTypes.string.isRequired,
      color: PropTypes.string.isRequired,
      fallback: PropTypes.node,
      maxWidth: PropTypes.number,
      maxHeight: PropTypes.number
    };
    
    export default memo(IconTint);
    

    现在您可以像这样使用这个修改后的版本:

    <IconTint src={getCustomIcon()} color={isActive ? '#0000ff' : "#b43285"}/>
    

    【讨论】:

      猜你喜欢
      • 2018-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-21
      • 1970-01-01
      • 2018-12-31
      相关资源
      最近更新 更多