小程序中使用CANVAS实现手写签名并写入模板图片中

news2024/11/15 11:00:06

实测,开发者工具中滚动条位置会影响书写,显示会有些问题,手机上测试正常

  index.js

const App = getApp();

Page({

    /**
     * 页面的初始数据
     */
    data: {
      curScrollTop : 0
    },

    /**
     * 生命周期函数--监听页面加载
     */
    onLoad(options) {

    },

    /**
     * 生命周期函数--监听页面初次渲染完成
     */
    onReady() {

    },

    /**
     * 生命周期函数--监听页面显示
     */
    onShow() {
      if(this.data.showBigResImgPop){
        this.setData({
          showBigResImgPop : false
        })
      }else{
        // this.init();
      }
    },
    
    
    
    /**
     * 写入签名生成图片
     */
    onPageScroll:function(e){ // 获取滚动条当前位置
      this.setData({
        curScrollTop : e.scrollTop
      })
      // console.log(this.data.curScrollTop)
    },
    writeSignatureDo(){
      this.setData({
        showWriteSignature : true,
      })
      // 写入签名生成图片
      this.writeSignature();
    },
    writeSignature(){
      if(!this.data._left && !this.data._top){
        const query1 = wx.createSelectorQuery();
        query1.select('#writeSignatureCanvasBox').boundingClientRect((res)=>{
          // console.log(res)
          this.setData({
            _left : res.left,
            _top : res.top,
          })
        }).exec()
      }
      
      const query = wx.createSelectorQuery();
      query.select('#writeSignatureCanvas')
      .fields({
        node: true,
        size: true
      })
      .exec((res) => {
        // console.log(res);
        // const canvas = res[0].node;
        // console.log('canvas初始宽高', canvas.width, canvas.height);
        this.setData({
          canvas : res[0].node,
          screenWidth : wx.getSystemInfoSync().screenWidth,
          screenWidthHalf : wx.getSystemInfoSync().screenWidth / 2,
        })
        // console.log('canvas初始宽高', this.data.canvas.width, this.data.canvas.height);
        
        // /* START 不设置 CANVAS宽高时,canvas.width=300,canvas.height=150,ctx.lineWidth可以对应设置 细 一些 */
        // // setNameMouseX : (e.touches[0].pageX - this.data._left + ((this.data.screenWidth - this.data.canvas.width) / 2)) * (this.data.canvas.width / this.data.screenWidth),
        // // setNameMouseY : (e.touches[0].pageY - this.data._top + ((this.data.screenWidthHalf - this.data.canvas.height) / 2)) * (this.data.canvas.height / this.data.screenWidthHalf),
        // /* END */
        
        // /* START 设置 CANVAS宽高时,触摸画线根据CANVAS宽高计算标准必须是 300 | 150,ctx.lineWidth可以对应设置 稍粗 一些 */
        // this.data.canvas.width = this.data.screenWidth;
        // this.data.canvas.height = this.data.screenWidthHalf;
        // // setNameMouseX : e.touches[0].pageX - this.data._left + ((this.data.screenWidth - 300) / 2),
        // // setNameMouseY : e.touches[0].pageY - this.data._top + ((this.data.screenWidthHalf - 150) / 2),
        // /* END */
        
        /* START 设置 CANVAS宽高 * dpr 时,触摸画线根据CANVAS宽高计算标准必须是 300 * dpr | 150 * dpr,ctx.lineWidth可以对应设置 粗 一些 */
        this.setData({
          dpr : wx.getSystemInfoSync().pixelRatio
        })
        this.data.canvas.width = this.data.screenWidth * this.data.dpr;
        this.data.canvas.height = this.data.screenWidthHalf * this.data.dpr;
        // setNameMouseX : e.touches[0].pageX * this.data.dpr - this.data._left * this.data.dpr + ((this.data.screenWidth * this.data.dpr - 300 * this.data.dpr) / 2),
        // setNameMouseY : e.touches[0].pageY * this.data.dpr - this.data._top * this.data.dpr + ((this.data.screenWidthHalf * this.data.dpr - 150 * this.data.dpr) / 2),
        // y轴滚动条影响手写内容处理
        //  - this.data.curScrollTop * this.data.dpr
        /* END */
        
        this.setData({
          writeSignatureCanvasW : this.data.screenWidth,
          writeSignatureCanvasH : this.data.screenWidthHalf,
        })
        
        // const ctx = canvas.getContext('2d');
        this.setData({
          ctx : this.data.canvas.getContext('2d')
        })
        
        this.data.ctx.strokeStyle = "#4FADF8";
        this.data.ctx.lineWidth = 14;
        this.data.ctx.clearRect(0, 0, this.data.canvas.width, this.data.canvas.height);
      })
    },
    writeSignatureCanvasTouchStartEvent(e){
      // console.log(e)
      this.setData({
        // setNameMouseX : (e.touches[0].pageX - this.data._left + ((this.data.screenWidth - this.data.canvas.width) / 2)) * (this.data.canvas.width / this.data.screenWidth),
        // setNameMouseY : (e.touches[0].pageY - this.data._top + ((this.data.screenWidthHalf - this.data.canvas.height) / 2)) * (this.data.canvas.height / this.data.screenWidthHalf),
        
        // setNameMouseX : e.touches[0].pageX - this.data._left + ((this.data.screenWidth - 300) / 2),
        // setNameMouseY : e.touches[0].pageY - this.data._top + ((this.data.screenWidthHalf - 150) / 2),
        
        setNameMouseX : e.touches[0].pageX * this.data.dpr - this.data._left * this.data.dpr + ((this.data.screenWidth * this.data.dpr - 300 * this.data.dpr) / 2),
        setNameMouseY : e.touches[0].pageY * this.data.dpr - this.data._top * this.data.dpr + ((this.data.screenWidthHalf * this.data.dpr - 150 * this.data.dpr) / 2) - this.data.curScrollTop * this.data.dpr,
      })
      
    	this.data.ctx.beginPath();
    	this.data.ctx.moveTo(this.data.setNameMouseX,this.data.setNameMouseY);
    },
    writeSignatureCanvasTouchEndEvent(e){
      // console.log(e)
      this.setData({
        setNameMouseX : null,
        setNameMouseY : null,
      })
      this.setData({
        showResButton : true
      })
    },
    writeSignatureCanvasTouchMoveEvent(e){
      this.setData({
        // setNameMouseX : (e.touches[0].pageX - this.data._left + ((this.data.screenWidth - this.data.canvas.width) / 2)) * (this.data.canvas.width / this.data.screenWidth),
        // setNameMouseY : (e.touches[0].pageY - this.data._top + ((this.data.screenWidthHalf - this.data.canvas.height) / 2)) * (this.data.canvas.height / this.data.screenWidthHalf),
        
        // setNameMouseX : e.touches[0].pageX - this.data._left + ((this.data.screenWidth - 300) / 2),
        // setNameMouseY : e.touches[0].pageY - this.data._top + ((this.data.screenWidthHalf - 150) / 2),
        
        setNameMouseX : e.touches[0].pageX * this.data.dpr - this.data._left * this.data.dpr + ((this.data.screenWidth * this.data.dpr - 300 * this.data.dpr) / 2),
        setNameMouseY : e.touches[0].pageY * this.data.dpr - this.data._top * this.data.dpr + ((this.data.screenWidthHalf * this.data.dpr - 150 * this.data.dpr) / 2) - this.data.curScrollTop * this.data.dpr,
      })
      
    	this.data.ctx.lineTo(this.data.setNameMouseX,this.data.setNameMouseY);
    	this.data.ctx.stroke();
    },
    // 重写
    reSetWriteSignatureCanvas(){
      this.setData({
        showResButton : false
      })
      this.data.ctx.clearRect(0, 0, this.data.canvas.width, this.data.canvas.height);
    },
    // 确定
    setWriteSignatureCanvas(){
      this.setData({
        showWriteSignature : false,
        writeSignatureImg : this.data.canvas.toDataURL('image/png'),
      })
      // 写入海报,生成最终图片
      this.makeResImg();
    },
    
    
    
    /**
     * 写入海报,生成最终图片
     */
    makeResImg() {
      const query = wx.createSelectorQuery();
      query.select('#makeResCanvas')
      .fields({
        node: true,
        size: true
      })
      .exec((res) => {
        // console.log(res);
        const canvas = res[0].node;
        // console.log('canvas初始宽高', canvas.width, canvas.height);
        const ctx = canvas.getContext('2d');
        
        // const dpr = wx.getSystemInfoSync().pixelRatio;
        // console.log(dpr);
        // canvas.width = res[0].width * dpr;
        // canvas.height = res[0].height * dpr;
        // console.log(canvas.width , canvas.height)
        // ctx.scale(dpr, dpr);
        
        // 写入 生成图片 背景图片
        const posterBgImg = canvas.createImage();
        posterBgImg.src = '../../images/form-img.png';
        posterBgImg.onload = () => {
          // console.log('背景图实际宽高', posterBgImg.width, posterBgImg.height);
          canvas.width = posterBgImg.width;
          canvas.height = posterBgImg.height;
          this.setData({
            // makeResCanvasW: posterBgImg.width,
            // makeResCanvasH: posterBgImg.height,
            // makeResCanvasW:750,
            // makeResCanvasH:750 / posterBgImg.width * posterBgImg.height,
            makeResCanvasW:0,
            makeResCanvasH:0,
          })
          // console.log('canvas宽高设置与背景图一致', canvas.width, canvas.height);
          ctx.drawImage(posterBgImg, 0, 0, posterBgImg.width, posterBgImg.height);
          // 写入勾选项图片
          const checkImg = canvas.createImage();
          checkImg.src = '../../images/check.png';
          checkImg.onload = () => {
            // 画入签名图片
            const reSetWriteSignatureImg = canvas.createImage();
            reSetWriteSignatureImg.src = this.data.writeSignatureImg;
            reSetWriteSignatureImg.onload = () => {
              ctx.drawImage(reSetWriteSignatureImg, 1892, 2848, 350, 175);
              // 设置勾选项
              ctx.drawImage(checkImg, 558, 1895, 32, 24);
              ctx.drawImage(checkImg, 1443, 1895, 32, 24);
              ctx.drawImage(checkImg, 1443, 2047, 32, 24);
              // 写入文本
              ctx.fillStyle = '#4FADF8';
              ctx.textAlign = 'left';
              ctx.textBaseline = 'top';
              ctx.font = '46px "PingFangSC-Regular","STHeitiSC-Light","微软雅黑","Microsoft YaHei","sans-serif"';
              ctx.fillText('客户姓名',545,384);
              // 写入多行文本
              // this.writeTextOnCanvas(ctx, 42, 40, '写入多行文本写入多行文本写入多行文本写入多行文本写入多行文本写入多行文本' ,562, 1350);
              let add = '北京市北京市东城区东华门街道';
              add = '北京市北京市东城区东华门街道多四个字';
              if(add.length > 14){
                this.writeTextOnCanvas(ctx, 52, 30, add ,1716, 869);
              }else{
                ctx.fillText(add,1716,895);
              }
              ctx.fillText('企业详细地址',545,1064);
              let date = new Date()
              ctx.fillText(date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate(),1892,3035);
              
              // 生成最终图片
              this.setData({
                posterUrl: canvas.toDataURL('image/png'),
              })
            }
          }
        }
      })
    },
    // 查看大图
    showBigResImg(){
      this.setData({
        showBigResImgPop : true
      })
      wx.previewImage({
        current: this.data.posterUrl,
        urls: [this.data.posterUrl]
      });
    },
    // 写入多行文本
    //ctx_2d		getContext("2d") 对象
    //lineheight	段落文本行高
    //bytelength	设置单字节文字一行内的数量
    //text			写入画面的段落文本
    //startleft		开始绘制文本的 x 坐标位置(相对于画布)
    //starttop		开始绘制文本的 y 坐标位置(相对于画布)
    writeTextOnCanvas(ctx_2d, lineheight, bytelength, text ,startleft, starttop){
    	function getTrueLength(str){//获取字符串的真实长度(字节长度)
    		var len = str.length, truelen = 0;
    		for(var x = 0; x < len; x++){
    			if(str.charCodeAt(x) > 128){
    				truelen += 2;
    			}else{
    				truelen += 1;
    			}
    		}
    		return truelen;
    	}
    	function cutString(str, leng){//按字节长度截取字符串,返回substr截取位置
    		var len = str.length, tlen = len, nlen = 0;
    		for(var x = 0; x < len; x++){
    			if(str.charCodeAt(x) > 128){
    				if(nlen + 2 < leng){
    					nlen += 2;
    				}else{
    					tlen = x;
    					break;
    				}
    			}else{
    				if(nlen + 1 < leng){
    					nlen += 1;
    				}else{
    					tlen = x;
    					break;
    				}
    			}
    		}
    		return tlen;
    	}
    	for(var i = 1; getTrueLength(text) > 0; i++){
    		var tl = cutString(text, bytelength);
    		ctx_2d.fillText(text.substr(0, tl).replace(/^\s+|\s+$/, ""), startleft, (i-1) * lineheight + starttop);
    		text = text.substr(tl);
    	}
    },
    
    

    /**
     * 生命周期函数--监听页面隐藏
     */
    onHide() {

    },

    /**
     * 生命周期函数--监听页面卸载
     */
    onUnload() {

    },

    /**
     * 页面相关事件处理函数--监听用户下拉动作
     */
    onPullDownRefresh() {

    },

    /**
     * 页面上拉触底事件的处理函数
     */
    onReachBottom() {

    },

    /**
     * 用户点击右上角分享
     */
    onShareAppMessage() {

    }
})

 index.wxml

