亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

微信小程序?qū)崿F(xiàn)canvas電子簽名功能

 更新時(shí)間:2024年10月23日 09:10:37   作者:阿兵吶  
這篇文章主要為大家詳細(xì)介紹了如何通過微信小程序?qū)崿F(xiàn)canvas電子簽名功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

一、先看效果

小程序canvas電子簽名

二、文檔

微信小程序canvas 組件文檔

微信小程序canvas API文檔

三、分析

1、初始話Canvas容器

2、Canvas觸摸事件,bindtouchstart(手指觸摸動作開始)、bindtouchmove(手指觸摸后移動)、bindtouchend(手指觸摸動作結(jié)束)、bindtouchcancel(手指觸摸動作被打斷,如來電提醒,彈窗)

3、記錄每次從開始到結(jié)束的路徑段

4、清除、撤銷

四、代碼分析

1、頁面的布局、Canvas容器的初始化

先將屏幕橫過來,index.json配置文件,“pageOrientation”: “landscape”

wx.getSystemInfoSync() 獲取可使用窗口的寬高,賦值給Canvas畫布(注意若存在按鈕區(qū)域、屏幕安全區(qū)之類的,需要減去)

 // 獲取可使用窗口的寬高,賦值給Canvas(寬高要減去上下左右padding的20,以及高度要減去footer區(qū)域)
 wx.createSelectorQuery()
   .select('.footer') // canvas獲取節(jié)點(diǎn)
   .fields({node: true, size: true}) // 獲取節(jié)點(diǎn)的相關(guān)信息,node:是否返回節(jié)點(diǎn)對應(yīng)的 Node 實(shí)例,size:是否返回節(jié)點(diǎn)尺寸
   .exec((res) => {
     // 獲取手機(jī)左側(cè)安全區(qū)域(劉海)
     const deviceInFo = wx.getSystemInfoSync()
     const canvasWidth = deviceInFo.windowWidth - 20 - deviceInFo?.safeArea?.left || 0
     const canvasHeight = deviceInFo.windowHeight - res[0].height - 20
     console.log('canvasWidth', canvasWidth);
     console.log('canvasHeight', canvasHeight);
     this.setData({
       deviceInFo,
       canvasWidth,
       canvasHeight
     })
     this.initCanvas('init')
   })

通過wx.createSelectorQuery()獲取到canvas節(jié)點(diǎn),隨即可獲取到canvas的上下文實(shí)例

  // 初始話Canvas畫布
  initCanvas() {
    let ctx = null
    let canvas = null
    // 獲取Canvas畫布以及渲染上下文
    wx.createSelectorQuery()
      .select('#myCanvas') // canvas獲取節(jié)點(diǎn)
      .fields({node: true, size: true}) // 獲取節(jié)點(diǎn)的相關(guān)信息,node:是否返回節(jié)點(diǎn)對應(yīng)的 Node 實(shí)例,size:是否返回節(jié)點(diǎn)尺寸
      .exec((res) => { // 執(zhí)行所有的請求。請求結(jié)果按請求次序構(gòu)成數(shù)組
        // Canvas 對象實(shí)例
        canvas = res[0].node
        // Canvas 對象上下文實(shí)例(動畫動作繪圖等都是在他的身上完成)
        ctx = canvas.getContext('2d')
        // Canvas 畫布的實(shí)際繪制寬高
        const width = res[0].width;
        const height = res[0].height;
        // 獲取設(shè)備像素比
        const dpr = wx.getWindowInfo().pixelRatio;
        // 初始化畫布大小
        canvas.width = width * dpr;
        canvas.height = height * dpr;
        // 畫筆的顏色
        ctx.fillStyle = 'rgb(200, 0, 0)';
        // 指定了畫筆(繪制線條)操作的線條寬度
        ctx.lineWidth = 5
        // 縮小/放大圖像
        ctx.scale(dpr, dpr)
        this.setData({canvas, ctx});
      })
  },

