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


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>
 
素材图片:

 



















