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"}/>
有没有办法动态更改 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"}/>