<button bindtap="writeSignatureDo" style="background-color: #0FC393;">我要签名</button>
<view wx:for="{{5}}">
  <view>1111</view>
  <view>2222</view>
  <view>3333</view>
  <view>5555</view>
  <view>6666</view>
  <view>7777</view>
  <view>8888</view>
  <view>9999</view>
</view>
<button bindtap="writeSignatureDo" style="background-color: #0FC393;">我要签名</button>
<image wx:if="{{writeSignatureImg}}" src="{{writeSignatureImg}}" mode="widthFix"></image>

<!--  -->

<view>
 <canvas type="2d" id="makeResCanvas"  style="width: {{makeResCanvasW}}rpx;height: {{makeResCanvasH}}rpx;"></canvas>
 <image bindtap="showBigResImg" wx:if="{{posterUrl}}" src="{{posterUrl}}" mode="widthFix" style="width: 100%;"></image>
</view>
 
<view wx:if="{{showWriteSignature}}" class="dis-flex flex-dir-column flex-x-center flex-y-center" style="width: 100%; height: 100%; background-color: rgba(0,0,0,.5); position: fixed; left: 0; top: 0;">
  <view style="text-align: center; color: #fff; margin-bottom: 30rpx;">请写入签名</view>
  <view id="writeSignatureCanvasBox">
    <canvas type="2d" id="writeSignatureCanvas" catch:touchstart="writeSignatureCanvasTouchStartEvent" catch:touchend="writeSignatureCanvasTouchEndEvent" catch:touchmove="writeSignatureCanvasTouchMoveEvent" style="width: {{writeSignatureCanvasW}}px;height: {{writeSignatureCanvasH}}px; background-color: #fff;"></canvas>
  </view>
  <view class="dis-flex flex-x-center flex-y-center" style="width: 50%; margin-top: 30rpx;">
    <button catchtap="reSetWriteSignatureCanvas" type="default">重写</button>
    <button catchtap="setWriteSignatureCanvas" wx:if="{{showResButton}}" type="default">确定</button>
  </view>