2、線條的繪制

通過canva組件的觸摸事件bindtouchstart、bindtouchmove、bindtouchend、bindtouchcancel結(jié)合canvas的路徑繪制的方法moveTo(x,y)、lineTo(x,y)、stroke()來實(shí)現(xiàn)一段線條的繪制

bindtouchstart手指觸摸動作開始,結(jié)合moveTo(x,y) 用來設(shè)置繪圖起始坐標(biāo)的方法確定線段的開始坐標(biāo)

  // 手指觸摸動作開始
  bindtouchstart(event) {
      let {type, changedTouches} = event;
    let {x, y} = changedTouches[0];
    ctx.moveTo(x, y); // 設(shè)置繪圖起始坐標(biāo)。
  },

bindtouchend手指觸摸動作結(jié)束,結(jié)合lineTo(x,y) 來繪制一條直線,最后stroke()渲染路徑

  // 手指觸摸動作結(jié)束
  bindtouchend(event) {
      let {type, changedTouches} = event;
    let {x, y} = changedTouches[0];
     ctx.lineTo(x, y);
     // 繪制
     ctx.stroke();
  },

但這只是一條直線段,并未實(shí)現(xiàn)簽名所需的曲線(曲線實(shí)質(zhì)上也是由無數(shù)個(gè)非常短小的直線段構(gòu)成)

bindtouchmove事件會在手指觸摸后移動時(shí),實(shí)時(shí)返回當(dāng)前狀態(tài)

那么可否通過bindtouchmove 結(jié)合 moveTo ==> lineTo ==> stroke ==> moveTo ==> … 以上一次的結(jié)束為下一次的開始這樣的方式來實(shí)時(shí)渲染直線段合并為一個(gè)近似的曲線

  // 手指觸摸后移動    
  bindtouchmove(event) {
      let {type, changedTouches} = event;
    let {x, y} = changedTouches[0];
    // 上一段終點(diǎn)
    ctx.lineTo(x, y) // 從最后一點(diǎn)到點(diǎn)(x,y)繪制一條直線。
    // 繪制
    ctx.stroke();
    // 下一段起點(diǎn)
    ctx.moveTo(x, y) // 設(shè)置繪圖起始坐標(biāo)。
  },

歸納封裝

  // 手指觸摸動作開始
  bindtouchstart(event) {
    this.addPathDrop(event)
  },
  // 手指觸摸后移動	
  bindtouchmove(event) {
    this.addPathDrop(event)
  },
  // 手指觸摸動作結(jié)束
  bindtouchend(event) {
    this.addPathDrop(event)
  },
  // 手指觸摸動作被打斷,如來電提醒,彈窗
  bindtouchcancel(event) {
    this.addPathDrop(event)
  },
  // 添加路徑點(diǎn)
  addPathDrop(event) {
    let {ctx, historyImag, canvas} = this.data
    let {type, changedTouches} = event
    let {x, y} = changedTouches[0]
    if(type === 'touchstart') { // 每次開始都是一次新動作
      // 最開始點(diǎn)
      ctx.moveTo(x, y) // 設(shè)置繪圖起始坐標(biāo)。
    } else {
      // 上一段終點(diǎn)
      ctx.lineTo(x, y) // 從最后一點(diǎn)到點(diǎn)(x,y)繪制一條直線。
      // 繪制
      ctx.stroke();
      // 下一段起點(diǎn)
      ctx.moveTo(x, y) // 設(shè)置繪圖起始坐標(biāo)。
    }
  },

3、上一步、重繪、提交

主體思路為每一次繪制完成后都通過wx.canvasToTempFilePath生成圖片,并記錄下來,通過canvas的drawImage方法將圖片繪制到 canvas 上

五、完整代碼

1、inde.json

