Skip to content

uniapp--vue3生成canvas实现电子签名

概要

提示:使用uniapp中vue3语法,实现canvas电子签名,并将签名保存为图片并生成地址。

使用场景: 用户可在应用中进行电子签名,并将签名保存为图片后存入数据库等用途。

项目构建: 本教程提供了多种实现方法。其中,方法一具有较强的封装性和简洁性,推荐使用;而方法二虽然可用,但代码冗余,不太推荐使用。

使用步骤

方法一:使用vue3的hooks直接封装,减少不必要的代码

javascript
// 在hooks目录下,新建index.js
import { ref, unref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'

export function useDrawSignature(canvasId) {
  let ctx = null
  let isButtonDown = false
  let points = []
  let isSigned = ref(false)

  onLoad(() => {
    ctx = uni.createCanvasContext(canvasId)
    // 设置画笔样式
    ctx.lineWidth = 4
    ctx.lineCap = 'round'
    ctx.lineJoin = 'round'
  })

    // 触摸开始,获取到起点
  function touchStart(e) {
    let startPoint = { X: e.changedTouches[0].x, Y: e.changedTouches[0].y }
    points.push(startPoint) // 把起点存起来
    ctx.beginPath() // 每次触摸开始,开启新的路径
    isButtonDown = true
  }

  // 触摸移动,获取到路径点
  function touchMove(e) {
    if (isButtonDown) {
      let movePoint = { X: e.changedTouches[0].x, Y: e.changedTouches[0].y }
      points.push(movePoint) // 存点
      let len = points.length
      if (len >= 2) {
        draw() // 绘制路径
      }
    }
  }

  // 触摸结束,将未绘制的点清空防止对后续路径产生干扰
  function touchEnd() {
    points = []
    isButtonDown = false
  }

  // 绘画
  function draw() {
    let point1 = points[0]
    let point2 = points[1]
    points.shift()
    ctx.moveTo(point1.X, point1.Y)
    ctx.lineTo(point2.X, point2.Y)
    ctx.stroke()
    ctx.draw(true)
    isSigned.value = true
  }

  function clear() {
    ctx.clearRect(0, 0, 1000, 1000)
    ctx.draw(true)
    isSigned.value = false
  }

  return {
    isSigned,
    touchStart,
    touchMove,
    touchEnd,
    clear
  }
}

使用:

vue
<template>
	<canvas
	   canvas-id="canvas"
	   class="canvas-inner"
	   disable-scroll="true"
	   @touchstart="touchStart"
	   @touchmove="touchMove"
	   @touchend="touchEnd"
	/>
	 <cover-view class="flex btn flex-end">
	    <cover-view class="clear btn-inner text-center" @click="clear">清空</cover-view>
	    <cover-view class="confirm btn-inner text-center" @click="save">保存</cover-view>
    </cover-view>
</template>
<script setup>
import { useDrawSignature } from '@/hooks/index'
const { isSigned, touchStart, touchMove, touchEnd, clear } = useDrawSignature('canvas')

const save = () => {
	if (!unref(isSigned)) {
		uni.showToast({
			title: '请签名',
			icon: 'none'
		})
		return
	}
}
</script>

方法二:(推荐方法一)

1、 提示:template部分

vue
<template>
  <view class="setUpASeal">
    <canvas
      canvas-id="writeCanvas"
      class="writeCanvas"
      disable-scroll="true"
      @touchstart="touchStart"
      @touchmove="touchMove"
      @touchend="touchEnd"
    />
    <cover-view class="flex btn">
      <cover-view class="clearBtn" @tap="clear">清除</cover-view>
      <cover-view class="saveBtn" @tap="saveCanvas">保存图片</cover-view>
    </cover-view>
  </view>
</template>

2、 提示:script部分

vue
<script setup>
import { ref, unref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import * as CASERVICE from '@/caCertification/service/index'
import { uploadFile } from '@/api/index'
import { useAccountStore } from '@/store/account'
const account = useAccountStore()
const userInfo = ref(account.user)

// 初始化画布
let ctx = null
let isButtonDown = false
let lastX = 0
let lastY = 0
let points = []
onLoad(() => {
  ctx = uni.createCanvasContext('writeCanvas')
  // 设置画笔样式
  ctx.lineWidth = 4
  ctx.lineCap = 'round'
  ctx.lineJoin = 'round'
})

// 触摸开始,获取到起点
function touchStart(e) {
  lastX = e.changedTouches[0].x
  lastY = e.changedTouches[0].y
  let startPoint = { X:lastX, Y:lastY }
  points.push(startPoint) // 把起点存起来
  ctx.beginPath() // 每次触摸开始,开启新的路径
  isButtonDown = true
}

// 触摸移动,获取到路径点
function touchMove(e) {
  if (isButtonDown) {
    const currentX = e.changedTouches[0].x
    const currentY = e.changedTouches[0].y
    let movePoint = { X:currentX, Y:currentY }
    points.push(movePoint) // 存点
    let len = points.length
    if (len >= 2) {
      draw() // 绘制路径
    }
  }
}

// 触摸结束,将未绘制的点清空防止对后续路径产生干扰
function touchEnd() {
  points = []
  isButtonDown = false
}

// 绘画
function draw() {
  let point1 = points[0]
  let point2 = points[1]
  points.shift()
  ctx.moveTo(point1.X, point1.Y)
  ctx.lineTo(point2.X, point2.Y)
  ctx.stroke()
  ctx.draw(true)
}

const upload = async(tempFilePath) => {
  let res = await uploadFile(tempFilePath)
  const { data } = JSON.parse(res)
  // 保存签章
  let params = {
    handwrittenSignatureUrl: data.url,
    organId: unref(userInfo).doctorLoginInfo.organId,
    doctorId: unref(userInfo).doctorLoginInfo.doctorId,
  }
  let setHandwrittenInfo = await CASERVICE.setHandwrittenSignature(params)
  return setHandwrittenInfo
}

// 保存图片
function saveCanvas() {
  uni.showLoading({
    title: '保存中...',
    mask: true
  })
  uni.canvasToTempFilePath({
    canvasId: 'writeCanvas',
    async success(res) {
      let path = res.tempFilePath
      const setHandwrittenInfo = await upload(path)
      if (setHandwrittenInfo.code + '' === '1') {
        uni.showToast({
          title: '保存成功',
          icon: 'none',
          duration: 1000
        })
        setTimeout(() => {
          uni.navigateBack()
        }, 600)
      } else {
        uni.showToast({
          title: '保存失败',
          icon: 'none',
          duration: 1000
        })
      }
    }
  })
  uni.hideLoading()
}

// 清除画布
function clear() {
  ctx.clearRect(0, 0, 1000, 1000)
  ctx.draw(true)
}

</script>

3、提示:style部分

scss
<style lang="scss" scoped>
.setUpASeal {
  width: 100vw;
  height: 100vh;
  overflow: hidden;

  .writeCanvas {
    width: 100%;
    height: 100%;
  }

  .btn {
    position: fixed;
    right: 20rpx;
    bottom: 20rpx;
    z-index: 999;
    font-size: 20rpx;
    color: #fff;
    text-align: center;

    .clearBtn {
      margin-right: 32rpx;
      background-color: #333;
      border-radius: 8rpx;
    }

    .saveBtn {
      background-color: #333;
      border-radius: 8rpx;
    }

    .clearBtn,
    .saveBtn {
      padding: 10rpx 20rpx;
    }
  }
}
</style>

技术细节

提示:这里可以添加技术细节

小结

提示:这里可以添加总结

小小棱镜,无限可能