当前位置: 首页 > 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;
}
http://www.xdnf.cn/news/112339.html

相关文章:

  • Java的进阶学习
  • 鲲鹏麒麟搭建Docker仓库
  • 海量聊天消息处理:ShardingJDBC分库分表、ClickHouse冷热数据分离、ES复合查询方案、Flink实时计算与SpringCloud集成
  • C++ RPC以及cmake
  • VBA技术资料MF300:利用Mid进行文本查找
  • 专家系统的一般结构解析——基于《人工智能原理与方法》的深度拓展
  • JBoltAI 赋能金融文档:基于 RAG 的基金招募说明书视觉增强方案
  • 分布式微服务架构,数据库连接池设计策略
  • 【框架学习】Spring AI-功能学习与实战(一)
  • node.js 实战——(Http 知识点学习)
  • 使用PyTorch如何配置一个简单的GTP
  • Framework.jar里的类无法通过Class.forName反射某个类的问题排查
  • FPGA上实现YOLOv5的一般过程
  • 机器学习特征工程中的数值分箱技术:原理、方法与实例解析
  • 看一看 中间件Middleware
  • mapbox高阶,高程影像、行政区边界阴影效果实现
  • 开源项目实战学习之YOLO11:ultralytics-cfg-datasets-lvis.yaml文件(五)
  • 长城杯铁人三项初赛-REVERSE复现
  • Linux常见指令介绍下(入门级)
  • 手搓雷达图(MATLAB)
  • Java24新增特性
  • C语言数据结构之顺序表
  • 从代码学习深度学习 - 图像增广 PyTorch 版
  • 解决VSCode每次SSH连接服务器时,都需要下载vscode-server
  • Rust 2025:内存安全革命与异步编程新纪元
  • 大模型技术全景解析:从基础架构到Prompt工程
  • 无感字符编码原址转换术——系统内存(Mermaid文本图表版/DeepSeek)
  • 7.9 Python+Click实战:5步打造高效的GitHub监控CLI工具
  • #define STEUER_A_H {PWM_A_ON}
  • CSS3 基础(背景-文本效果)