当前位置: 首页 > news >正文

原生微信小程序,canvas生成凭证,保存到手机

原生微信小程序通过canvas可以将表单( 文本、图片 )转化成图片,同时添加水印,生成凭证,这里只是基本功能实现。可以继续完善和扩展。

<view class="container"><!-- Canvas 组件 --><canvas type="2d" id="myCanvas" style="width: {{canvasWidth}}px;height: {{canvasHeight}}px;"></canvas><!-- 保存按钮 --><button bindtap="saveImage">保存图片到手机</button>
</view>
Page({data: {canvasWidth: 300,canvasHeight: 0,fields: {schemeType: "方案类型:普通保险",mainInsurance: "主险金额:100,000元",medicalInsurance: "附加医疗险:20,000元",employerInsurance: "附加补充雇主险:50,000元",thirdPartyInsurance: "附加第三者财产险:30,000元",imgs: ['https://cdn.bellmesse.com/upload/images/article/20241031/17303356875926158.jpg', // 示例图片1'https://cdn.bellmesse.com/upload/images/article/20241031/17303356875926158.jpg', // 示例图片2'https://cdn.bellmesse.com/upload/images/article/20241031/17303356875926158.jpg', // 示例图片3],},watermarkText: "2025-4-24 xx保险公司 xx",},onReady() {this.init();},async init() {let {canvas,ctx} = await this.initCanvas();let paddingTop = 30; // 顶部预留空间let titleHeight = 30; // 标题高度let oneFieldHeight = 30; // 单字段高度// 总高度let totalHeight = paddingTop + titleHeight + Object.keys(this.data.fields).length * oneFieldHeight;// 减去imgif (this.data.fields.imgs) {totalHeight -= oneFieldHeight;}// 计算图片的缩放比例和高度let images = await this.initImg(canvas, this.data.fields.imgs);if (images.length) {images = images.map(image => {const originalWidth = image.width;const originalHeight = image.height;const newHeight = (this.data.canvasWidth / originalWidth) * originalHeight;let result = {imageSource: image,x: 0,y: totalHeight,width: this.data.canvasWidth,height: newHeight}totalHeight = totalHeight + newHeight + 16;return result;})}// 设置 Canvas 尺寸this.setData({canvasHeight: totalHeight})const dpr = wx.getSystemInfoSync().pixelRatio;canvas.width = this.data.canvasWidth * dpr;canvas.height = totalHeight * dpr;ctx.scale(dpr, dpr);// 设置背景颜色ctx.fillStyle = '#ffffff';ctx.fillRect(0, 0, canvas.width, canvas.height);// 绘制标题ctx.font = '16px sans-serif';ctx.fillStyle = '#333333';ctx.fillText("保险凭证", 10, titleHeight);// 绘制字段let keyIndex = 0;for (const key in this.data.fields) {if (key !== 'imgs') {ctx.font = '14px sans-serif';ctx.fillStyle = '#666666';ctx.fillText(this.data.fields[key], 10, (keyIndex * oneFieldHeight + titleHeight + paddingTop));keyIndex += 1;}}// 渲染图片images.forEach(image => {ctx.drawImage(image.imageSource, image.x, image.y, image.width, image.height);})// 渲染水印this.addEnhancedWatermark(ctx, canvas.width, canvas.height, this.data.watermarkText);},// 获取canvas对象initCanvas() {return new Promise((resolve, reject) => {const query = wx.createSelectorQuery();query.select('#myCanvas').fields({node: true,size: true}).exec((res) => {const canvas = res[0].node;const ctx = canvas.getContext('2d');if (res[0].node) {resolve({canvas,ctx})} else {reject()}})})},// 加载图片initImg(canvas, imgs) {if (!canvas) {wx.showToast({title: '未找到canvas',icon: 'none'})return;}const imgPromises = imgs.map((imgUrl) =>new Promise((resolve, reject) => {const image = canvas.createImage();image.onload = () => resolve(image);image.onerror = reject;image.src = imgUrl; // 设置图片路径}));return Promise.all(imgPromises);},addEnhancedWatermark(ctx, canvasWidth, canvasHeight, watermarkText) {ctx.save();// 水印样式设置ctx.font = '16px sans-serif';ctx.fillStyle = 'rgba(200, 200, 200, 0.5)'; // 更浅的透明度ctx.textAlign = 'center';ctx.textBaseline = 'middle';ctx.rotate(-20 * Math.PI / 180); // 保持倾斜// 水印间距参数const watermarkWidth = 280;const watermarkHeight = 60;const horizontalOffset = watermarkWidth * 0.5; // 水平交错偏移量(半格)// 计算需要绘制的水印行数和列数(扩大范围确保覆盖整个Canvas)const cols = Math.ceil(canvasWidth / watermarkWidth) + 2;const rows = Math.ceil(canvasHeight / watermarkHeight) + 2;// 绘制交错水印for (let i = -1; i < rows; i++) {for (let j = -1; j < cols; j++) {// 关键修改:奇数行水平偏移半格const xOffset = (i % 2 === 0) ? 0 : horizontalOffset;const x = j * watermarkWidth + xOffset;const y = i * watermarkHeight;ctx.fillText(watermarkText, x, y);// 可选:添加随机微调(更自然的效果)// const randomOffsetX = (Math.random() - 0.5) * 10;// const randomOffsetY = (Math.random() - 0.5) * 10;// ctx.fillText(watermarkText, x + randomOffsetX, y + randomOffsetY);}}ctx.restore();},// 保存图片到手机saveImage() {const query = wx.createSelectorQuery();query.select('#myCanvas').fields({node: true,size: true}).exec((res) => {const canvas = res[0].node;// 导出 Canvas 为临时文件wx.canvasToTempFilePath({canvas,success: (res) => {const tempFilePath = res.tempFilePath;// 保存图片到相册wx.saveImageToPhotosAlbum({filePath: tempFilePath,success: () => {wx.showToast({title: '保存成功',icon: 'success',});},fail: () => {wx.showToast({title: '保存失败',icon: 'none',});},});},fail: () => {wx.showToast({title: '生成图片失败',icon: 'none',});},});});},
});
.container {display: flex;flex-direction: column;align-items: center;justify-content: center;padding: 20px;
}button {margin-top: 20px;padding: 10px 20px;background-color: #007bff;color: white;border: none;border-radius: 5px;
}

相关文章:

  • 数据结构-选择排序(Python)
  • 配置RSUniVLM环境(自用)
  • 多模态大模型 Qwen2.5-VL 的学习之旅
  • 无标注文本的行业划分(行业分类)算法 —— 无监督或自监督学习
  • 以太网的mac帧格式
  • 优化uniappx页面性能,处理页面滑动卡顿问题
  • WebServiceg工具
  • 中心极限定理(CLT)习题集 · 题目篇
  • 深入浅出学会函数(上)
  • C++ 模板特化 (Template Specialization)
  • 如何规避矩阵运营中的限流风险及解决方案
  • springboot整合redis实现缓存
  • mapbox高阶,高程影像、行政区边界阴影效果实现
  • Windows 安装 JDK
  • Qt 处理 XML 数据
  • HarmonyOS:一多能力介绍:一次开发,多端部署
  • 声音分离人声和配乐-从头设计数字生命第5课, demucs——仙盟创梦IDE
  • 【Unity AR开发插件】一、高效热更新:Unity AR 插件结合 HybridCLR 与 ARFoundation 的开源仓库分享
  • 大模型技术全景解析:从基础架构到Prompt工程
  • Windows IIS 配置编辑器 应用程序初始化 <applicationInitialization>
  • 巴黎奥运后红土首秀落败,郑钦文止步马德里站次轮
  • 体育公益之约跨越山海,雪域高原果洛孕育足球梦
  • 魔都眼丨人形机器人“华山论剑”:拳击赛缺席,足球赛抢镜
  • 政治局会议:优化存量商品房收购政策,持续巩固房地产市场稳定态势
  • 台媒称美派遣前军官出任“汉光演习”资深观察员,国防部回应
  • 天问三号计划2028年前后发射实施,开放20千克质量资源