</view>

素材图片:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/488765.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【软考高项笔记】第2章 信息技术发展2.1 信息技术及其发展

2.1 信息技术及其发展 获取信息、处理信息、传输信息、使用信息硬技术&#xff08;物化技术&#xff09;传感器&#xff0c;服务器&#xff0c;手机&#xff0c;软技术&#xff08;非物化&#xff09;数据分析&#xff0c;规划决策2.1.1 计算机软硬件 硬件 物理装置 &#xff…

知识管理在企业中的重要性

随着经济全球化和信息化的快速发展&#xff0c;企业面临着越来越多的竞争和挑战。如何把握市场动态、满足客户需求、提高产品质量和效率等&#xff0c;成为了企业发展中亟待解决的问题。而知识管理作为一种新兴的管理方式&#xff0c;逐渐引起了企业们的重视。本文将从以下几个…

go-zero

目录 引入开发派系标准库/自研派系——不要让框架束缚开发web框架派系——gingrpc大一统框架 go-zerogo-zero快速实现一个微服务user serviceorder api server启动 goctl安装生成的api网关目录生成的pb目录api语法syntaximport语法块infotypeservice注释 命令大全 引入 该图片来…

图论专题(各类算法和蓝桥杯真题例题)

1.图论入门 1.1存边方式 1.1.1 数组存边 1.1.2 临接矩阵存边 1.1.3 临接表存边 1.2 图的遍历和连通性 通过DFS和BFS遍历每一个图 对于非连通图&#xff0c;循环对每一个点dfs操作 也可以通过并查集来判断连通性 1.2.1全球变暖例题 import sys sys.setrecursionlimit(6000…

