序言
本文实现了在图片上绘制文字 的两种思路,并通过demo的方式,去具体实现,实现代码为组件形式,支持即粘即贴即用。
效果展示
需求简介
遇到一个这样的需求,产品要求根据B端用户上传的图片模板,在C端绘制上个性字体且展示 ,最后也支持C端用户下载绘制后的图片。
解决思路
我们先梳理一下这个需求的一些关键的点,即图片来源、图片传递路径、获取图片、图片绘制文字、下载图片
graph TB
B端上传图片 --> 服务端保存到一个固定地址并提供url --> C端获取到图片地址 --> 将文字绘制到图片上 --> 将图片加文字整体下载下来保存为图片
那最关键的步骤就是图片绘制
我这里想到了两种方案:
方案一:定位+html2canvas
将图片用Image 渲染出来并放在一个DOM 中,然后将文字 再单独写一个DOM ,通过定位 的形式将其定位到对应的位置,最后通过html2canvas ,将整个DOM绘制成一个canvas,转成图片下载下来。
代码实现
js
import React from "react";
import html2Canvas from "html2canvas";
import { Button, Image } from "antd-mobile";
type LoadCanvasImgProps = {
containerStyle?: React.CSSProperties; // 最外层父组件的样式
backgroundImageSrc?: string; // 背景图
ImageWidth?: string | number;
text?: string; // 需要写的值
textStyle?: React.CSSProperties; // 渲染文本的样式
buttonValue?: string; // button值
onClickButton?: () => void; // 点击下载前执行的函数
loadName?: string; // 下载后的文件名称
};
const LoadCanvasImg: React.FC<LoadCanvasImgProps> = ({
text = "我是名字",
loadName = "load",
buttonValue = "点击下载",
backgroundImageSrc = "",
textStyle = { position: "absolute", top: "0px", left: "0px" },
containerStyle,
ImageWidth = 100,
onClickButton,
}) => {
const onHtml2Canvas = async () => {
try {
const loadBody = document.getElementById("loadBody") as HTMLElement;
const canvas = await html2Canvas(loadBody, {
useCORS: true,
allowTaint: false,
});
downloadCanvasImg(canvas);
onClickButton && onClickButton();
} catch (error) {
console.error("Error generating canvas image:", error);
}
};
// 下载canvas
const downloadCanvasImg = (canvas: HTMLCanvasElement) => {
const dataURL = canvas.toDataURL("image/png") || "";
const downloadLink = document.createElement("a");
downloadLink.href = dataURL;
downloadLink.download = `${loadName}.png`;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
};
return (
<div className="loadCanvasImg" style={containerStyle}>
<div id="loadBody">
<div style={textStyle}>{text}</div>
<Image src={backgroundImageSrc} width={ImageWidth} alt="Background" />
</div>
<Button onClick={onHtml2Canvas} color="primary">
{buttonValue}
</Button>
</div>
);
};
export default LoadCanvasImg;
使用方式
js
import React from "react";
import LoadCanvasImg from "..."; // 这里的路径是你的组件路径
const Demo1 = () => {
return (
<div>
<LoadCanvasImg
backgroundImageSrc={""} // 图片的url
text={"我是名字"} // 需要绘制的值
containerStyle={{ position: "relative" }}
textStyle={{
position: "absolute",
top: "10px",
left: "100px",
fontSize: "20px",
}}
buttonValue="保存证书"
onClickButton={() => {}} // 点击下载前执行的函数
ImageWidth={200}
/>
</div>
);
};
方案二:canvas绘制
先用canvas绘制图片 ,再用canvas绘制字体,最后转成图片下载下来。
代码实现
js
import React, { useEffect, useRef } from "react";
import { Button } from "antd-mobile";
interface CanvasFontSizeProps {
text?: string; // 要绘制的文字
backgroundImageSrc?: string; // 图片的url链接
x?: number; // 图片的x轴定位
y?: number; // 图片的y轴定位
textStyle?: React.CSSProperties; // 绘制的文字样式
fillStyle?: string | CanvasGradient | CanvasPattern; // canvas绘制文字的样式
canvasStyle?: React.CSSProperties; // canvas的样式
}
const CanvasFontSize: React.FC<CanvasFontSizeProps> = ({
text = "姓名",
backgroundImageSrc = "",
x = 100,
y = 100,
textStyle = { fontFamily: "e1kcQpNW_GBK_ry", fontSize: "22px" },
fillStyle = "#000",
canvasStyle = { width: 800, height: 600 },
}) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const downloadCanvasImage = () => {
const canvas = canvasRef.current;
const dataURL = canvas?.toDataURL("image/png") || "";
const downloadLink = document.createElement("a");
downloadLink.href = dataURL;
downloadLink.download = "canvas_image.png";
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
};
useEffect(() => {
const canvas = canvasRef.current as HTMLCanvasElement;
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
const backgroundImage = new Image();
backgroundImage.setAttribute('crossOrigin', 'Anonymous'); // 跨域的时候加上
backgroundImage.src = backgroundImageSrc;
backgroundImage.onload = () => {
ctx.drawImage(backgroundImage, 0, 0, canvas.width, canvas.height);
ctx.font = `${textStyle.fontSize} ${textStyle.fontFamily}`;
ctx.fillStyle = fillStyle;
ctx.fillText(text, x, y);
};
}, [text, x, y, backgroundImageSrc, textStyle, fillStyle]);
return (
<div>
<canvas ref={canvasRef} style={canvasStyle} />
<Button onClick={downloadCanvasImage} color="primary">
下载链接
</Button>
</div>
);
};
export default CanvasFontSize;
使用方式
js
import React from "react";
import CanvasFontSize from "..."; // 这里的路径是你的组件路径
const Demo1 = () => {
return (
<div>
<CanvasFontSize
backgroundImageSrc={""} // 图片的url
text={"我是名字"} // 需要绘制的值
x={10} // x轴坐标
y={10} // y轴坐标
textStyle = { fontFamily: "e1kcQpNW_GBK_ry", fontSize: "22px" } // 文字样式
fillStyle = "#000" // 文字颜色
canvasStyle = { width: 800, height: 600 } // canvas样式
/>
</div>
);
};
两种思路的优缺点对比
优点 | 缺点 | |
---|---|---|
定位+html2canvas | 对绘制复杂样式支持友好 | 需要安装第三方依赖 |
canvas绘制 | 使用简单,canvas原生支持 | 遇到复杂样式绘制较为困难 |
遇到的一些坑
图片跨域
使用canvas画图片 的时候,当图片域名与项目域名不一致 的时候,浏览器会报跨域错误 ,或执行到const dataURL = canvas?.toDataURL("image/png") || ""
这一步报错;
解决办法 :在使用 Image 对象时添加上backgroundImage.setAttribute('crossOrigin', 'Anonymous')
最后
欢迎大家来讨论交流,遇到啥问题欢迎咨询我,若是有别的建议,也可评论区或者私信我!!!!!!
最后祝大家一夜暴富,梦想成真!!!!!!