具体效果如下
1、 未签名前
a1.png
2、签名中
a2.png
3、完成签名
a3.png
使用canvas 功能来实现
1、获取canvas对象
const query = this.createSelectorQuery();
query.select('#myCanvas').fields({id:true,
node:true,
size:true,
dataset: true,
size: true})
.exec((res) => {
const canvas = res[0].node
const ctx = canvas.getContext('2d')
})
2、初始化提示语
this.data.ctx.lineWidth = 3; // 字体粗细
this.data.ctx.font = '30px Arial'; // 字体大小,
this.data.ctx.fillStyle = '#ecf0ef'; // 填充颜色
var metrics = this.data.ctx.measureText(this.data.writeTips);
var textWidth = metrics.width;
var textHeight = 30;
var x = (this.data.canvas.width / 2 / this.data.dpr) - textWidth / 2;
var y = this.data.canvas.height / 2 / this.data.dpr + textHeight / 2;
this.data.ctx.fillText(this.data.writeTips, x , y)
this.data.ctx.closePath();
绘制
绑定 start、move、end事件
onStart(event) {
let currentPoint = {
x: event.touches[0].x,
y: event.touches[0].y
}
},
onMove(event) {
let {
startPoint
} = this.data
let currentPoint = {
x: event.touches[0].x,
y: event.touches[0].y
}
}
3、保存成图片
wx.canvasToTempFilePath({
x: 0,
y: 0,
width: this.data.canvas.width,
height: this.data.canvas.height,
// canvasId: 'myCanvas',
canvas:this.data.canvas,
fileType:'png',
quality:1,
success:(res)=> {
},
fail(err) {
console.log('canvas 转换成图片',err);
}
},this)
4、封装成组件(完整代码)
1、wxml
<view class="modal-content-area">
<view class="modal-content">
<canvas type="2d" id="myCanvas" class="modal-canvas" disable-scroll="{{true}}"
bindtouchstart="onStart"
bindtouchmove="onMove"
bindtouchend="onEnd"></canvas>
</view>
<view class="modal-bottom">
<view class="model-btn modal-clear {{!hasDraw?'disabled':''}}" bindtap="onClear">重新签名</view>
<view class="model-btn modal-confirm {{!hasDraw?'disabled':''}}" bindtap="onConfrim" wx:if="{{!hasConfirm}}"> 确认签名</view>
</view>
</view>
2、scss
.modal-content-area {
z-index: 5;
width: 100%;
background: #F5F7FB;
position: relative;
.modal-content {
width: 100%;
height: 300px;
background: #ffffff;
position: relative;
.toast {
position: absolute;
top:0;
left: 0;
right:0;
bottom:0;
z-index: 100;
font-size: 26px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: rgba(58, 65, 85, 0.4);
display: flex;
align-items: center;
justify-content: center;
}
}
.modal-bottom {
display: flex;
justify-content: flex-end;
width: 100%;
padding:20px;
box-sizing: border-box;
.model-btn{
font-size: 14px;
padding:10px 15px;
box-sizing: border-box;
&.modal-clear {
background: rgba(103, 149, 255, 0.2);
color: #3670F5;
margin-right: 12rpx;
&.disabled{
opacity: 0.5;
}
}
&.modal-confirm {
background: rgba(103, 149, 255,1);
color: #FFFFFF;
&.disabled{
background: rgba(103, 149, 255,.5);
}
}
}
}
}
.signature-modal {
width: 100%;
height: 100%;
overflow: hidden;
z-index: 3;
background: blue;
}
.modal-mask {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 4;
background-color: rgba(0, 0, 0, .5);
}
.modal-canvas {
width: 100%;
height: 100%;
background: transparent;
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 4px;
z-index: 2;
position: relative;
}
.flex_btn {
display: flex;
align-items: center;
}
3、js
Component({
/**
* 组件的初始数据
*/
data: {
tempFilePath: '',
writeTips: '请在此区域签名',
writeTipsTrue: true,
hasDraw: false,
hasConfirm:false,
canvasName: '#handWriting',
ctx: '',
dpr:'1',
canvas:'',
canvasWidth: 0,
canvasHeight: 0,
startPoint: {
x: 0,
y: 0,
},
selectColor: 'black',
lineColor: '#000', // 颜色
lineWidth: 1, // 笔记倍数
radius: 2, //画圆的半径
},
// 初始化画布
attached() {
this.setData({
hideModal: false,
hasConfirm:false
})
this.initCanvas()
this.triggerEvent('reset')
},
// 方法列表
methods: {
ininIips(){
this.data.ctx.lineWidth = 3; // 字体粗细
this.data.ctx.font = '30px Arial'; // 字体大小,
this.data.ctx.fillStyle = '#ecf0ef'; // 填充颜色
var metrics = this.data.ctx.measureText(this.data.writeTips);
var textWidth = metrics.width;
var textHeight = 30;
var x = (this.data.canvas.width / 2 / this.data.dpr) - textWidth / 2;
var y = this.data.canvas.height / 2 / this.data.dpr + textHeight / 2;
this.data.ctx.fillText(this.data.writeTips, x , y)
this.data.ctx.closePath();
},
initCanvas(){
const query = this.createSelectorQuery();
query.select('#myCanvas').fields({id:true,
node:true,
size:true,
dataset: true,
size: true})
.exec((res) => {
const canvas = res[0].node
const ctx = canvas.getContext('2d')
const dpr=wx.getWindowInfo().pixelRatio||1;//缩放级
canvas.width = res[0].width * dpr
canvas.height= res[0].height * dpr
ctx.scale(dpr, dpr);
ctx.lineGap ='round';
ctx.lineJoin ='round';
this.setData({
ctx,
canvas,
dpr
})
this.ininIips()
})
},
onStart(event) {
// console.log('kaishi',event)
if (event.type != 'touchstart') return false;
if(this.data.hasConfirm) {
wx.showToast({
title: '已经签过名字了呦,请勿重复签名!',
icon:'none'
})
return
}
let currentPoint = {
x: event.touches[0].x,
y: event.touches[0].y
}
//清空画布
if(this.data.writeTipsTrue){
this.data.ctx.clearRect(0, 0, this.data.canvas.width,this.data.canvas.height);
this.data.ctx.linewidth=this.data.lineWidth;//字体粗细
this.data.ctx.strokeStyle = this.data.selectColor
this.data.ctx.font = '20px Arial'; // 字体大小,
this.setData({
writeTipsTrue:false
})
}
this.data.ctx.fillStyle = 'rgba(0, 0, 0, .1)';
this.drawCircle(currentPoint);
this.setData({
startPoint: currentPoint,
hasDraw: true, //签字了
});
},
onMove(event) {
// console.log('移动')
if (event.type != "touchmove") return false;
if(this.data.hasConfirm) return
let {
startPoint
} = this.data
let currentPoint = {
x: event.touches[0].x,
y: event.touches[0].y
}
// console.log('===',startPoint,currentPoint)
this.drawLine(startPoint, currentPoint)
this.setData({
startPoint: currentPoint
})
},
onDown() {},
onEnd(e) {
this.data.ctx.stroke();
},
drawCircle(point) { //这里负责点
this.data.ctx.beginPath();
this.data.ctx.fillStyle = this.data.lineColor
//笔迹粗细由圆的大小决定
this.data.ctx.arc(point.x, point.y, this.data.radius, 0, 2 * Math.PI);
this.data.ctx.fill();
this.data.ctx.closePath();
// this.data.ctx.draw(true)
},
drawLine(sourcePoint, targetPoint) {
this.drawCircle(targetPoint);
this.data.ctx.beginPath();
this.data.ctx.lineWidth = this.data.radius * 2
this.data.ctx.moveTo(sourcePoint.x, sourcePoint.y);
this.data.ctx.lineTo(targetPoint.x, targetPoint.y);
this.data.ctx.stroke();
this.data.ctx.strokeStyle = this.data.lineColor
this.data.ctx.closePath();
// console.log('绘制线条',this.data.ctx)
},
onClear() {
if(!this.data.hasDraw){
wx.showToast({
title: '还未签字哟!',
icon: 'none',
mask: true
})
return
}
//清空画布
this.data.ctx.clearRect(0, 0, this.data.canvas.width,this.data.canvas.height);
this.data.ctx.fillStyle = 'rgba(0, 0, 0, .1)';
this.setData({
hasDraw: false, //未签字
writeTipsTrue:true,
hasConfirm:false
})
this.ininIips()
this.triggerEvent('confirm','')
},
onConfrim() {
if (!this.data.hasDraw) {
wx.showToast({
title: '还未签字哟!',
icon: 'none',
mask: true
})
return
} else {
this.setData({
hideModal:false
})
this.saveToImage();
}
},
saveToImage() {
wx.canvasToTempFilePath({
x: 0,
y: 0,
width: this.data.canvas.width,
height: this.data.canvas.height,
// canvasId: 'myCanvas',
canvas:this.data.canvas,
fileType:'png',
quality:1,
success:(res)=> {
// console.log(res, "res.tempFilePath");
if (res.tempFilePath) {
wx.showToast({
title: '签名保存成功!',
icon: 'none',
mask: true
})
this.setData({
hasConfirm:true
})
this.triggerEvent('confirm',res.tempFilePath)
}
},
fail(err) {
console.log('canvas 转换成图片',err);
}
},this)
}
}
})
5、引入使用
wxml
<signature bindconfirm = "onSignatureConfrim" id="signature" />
JSON文件
"usingComponents": {
"signature": "./components/signature/index"
}
js 文件
onSignatureConfrim(e){
console.log('签名',e)
},