【目标检测】Haar-like特征检测简介

文章目录 一、背景模板匹配&#xff08;template matching&#xff09;关键点检测角点检测 二、harris特征提取原理Harris Detector 的具体流程&#xff1a;harris特征的可复用性旋转尺度 scale亮度 illuminationview point 三、Viola Jones检测原理Harr-like特征提取积分图训练…

QT教程demo之串口助手代码设计实现

关注WeChat Official Account 南山府嵌入式获取更多精彩 我创建了一个群关注V号后加入。因为这里不允许添加二维码 代码&#xff1a;QT_Pr 1-QT开发串口助手需要的基本文件 在QT6开发串口助手时&#xff0c;通常需要以下头文件&#xff1a; #include <QSerialPort> #i…

TCP协议策略

TCP可靠性 基于序号的确认应答(ACK)机制 TCP保证可靠性最核心的机制就是基于序号的确认应答机制。 TCP并不是百分之百可靠的&#xff0c;但是只要一条消息有应答&#xff0c;那么我们就可以确定该消息100%被对方收到了&#xff0c;这就是确认应答的意义。 可靠性不仅仅是保…

MOSS模型量化版部署过程

文章目录 项目背景配置环境与准备部署推理命令行部署报错1报错2&#xff1a; 网页版部署 项目背景 2023年4月21日&#xff0c;复旦大学自然语言处理实验室正式开放MOSS模型&#xff0c;是国内首个插件增强的开源对话大语言模型。MOSS 相关代码、数据、模型参数已在 GitHub 和 …

