先看效果图
我把这个做成了页面,没有做成组件,因为之前我是配合uview-plus的popup弹出层使用的,这种组件好像是没有生命周期的,第一次打开弹出层可以正常写字,但是关闭之后再打开就不会显示绘制的线条了,还需要重新加载组件的父页面才可以重新写字,所以我又做成了页面,功能就正常了。
代码实现:
<template>
<view class="page">
<canvas class="mycanvas" canvas-id="mycanvas" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend"></canvas>
<view class="button-group">
<button class="one" @click="clear">清空</button>
<button class="two" @click="finish">确定</button>
</view>
</view>
</template>
<script setup>
import { reactive,onMounted } from "vue";
import {
onReachBottom,
onShow,onLoad,onReady,onHide
} from '@dcloudio/uni-app';
import http from '@/common/http.js'
import store from '@/store/index.js'
import { pathToBase64, base64ToPath } from 'image-tools'
let state = store()
let props=defineProps({
})
let emits = defineEmits()
let data=reactive({
ctx:null, //绘图图像
points:[],//路径点集合
path:'',
flag:false,
})
onShow(()=>{
data.ctx = uni.createCanvasContext("mycanvas",this); //创建绘图对象
//设置画笔样式
data.ctx.lineWidth = 4;
data.ctx.lineCap = "round"
data.ctx.lineJoin = "round"
data.flag=false
})
//触摸开始,获取到起点
function touchstart(e){
let startX = e.changedTouches[0].x;
let startY = e.changedTouches[0].y;
let startPoint = {X:startX,Y:startY};
/* **************************************************
#由于uni对canvas的实现有所不同,这里需要把起点存起来
* **************************************************/
data.points.push(startPoint);
//每次触摸开始,开启新的路径
data.ctx.beginPath();
}
//触摸移动,获取到路径点
function touchmove(e){
let moveX = e.changedTouches[0].x;
let moveY = e.changedTouches[0].y;
let movePoint = {X:moveX,Y:moveY};
data.points.push(movePoint);//存点
let len = data.points.length;
if(len>=2){
draw();//绘制路径
}
}
// 触摸结束,将未绘制的点清空防止对后续路径产生干扰
function touchend(){
data.points=[];
}
/* ***********************************************
# 绘制笔迹
# 1.为保证笔迹实时显示,必须在移动的同时绘制笔迹
# 2.为保证笔迹连续,每次从路径集合中区两个点作为起点(moveTo)和终点(lineTo)
# 3.将上一次的终点作为下一次绘制的起点(即清除第一个点)
************************************************ */
function draw() {
let point1 = data.points[0]
let point2 = data.points[1]
data.points.shift()
data.ctx.moveTo(point1.X, point1.Y)
data.ctx.lineTo(point2.X, point2.Y)
data.ctx.stroke()
data.ctx.draw(true)
data.flag=true
}
//清空画布
function clear(){
uni.getSystemInfo({
success: function(res) {
let canvasw = res.windowWidth;
let canvash = res.windowHeight;
data.ctx.clearRect(0, 0, canvasw, canvash);
data.ctx.draw(false);
data.flag=false
}
})
}
//完成绘画并保存到本地
function finish(){
if(!data.flag){
http.hint('请签名!')
return
}
// 此API,H5中返回的是base64,APP中返回的是png图片,一个临时路径
uni.canvasToTempFilePath({
canvasId: 'mycanvas',
success: function(res) {
let path = res.tempFilePath;
// 因为我是在APP在,所以需要base64,在这里用image-tools插件把png转为base64
pathToBase64(path).then(res=>{
// 因为之前后端要svg字符串,但是png直接转svg转不了,所以先转为base64,然后在转svg
// let svg=`<?xml version="1.0" standalone="no"?>
// <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
// <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="body_1" width="100%" height="500">
// <g transform="matrix(1.3333334 0 0 1.3333334 0 0)">
// <image x="0" y="0" xlink:href="${res}" preserveAspectRatio="none" width="100%" height="500" /></g>
// </svg>`
state.svgPath=res
// emits('confirm',true)
uni.navigateBack({
delta:1
})
}).catch(err=>{
console.log('err :>> ', err);
})
}
})
}
</script>
<style scoped lang="less">
.page{
overflow: hidden;
}
.button-group{
position: fixed;
left: 50rpx;
bottom: 10rpx;
display: flex;
align-items: center;
// justify-content: space-between;
// height: 200rpx;
.one{
width: 200rpx;
margin-right: 50rpx;
border: 1px solid #123454;
}
.two{
width: 400rpx;
background-color: #123454;
color: #fff;
}
}
.title{
height:50upx;
line-height: 50upx;
font-size: 16px;
}
.mycanvas{
width: 100%;
height: calc(100vh - 200upx);
background-color: #ECECEC;
}
.footer{
font-size: 16px;
height: 150upx;
display: flex;
justify-content: space-around;
align-items: center;
}
.left,.right{
line-height: 100upx;
height: 100upx;
width: 250upx;
text-align: center;
font-weight: bold;
color: white;
border-radius: 5upx;
}
.left{
background: #007AFF;
}
.right{
background:orange;
}
</style>
在H5中,点击确定后输出为base64,在APP中输出为临时路径的png图片,我是在APP中使用,所以还需要把png图片转为base64,格式按后端要求,如果只需要传图片就不需要转base64了,也可以转svg,转svg的我注释掉了,用到的图片转换插件为image-tools。
如果大家在使用过程中发现了什么问题,欢迎在评论区指正,谢谢观看!