封装类:
/** * Canvas绘制海报 */ class Poster { canvas: HTMLCanvasElement; context: CanvasRenderingContext2D; constructor(el: string, width: number, height: number) { const canvas = document.querySelector<HTMLCanvasElement>(el); if (canvas === null) { throw new Error("获取canvas失败"); } canvas.width = width; canvas.height = height; this.canvas = canvas; const context = canvas.getContext("2d"); if (context === null) { throw new Error("获取context失败"); } this.context = context; } setFillStyle(style: string) { this.context.fillStyle = style; return this; } setStrokeStyle(style: string) { this.context.strokeStyle = style; return this; } setFont(font: string) { this.context.font = font; return this; } fillCanvas() { this.context.fillRect(0, 0, this.canvas.width, this.canvas.height); return this; } clearCanvas() { this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); return this; } loadImage(src: string) { return new Promise<HTMLImageElement>((resolve, reject) => { const img = new Image(); img.src = src; img.crossOrigin = "anonymous"; img.onload = function () { resolve(img); }; }); } drawImage(image: CanvasImageSource, x: number, y: number, width: number, height: number) { this.context.drawImage(image, x, y, width, height); return this; } drawText(text: string, x: number, y: number, limitWidth?: number, lineHeight?: number) { if (limitWidth != undefined) { let measureTextWidth = 0; let lastSubstringIndex = 0; for (let i = 0; i < text.length; i++) { const metrics = this.context.measureText(text[i]); measureTextWidth += metrics.width; if (measureTextWidth > limitWidth) { this.context.fillText(text.substring(lastSubstringIndex, i), x, y); measureTextWidth = 0; lastSubstringIndex = i; y += lineHeight ?? metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent; } if (i === text.length - 1) { this.context.fillText(text.substring(lastSubstringIndex, i + 1), x, y); } } } else { this.context.fillText(text, x, y); } return this; } drawLine(x1: number, y1: number, x2: number, y2: number) { this.context.moveTo(x1, y1); this.context.lineTo(x2, y2); this.context.stroke(); return this; } toDataURL(type?: string, quality?: any) { return this.canvas.toDataURL(type, quality); } download(filename: string = Date.now().toString()) { let ext = "png"; if (filename.includes(".")) { const filenameSplit = filename.split("."); ext = filenameSplit[filenameSplit.length - 1].toLowerCase(); } let mime = "image/png"; if (ext === "jpg" || ext === "jpeg") { mime = "image/jpeg"; } const base64 = this.toDataURL(mime); const aEl = document.createElement("a"); aEl.href = base64; aEl.setAttribute("download", filename); aEl.click(); } } export default Poster;
使用示例:
<script setup lang="ts"> import Poster from "@/utils/poster"; import QRCode from "qrcode"; import { onMounted, ref } from "vue"; defineOptions({ name: "Poster", }); // 获取本地图片 function getAssetImg(name: string) { return new URL(`../../assets/poster/${name}`, import.meta.url).href; } async function generatePoster() { const width = 750; const height = 1334; const poster = new Poster("#canvas", width, height); const bgImg = await poster.loadImage(getAssetImg("bg.jpeg")); // 生成二维码base64图片 const qrcodeUrl = await QRCode.toDataURL(`https://www.baidu.com`); const qrcodeImg = await poster.loadImage(qrcodeUrl); // 开始绘制 poster .drawImage(bgImg, 0, 0, width, height) .drawImage(qrcodeImg, width - 150, height - 150, 100, 100) .setFillStyle("#fff") .setFont("14px bold 黑体") .drawText("打开爱奇艺app,扫码领取积分,赢取豪华大礼,惊喜等着你~", 50, height - 100, 250, 20); return poster; } const posterObj = ref(); const isLoading = ref(true); onMounted(async () => { posterObj.value = await generatePoster(); isLoading.value = false; }); </script> <template> <div> <div v-if="isLoading">生成中。。。</div> <div v-show="!isLoading"> <canvas id="canvas"></canvas> <div> <button type="button" @click="posterObj.download()">保存海报</button> </div> </div> </div> </template> <style lang="scss" scoped></style>
标签:Canvas,const,海报,number,context,canvas,return,height,绘制 From: https://www.cnblogs.com/cshaptx4869/p/18396575