| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- 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<CertLayoutProps> = ({ 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<CanvasImageSource>;
- };
- 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<Taro.getImageInfo.SuccessCallbackResult>;
- };
- return (
- <AtFloatLayout className="cert" {...props}>
- <View className="cert-wrap">
- {imgPath ? (
- <>
- <Image
- className="cert__img"
- src={imgPath}
- showMenuByLongpress
- onClick={() => {
- Taro.previewImage({
- current: imgPath,
- urls: [imgPath],
- });
- }}
- />
- <View className="cert__tips">长按图片,保存证书</View>
- </>
- ) : (
- <Canvas
- id="certCanvas"
- type="2d"
- className="cert__img"
- style="position:absolute; top: -200%; left: -200%"
- />
- )}
- </View>
- <Image className="cert__close" src={CloseIcon} onClick={props.onClose} />
- </AtFloatLayout>
- );
- };
|