import { Canvas, Image, View } from "@tarojs/components"; import Taro, { FC } from "@tarojs/taro"; import { useEffect, useRef, useState } from "react"; import { AtFloatLayout } from "taro-ui"; import { AtFloatLayoutProps } from "taro-ui/types/float-layout"; import CloseIcon from "../../../images/icon_back@2x-min.png"; import "./index.scss"; export interface CertLayoutProps extends AtFloatLayoutProps { name: string; date: string; } const system = Taro.getSystemInfoSync(); export const CertLayout: FC = ({ name, date, ...props }) => { const loaded = useRef(false); const [imgPath, setImgPath] = useState(""); useEffect(() => { if (props.isOpened && !loaded.current) init(); }, [props.isOpened]); const init = async () => { try { Taro.showLoading({ title: "绘制中", }); await new Promise((resolve, reject) => { Taro.createSelectorQuery() .select("#certCanvas") .fields({ node: true, size: true }, async (res) => { const canvas = res.node; const ctx = canvas.getContext("2d") as CanvasRenderingContext2D; canvas.width = res.width * system.pixelRatio; canvas.height = res.height * system.pixelRatio; try { const bgInfo = await getTempImgPath( "https://wuxicharitymuseum.cn/wx-csbwg-public/images/cert-min.png" ); const ratio = bgInfo.width / canvas.width; const bgSource = await loadImg(canvas, bgInfo.path); ctx.drawImage(bgSource, 0, 0, canvas.width, canvas.height); ctxText( ctx, "特此授予锡善云城·热心市民".split("").join(" "), `${7 * system.pixelRatio}px SourceHanSansSCRegular`, "center", "#424A4A", canvas.width / 2, 800 / ratio ); ctxText( ctx, name.split("").join(" "), `${15 * system.pixelRatio}px SourceHanSansCN-Bold`, "center", "#CFC49E", canvas.width / 2, 1020 / ratio ); ctxTextWrap( ctx, "特此证明,感谢您的热情参与与慷慨支持。您的善举为社区带来了无限的温暖与希望。因您的无私奉献,特授予您热心市民称号,以表彰您对慈善事业的贡献。您的善行将永远激励着我们,为构建更美好的社会贡献一份力量。", `${7 * system.pixelRatio}px SourceHanSansSCRegular`, "center", "#424A4A", canvas.width / 2, 1200 / ratio, 460 * system.pixelRatio ); ctxText( ctx, date, `${6 * system.pixelRatio}px SourceHanSerifCN-Bold`, "left", "#424A4A", 740 / ratio, 1660 / ratio ); ctxText( ctx, "锡善云城", `${6 * system.pixelRatio}px SourceHanSerifCN-Bold`, "left", "#424A4A", 1880 / ratio, 1660 / ratio ); await new Promise((resolve, reject) => { Taro.canvasToTempFilePath({ canvas, fileType: "jpg", success(res2) { setImgPath(res2.tempFilePath); loaded.current = true; resolve(true); }, fail(err) { Taro.showToast({ title: err.errMsg, icon: "none", duration: 4000, }); reject(err); }, }); }); resolve(true); } catch (err) { reject(false); } }) .exec(); }); } finally { Taro.hideLoading(); } }; const loadImg = (canvas, src) => { return new Promise((res, rej) => { const source = canvas.createImage(); source.src = src; source.onload = () => { res(source); }; source.onerror = rej; }) as Promise; }; const ctxTextWrap = (ctx, text, font, align, color, x, y, maxWidth) => { let line = ""; const lines: string[] = []; const words = text.split(""); for (let i = 0; i < words.length; i++) { const word = words[i]; const testLine = line + word; const metrics = ctx.measureText(testLine); if (metrics.width > maxWidth && i > 0) { lines.push(line); line = word; } else { line = testLine; } } lines.push(line); lines.forEach((t, idx) => ctxText(ctx, t, font, align, color, x, y + idx * 28) ); }; const ctxText = (ctx, text, font, align, color, x, y) => { ctx.beginPath(); ctx.font = font; ctx.textAlign = align; ctx.fillStyle = color; ctx.fillText(text, x, y); ctx.save(); }; const getTempImgPath = async (src: string) => { return new Promise((resolve, reject) => { Taro.getImageInfo({ src, success(res) { resolve(res); }, fail(err) { reject(err); }, }); }) as Promise; }; return ( {imgPath ? ( <> { Taro.previewImage({ current: imgPath, urls: [imgPath], }); }} /> 长按图片,保存证书 ) : ( )} ); };