{
  "navigationBarTitleText": "電子簽名",
  "backgroundTextStyle": "dark",
  "pageOrientation": "landscape",
  "disableScroll": true,
  "usingComponents": {
    "van-button": "@vant/weapp/button/index",
    "van-toast": "@vant/weapp/toast/index"
  }
}

2、index.wxml

<!-- index.wxml -->
<view>
  <view class="content" style="padding-left: {{deviceInFo.safeArea.left || 10}}px">
    <view class="canvas_box">
      <!-- 定位到canvas畫布的下方作為背景 -->
      <view class="canvas_tips">
        簽字區(qū)
      </view>
      <!-- canvas畫布 -->
      <canvas class="canvas_content" type="2d" style='width:{{canvasWidth}}px; height:{{canvasHeight}}px' id="myCanvas" bindtouchstart="bindtouchstart" bindtouchmove="bindtouchmove" bindtouchend="bindtouchend" bindtouchcancel="bindtouchcancel"></canvas>
    </view>
  </view>
  <!-- footer -->
  <view class="footer" style="padding-left: {{deviceInFo.safeArea.left}}px">
    <van-button plain class="item" block icon="replay" bind:click="overwrite" type="warning">清除重寫</van-button>
    <van-button plain class="item" block icon="revoke" bind:click="prev" type="danger">撤銷</van-button>
    <van-button class="item" block icon="passed" bind:click="confirm" type="info">提交</van-button>
  </view>
</view>
<!-- 提示框組件 -->
<van-toast id="van-toast" />

3、index.less

.content {
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  padding: 10px;
  .canvas_box {
    width: 100%;
    height: 100%;
    background-color: #E8E9EC;
    position: relative;
    // 定位到canvas畫布的下方作為背景
    .canvas_tips {
      position: absolute;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      font-size: 80px;
      color: #E2E2E2;
      font-weight: bold;
      display: flex;
      align-items: center;
      justify-content: center;
    }
  
    // .canvas_content {
    //   width: 100%;
    //   height: 100%;
    // }
  }
}
// 底部按鈕
.footer {
  box-sizing: border-box;
  padding: 20rpx 0;
  z-index: 2;
  background-color: #ffffff;
  text-align: center;
  position: fixed;
  width: 100%;
  box-shadow: 0 0 15rpx rgba(0, 0, 0, 0.1);
  left: 0;
  bottom: 0;
  display: flex;

  .item {
    flex: 1;
    margin: 0 10rpx;
  }

  .scan {
    width: 80rpx;
    margin: 0 10rpx;
  }

  .moreBtn {
    width: 150rpx
  }
}

4、index.js

// index.js
// 獲取應(yīng)用實(shí)例
// import request from "../../request/index";
import Toast from '@vant/weapp/toast/toast';