【键入网址到网页显示】

HTTP 对 URL 进行解析之后&#xff0c;浏览器确定了 Web 服务器和文件名&#xff0c;接下来就是根据这些信息来生成 HTTP 请求消息了。 http://www.server.com/dir1/file1.html http:访问数据的协议 www.server.com:服务器 dir1:目录名 file1.html:文件名生产 HTTP 请求信息…

SpringCloud-10_Alibaba Nacos

SpringCloud系列 SpringCloud-9、SleuthZipkin SpringCloud-8、Gateway网关服务 SpringCloud-7_OpenFeign服务调用 SpringCloud-6_Ribbon负载均衡 SpringCloud-5_模块集群化 文章目录 SpringCloud系列Nacos基础Nacos是什么&#xff1f;Nacos下载&运行 创建Nacos服务提供者…

JavaScript:哈希表

文章目录 哈希表242. 有效的字母异位词思路补充&#xff1a;JavaScript String charCodeAt() 方法代码详细分析 349. 两个数组的交集代码分析补充&#xff1a;JavaScript Set 对象思考一下哈希是什么&#xff1f;什么时候使用&#xff1f;补充&#xff1a;js 数组 map() 基本用…

github使用workflow工作流git push后自动打包部署github pages

workflows介绍 根目录新建.github/workflows/docs.yml .github/workflows/ 目录是用于存放 GitHub Actions 工作流程文件的目录&#xff0c;该目录的文件名必须以 .yml 或 .yaml 为后缀名&#xff0c;否则 GitHub 将无法识别该文件为工作流程文件。这些工作流程文件可用于自动…

