React IconTint (canvas) 动态改变颜色

React IconTint (canvas) change color dynamically

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

我还尝试在 isActive 上重新渲染整个元素

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

但是还是不行。

我看过源码,因为它是一个memoized组件,它不会在父级重新渲染时重新渲染,而且因为在组件内部使用的useEffect中,不包括颜色和图像src等依赖项,它不会在 prop 更改时重新渲染,显然这是一个错误或不需要的行为。我不知道为什么您的方法不起作用,因为这两个 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"}/>