const app = getApp()
Page({
  data: {
    // expertId: '', // 專家id
    deviceInFo: {}, // 設(shè)備信息
    canvasWidth: '', // 畫布寬
    canvasHeight: '', // 畫布高
    canvas: null, // Canvas 對象實(shí)例
    ctx: null, // Canvas 對象上下文實(shí)例
    historyImag: [], // 歷史記錄,每一筆動作完成后的圖片數(shù)據(jù),用于每一次回退上一步是當(dāng)作圖片繪制到畫布上
    fileList: [], // 簽名后生成的附件
    initialCanvasImg: '', // 初始畫布圖,解決非ios設(shè)備重設(shè)置寬高不能清空畫布的問題
  },
  onReady() {
    // 獲取可使用窗口的寬高,賦值給Canvas(寬高要減去上下左右padding的20,以及高度要減去footer區(qū)域)
    wx.createSelectorQuery()
      .select('.footer') // canvas獲取節(jié)點(diǎn)
      .fields({ node: true, size: true }) // 獲取節(jié)點(diǎn)的相關(guān)信息,node:是否返回節(jié)點(diǎn)對應(yīng)的 Node 實(shí)例,size:是否返回節(jié)點(diǎn)尺寸
      .exec((res) => {
        console.log('res', res);
        // 獲取手機(jī)左側(cè)安全區(qū)域(劉海)
        const deviceInFo = wx.getSystemInfoSync()
        const canvasWidth = deviceInFo.windowWidth - 20 - deviceInFo?.safeArea?.left || 0
        const canvasHeight = deviceInFo.windowHeight - res[0].height - 20
        this.setData({
          deviceInFo,
          canvasWidth,
          canvasHeight
        })
        this.initCanvas('init')
      })
  },
  onLoad(option) {
    wx.setNavigationBarTitle({
      title: '電子簽名'
    })
    // const {expertId} = option
    // this.setData({expertId})
  },
  // 初始話Canvas畫布
  initCanvas(type) {
    let ctx = null
    let canvas = null
    let {historyImag, canvasWidth, canvasHeight, deviceInFo, initialCanvasImg} = this.data
    // 獲取Canvas畫布以及渲染上下文
    wx.createSelectorQuery()
      .select('#myCanvas') // canvas獲取節(jié)點(diǎn)
      .fields({ node: true, size: true }) // 獲取節(jié)點(diǎn)的相關(guān)信息,node:是否返回節(jié)點(diǎn)對應(yīng)的 Node 實(shí)例,size:是否返回節(jié)點(diǎn)尺寸
      .exec((res) => { // 執(zhí)行所有的請求。請求結(jié)果按請求次序構(gòu)成數(shù)組
        // Canvas 對象實(shí)例
        canvas = res[0].node
        // Canvas 對象上下文實(shí)例(動畫動作繪圖等都是在他的身上完成)
        ctx = canvas.getContext('2d')
        // Canvas 畫布的實(shí)際繪制寬高
        const width = res[0].width
        const height = res[0].height
        // 獲取設(shè)備像素比
        const dpr = wx.getWindowInfo().pixelRatio
        // 初始化畫布大小
        canvas.width = width * dpr
        canvas.height = height * dpr
        // 畫筆的顏色
        ctx.fillStyle = 'rgb(200, 0, 0)';
        // 指定了畫筆(繪制線條)操作的線條寬度
        ctx.lineWidth = 5
        // 如果存在歷史記錄,則將歷史記錄最新的一張圖片拿出來進(jìn)行繪制。非ios時(shí)直接加載一張初始的空白圖片
        if(historyImag.length !== 0 || (deviceInFo.platform !== 'ios' && type !== 'init')) {
          // 圖片對象
          const image = canvas.createImage()
          // 圖片加載完成回調(diào)
          image.onload = () => {
            // 將圖片繪制到 canvas 上
            ctx.drawImage(image, 0, 0, canvasWidth, canvasHeight)
          }
          // 設(shè)置圖片src
          image.src = historyImag[historyImag.length - 1] || initialCanvasImg;
        }
        // 縮小/放大圖像
        ctx.scale(dpr, dpr)
        this.setData({canvas, ctx})
        // 保存一張初始空白圖片
        if(type === 'init') {
          wx.canvasToTempFilePath({
            canvas,
            png: 'png',
            success: res => {
              // 生成的圖片臨時(shí)文件路徑
              const tempFilePath = res.tempFilePath
              this.setData({initialCanvasImg: tempFilePath})
            },
          })
        }
      })
  },
  // 手指觸摸動作開始
  bindtouchstart(event) {
    this.addPathDrop(event)
  },
  // 手指觸摸后移動	
  bindtouchmove(event) {
    this.addPathDrop(event)
  },
  // 手指觸摸動作結(jié)束
  bindtouchend(event) {
    this.addPathDrop(event)
  },
  // 手指觸摸動作被打斷,如來電提醒,彈窗
  bindtouchcancel(event) {
    this.addPathDrop(event)
  },
  // 添加路徑點(diǎn)
  addPathDrop(event) {
    let {ctx, historyImag, canvas} = this.data
    let {type, changedTouches} = event
    let {x, y} = changedTouches[0]
    if(type === 'touchstart') { // 每次開始都是一次新動作
      // 最開始點(diǎn)
      ctx.moveTo(x, y) // 設(shè)置繪圖起始坐標(biāo)。
    } else {
      // 上一段終點(diǎn)
      ctx.lineTo(x, y) // 從最后一點(diǎn)到點(diǎn)(x,y)繪制一條直線。
      // 繪制
      ctx.stroke();
      // 下一段起點(diǎn)
      ctx.moveTo(x, y) // 設(shè)置繪圖起始坐標(biāo)。
    }
    // 每一次結(jié)束或者意外中斷,保存一份圖片到歷史記錄中
    if(type === 'touchend' || type === 'touchcancel') {
      // 生成圖片
      // historyImag.push(canvas.toDataURL('image/png'))
      wx.canvasToTempFilePath({
        canvas,
        png: 'png',
        success: res => {
          // 生成的圖片臨時(shí)文件路徑
          const tempFilePath = res.tempFilePath
          historyImag.push(tempFilePath)
          this.setData(historyImag)
        },
      })
    }
  },
  // 上一步
  prev() {
    this.setData({
      historyImag: this.data.historyImag.slice(0, this.data.historyImag.length - 1)
    })
    this.initCanvas()
  },
  // 重寫
  overwrite() {
    this.setData({
      historyImag: []
    })
    this.initCanvas()
  },
  // 提交
  confirm() {
    const {canvas, historyImag} = this.data
    if(historyImag.length === 0) {
      Toast.fail('請先簽名后保存!');
      return
    }
    // 生成圖片
    wx.canvasToTempFilePath({
      canvas,
      png: 'png',
      success: res => {
        // 生成的圖片臨時(shí)文件路徑
        const tempFilePath = res.tempFilePath
        // 保存圖片到系統(tǒng)
        wx.saveImageToPhotosAlbum({
          filePath: tempFilePath,
        })
        // this.beforeRead(res.tempFilePath)
      },
    })
  },
  // // 圖片上傳
  // async beforeRead(tempFilePath) {
  //   const that = this;
  //   wx.getImageInfo({
  //     src: tempFilePath,
  //     success(imageRes) {
  //       wx.uploadFile({
  //         url: '', // 僅為示例,非真實(shí)的接口地址
  //         filePath: imageRes.path,
  //         name: 'file',
  //         header: {token: wx.getStorageSync('token')},
  //         formData: {
  //           ext: imageRes.type
  //         },
  //         success(fileRes) {
  //           const response = JSON.parse(fileRes.data);
  //           if (response.code === 200) {
  //             that.setData({
  //               fileList: [response.data]
  //             })
  //             that.submit();
  //           } else {
  //             wx.hideLoading();
  //             Toast.fail('附件上傳失敗');
  //             return false;
  //           }
  //         },
  //         fail(err) {
  //           wx.hideLoading();
  //           Toast.fail('附件上傳失敗');
  //         }
  //       });
  //     },
  //     fail(err) {
  //       wx.hideLoading();
  //       Toast.fail('附件上傳失敗');
  //     }
  //   })
  // },
  // 提交
  // submit() {
  //   const {fileList} = this.data
  //   wx.showLoading({title: '提交中...',})
  //   request('post', '', {
  //     fileIds: fileList.map(item => item.id),
  //   }).then(res => {
  //     if (res.code === 200) {
  //       wx.hideLoading();
  //       Toast.success('提交成功!');
  //       setTimeout(() => {
  //         wx.navigateBack({delta: 1});
  //       }, 1000)
  //     }
  //   })
  // },
})

以上就是微信小程序?qū)崿F(xiàn)canvas電子簽名功能的詳細(xì)內(nèi)容,更多關(guān)于微信小程序電子簽名的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論