速锐得基于能源油气生产智能监控的物联网应用

自2016年速锐得基于商用车国六环保排放监控管理开发以来&#xff0c;我们同时也接触了很多涉及商用车领域的深入项目&#xff0c;包括了大庆油田、山东能源、兖矿集团、陕北矿业等多家能源巨头在数字化、科技、无线远程控制、监控管理、生产效能等方面的智能化方向的趋势。 就目…

MySQL 字段为 NULL 的5大坑,大部分人踩过

数据库字段允许空值(null)的问题&#xff0c;小伙伴你遇到过吗&#xff1f; 在验证问题之前&#xff0c;我们先建一张测试表及测试数据。 构建的测试数据&#xff0c;如下图所示&#xff1a; 有了上面的表及数据之后&#xff0c;我们就来看当列中存在 NULL 值时&#xff0c;究…

基于RGB-D的6D目标检测算法

基于RGB-D的6D目标检测算法 本文参考了ITAIC的文章 A Review of 6D Object Pose Estimation 概览 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eQX8ke6j-1683188966051)(https://mezereon-upic.oss-cn-shanghai.aliyuncs.com/uPic/image-20230420…

如何理解信息化、数字化和智能化的概念?一文给你解惑!

如何理解信息化、数字化和智能化的概念&#xff1f; 前两年流行“信息化”&#xff0c;网上铺天盖地都是关于“信息化”的文章&#xff0c;这两年开始流行起“数字化”&#xff0c;于是铺天盖地都是“数字化”的文章&#xff0c;最近又开始大谈“智能化”...... 但点开那些文…

常见的5种项目管理问题类型

项目管理的5大影响素&#xff1a;时间、范围、成本、风险、质量。项目经理需要对这些因素进行均衡考量&#xff0c;并根据需要略有侧重&#xff0c;进行整体把握&#xff0c;即我们常说的均衡型管理风格。而忽略任何一因素&#xff0c;都会对项目产生极大影响。 常见的项目管理…

医院导诊图怎么做?目前比较好用的医院导航地图是哪一款?

现在很多的大医院&#xff0c;不只是越建越高&#xff0c;面积也越来越大&#xff0c;同时医院内部按照门诊、住院、放射等不同功能划分的区域也是越来越多&#xff0c;走进这些“超级医院”就像走进了迷宫一样&#xff0c;如何促使病患走进医院后&#xff0c;能迅速找到要去的…

19C RAC主库 to RAC备库搭建adg报错ORA-16047(修改19C RAC DB_UNIQUE_NAME )

文章目录 前言一、问题描述二、修改DB_UNIQUE_NAME1.查看集群配置2.将hip40实例从集群中移除3.修改db_unique_name4.将hip40dg实例添加到集群资源中5.重新启动实例 三、19C RAC 密码文件替换 前言 主库环境是19C RAC备库环境也是19C RAC&#xff0c;主库到备库做adg&#xff0…

移动端动态开发能力的由来和流派

移动端动态化的由来 “动态化”并不是最近几年才产生的名词&#xff0c;而是从从互联网诞生的初期&#xff0c;这个词就已经出现了。大家所认知的早期互联网&#xff0c;其实就是各种各类的“动态网站”&#xff0c;内容数据和页面外观都不是固定的&#xff0c;都是随着服务器…