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

实测,开发者工具中滚动条位置会影响书写,显示会有些问题,并且会卡顿,安卓、苹果手机上测试正常

  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){
        console.log(1111)
        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);
        })
      }else{
        console.log(2222)
        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.reSetWriteSignatureCanvas();
      // 写入海报,生成最终图片
      this.makeResImg();
    },
    
    
    
    /**
     * 写入海报,生成最终图片
     */
    makeResImg() {
      if(this.data.posterCanvas){
        console.log(4444)
        this.makeResImgAfter();
      }else{
        console.log(3333)
        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');
          
          this.setData({
            posterCanvas : res[0].node,
          })
          
          this.setData({
            posterCtx : this.data.posterCanvas.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);
          
          this.makeResImgAfter();
        })
      }
    },
    makeResImgAfter(){
      this.data.posterCtx.clearRect(0, 0, this.data.posterCanvas.width, this.data.posterCanvas.height);
      // 写入 生成图片 背景图片
      const posterBgImg = this.data.posterCanvas.createImage();
      posterBgImg.src = '../../images/form-img.png';
      posterBgImg.onload = () => {
        // console.log('背景图实际宽高', posterBgImg.width, posterBgImg.height);
        this.data.posterCanvas.width = posterBgImg.width;
        this.data.posterCanvas.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('this.data.posterCanvas宽高设置与背景图一致', this.data.posterCanvas.width, this.data.posterCanvas.height);
        this.data.posterCtx.drawImage(posterBgImg, 0, 0, posterBgImg.width, posterBgImg.height);
        // 写入勾选项图片
        const checkImg = this.data.posterCanvas.createImage();
        checkImg.src = '../../images/check.png';
        checkImg.onload = () => {
          // 画入签名图片
          const reSetWriteSignatureImg = this.data.posterCanvas.createImage();
          reSetWriteSignatureImg.src = this.data.writeSignatureImg;
          reSetWriteSignatureImg.onload = () => {
            this.data.posterCtx.drawImage(reSetWriteSignatureImg, 1892, 2848, 350, 175);
            // 设置勾选项
            this.data.posterCtx.drawImage(checkImg, 558, 1895, 32, 24);
            this.data.posterCtx.drawImage(checkImg, 1443, 1895, 32, 24);
            this.data.posterCtx.drawImage(checkImg, 1443, 2047, 32, 24);
            // 写入文本
            this.data.posterCtx.fillStyle = '#4FADF8';
            this.data.posterCtx.textAlign = 'left';
            this.data.posterCtx.textBaseline = 'top';
            this.data.posterCtx.font = '46px "PingFangSC-Regular","STHeitiSC-Light","微软雅黑","Microsoft YaHei","sans-serif"';
            this.data.posterCtx.fillText('客户姓名',545,384);
            // 写入多行文本
            // this.writeTextOnCanvas(this.data.posterCtx, 42, 40, '写入多行文本写入多行文本写入多行文本写入多行文本写入多行文本写入多行文本' ,562, 1350);
            let add = '北京市北京市东城区东华门街道';
            add = '北京市北京市东城区东华门街道多四个字';
            if(add.length > 14){
              this.writeTextOnCanvas(this.data.posterCtx, 52, 30, add ,1716, 869);
            }else{
              this.data.posterCtx.fillText(add,1716,895);
            }
            this.data.posterCtx.fillText('企业详细地址',545,1064);
            let date = new Date()
            this.data.posterCtx.fillText(date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate(),1892,3035);
            
            // 生成最终图片
            this.setData({
              posterUrl: this.data.posterCanvas.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 class="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; {{showWriteSignature ? 'display: flex !important;' : 'display: none !important;'}}">
  <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%; height: 80rpx; margin-top: 30rpx;">
    <button catchtap="reSetWriteSignatureCanvas" wx:if="{{showResButton}}" type="default" style="height: 80rpx;line-height: 80rpx;">重写</button>
    <button catchtap="setWriteSignatureCanvas" wx:if="{{showResButton}}" type="default" style="height: 80rpx;line-height: 80rpx;">确定</button>
  </view>
</view>

素材图片:

 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
可以使用新版的canvas实现手写签名功能。下面是一个简单的示例代码: 1. 在wxml文件添加canvas标签: ```html <canvas canvas-id="myCanvas" style="width: 100%; height: 100%;"></canvas> ``` 2. 在js文件获取canvas对象并绑定事件: ```javascript const ctx = wx.createCanvasContext('myCanvas'); // 定义画笔的颜色、粗细等属性 ctx.setStrokeStyle('black') ctx.setLineWidth(3) ctx.setLineCap('round') ctx.setLineJoin('round') let isDrawing = false; let lastX = 0; let lastY = 0; // 触摸事件 wx.createSelectorQuery().select('#myCanvas').fields({ node: true, size: true }).exec((res) => { const canvas = res[0].node; const dpr = wx.getSystemInfoSync().pixelRatio; canvas.width = res[0].width * dpr; canvas.height = res[0].height * dpr; ctx.scale(dpr, dpr); canvas.addEventListener('touchstart', (e) => { isDrawing = true; lastX = e.touches[0].x; lastY = e.touches[0].y; }); canvas.addEventListener('touchmove', (e) => { if (!isDrawing) return; ctx.beginPath(); ctx.moveTo(lastX, lastY); ctx.lineTo(e.touches[0].x, e.touches[0].y); ctx.stroke(); lastX = e.touches[0].x; lastY = e.touches[0].y; }); canvas.addEventListener('touchend', (e) => { isDrawing = false; }); }); ``` 3. 在提交时获取canvas数据: ```javascript wx.canvasToTempFilePath({ canvasId: 'myCanvas', success: (res) => { // res.tempFilePath 是生成的图片路径 } }); ``` 这样就可以实现一个简单的手写签名功能了。当然,还可以根据需要进行更加复杂的实现
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值