1. 程式人生 > 程式設計 >微信小程式canvas實現簽名功能

微信小程式canvas實現簽名功能

在微信小程式專案中,開發模組涉及到手寫簽名功能,微信小程式canvas閃亮登場

前言

微信小程式canvas實現簽名功能

核心內容簡介:

(1)簽名實現,開始,移動,結束

(2)重寫

(3)完成

(4)上傳

一、微信小程式canvas實現簽名功能

效果演示:

(1)簽名實現

微信小程式canvas實現簽名功能

(2)重寫

微信小程式canvas實現簽名功能

(3)完成

微信小程式canvas實現簽名功能

完成後將圖片展示在相應的位置

(4)根據業務需求,可以將圖片上傳到後臺,在需要的地方展示

二、上程式碼

1.全部演示

wxml

<!--pages/canvas-test/canvas-test.wxml-->

<view class="handCenter">

 <canvas class="handWriting" disable-scroll="true" bindtouchstart="uploadScaleStart" bindtouchmove="uploadScaleMove"
 bindtouchend="uploadScaleEnd" bindtap="mouseDown" canvas-id="handWriting">
 </canvas>

</view>


<view class="handBtn">
 <button catchtap="retDraw" class="delBtn">重寫</button>
 <button catchtap="subCanvas" class="subBtn">完成</button> 
</view>


<view class="preview"> 

 <image wx:if="{{tmpPath}}" style="width:100%;height:100%;" src="{{tmpPath}}"></image>

</view>

js

const app = getApp()
const api = require('../../utils/request.js'); //相對路徑
const apiEev = require('../../config/config');
Page({
 data: {
 canvasName: 'handWriting',ctx: '',canvasWidth: 0,canvasHeight: 0,transparent: 1,// 透明度
 selectColor: 'black',lineColor: '#1A1A1A',// 顏色
 lineSize: 1.5,// 筆記倍數
 lineMin: 0.5,// 最小筆畫半徑
 lineMax: 4,// 最大筆畫半徑
 pressure: 1,// 預設壓力
 smoothness: 60,//順滑度,用60的距離來計算速度
 currentPoint: {},currentLine: [],// 當前線條
 firstTouch: true,// 第一次觸發
 radius: 1,//畫圓的半徑
 cutArea: { top: 0,right: 0,bottom: 0,left: 0 },//裁剪區域
 bethelPoint: [],//儲存所有線條 生成的貝塞爾點;
 lastPoint: 0,chirography: [],//筆跡
 currentChirography: {},//當前筆跡
 linePrack: [],//劃線軌跡,生成線條的實際點
 tmpPath:''
 },// 筆跡開始
 uploadScaleStart (e) {
 if (e.type != 'touchstart') return false;
 let ctx = this.data.ctx;
 ctx.setFillStyle(this.data.lineColor); // 初始線條設定顏色
 ctx.setGlobalAlpha(this.data.transparent); // 設定半透明
 let currentPoint = {
 x: e.touches[0].x,y: e.touches[0].y
 }
 let currentLine = this.data.currentLine;
 currentLine.unshift({
 time: new Date().getTime(),dis: 0,x: currentPoint.x,y: currentPoint.y
 })
 this.setData({
 currentPoint,// currentLine
 })
 if (this.data.firstTouch) {
 this.setData({
 cutArea: { top: currentPoint.y,right: currentPoint.x,bottom: currentPoint.y,left: currentPoint.x },firstTouch: false
 })
 }
 this.pointToLine(currentLine);
 },// 筆跡移動
 uploadScaleMove (e) {
 if (e.type != 'touchmove') return false;
 if (e.cancelable) {
 // 判斷預設行為是否已經被禁用
 if (!e.defaultPrevented) {
 e.preventDefault();
 }
 }
 let point = {
 x: e.touches[0].x,y: e.touches[0].y
 }

 //測試裁剪
 if (point.y < this.data.cutArea.top) {
 this.data.cutArea.top = point.y;
 }
 if (point.y < 0) this.data.cutArea.top = 0;

 if (point.x > this.data.cutArea.right) {
 this.data.cutArea.right = point.x;
 }
 if (this.data.canvasWidth - point.x <= 0) {
 this.data.cutArea.right = this.data.canvasWidth;
 }
 if (point.y > this.data.cutArea.bottom) {
 this.data.cutArea.bottom = point.y;
 }
 if (this.data.canvasHeight - point.y <= 0) {
 this.data.cutArea.bottom = this.data.canvasHeight;
 }
 if (point.x < this.data.cutArea.left) {
 this.data.cutArea.left = point.x;
 }
 if (point.x < 0) this.data.cutArea.left = 0;

 this.setData({
 lastPoint: this.data.currentPoint,currentPoint: point
 })
 let currentLine = this.data.currentLine
 currentLine.unshift({
 time: new Date().getTime(),dis: this.distance(this.data.currentPoint,this.data.lastPoint),x: point.x,y: point.y
 })
 // this.setData({
 // currentLine
 // })
 this.pointToLine(currentLine);
 },// 筆跡結束
 uploadScaleEnd (e) {
 if (e.type != 'touchend') return 0;
 let point = {
 x: e.changedTouches[0].x,y: e.changedTouches[0].y
 }
 this.setData({
 lastPoint: this.data.currentPoint,y: point.y
 })
 // this.setData({
 // currentLine
 // })
 if (currentLine.length > 2) {
 var info = (currentLine[0].time - currentLine[currentLine.length - 1].time) / currentLine.length;
 //$("#info").text(info.toFixed(2));
 }
 //一筆結束,儲存筆跡的座標點,清空,當前筆跡
 //增加判斷是否在手寫區域;
 this.pointToLine(currentLine);
 var currentChirography = {
 lineSize: this.data.lineSize,lineColor: this.data.lineColor
 };
 var chirography = this.data.chirography
 chirography.unshift(currentChirography);
 this.setData({
 chirography
 })
 var linePrack = this.data.linePrack
 linePrack.unshift(this.data.currentLine);
 this.setData({
 linePrack,currentLine: []
 })
 },onLoad () {
 let canvasName = this.data.canvasName
 let ctx = wx.createCanvasContext(canvasName)
 this.setData({
 ctx: ctx
 })
 var query = wx.createSelectorQuery();
 query.select('.handCenter').boundingClientRect(rect => {
 this.setData({
 canvasWidth: rect.width,canvasHeight: rect.height
 })
 }).exec();
 },subCanvas(){
 // 新增我的
 let that = this
 let ctx = this.data.ctx;
 ctx.draw(true,setTimeout(function(){ //我的新增定時器及回撥
 wx.canvasToTempFilePath({
 x: 0,y: 0,width: 375,height: 152,canvasId: 'handWriting',fileType: 'png',success: function(res) {
 that.setData({
 tmpPath:res.tempFilePath
 })
 console.log(that.data.tmpPath,'看下是個啥玩意')
 that.upImgs(that.data.tmpPath,0)
 }
 },ctx)
 },1000))
 },// 新增將儲存的圖片路徑上傳到檔案伺服器
upImgs: function (imgurl,index) {
 console.log(imgurl,'看下路徑是多少')
 var that = this;
 wx.uploadFile({
 url: apiEev.api + 'xxxx',//後臺上傳路徑
 filePath: imgurl,name: 'file',header: {
 'content-type': 'multipart/form-data'
 },formData: null,success: function (res) {
 console.log(res) //介面返回網路路徑
 var data = JSON.parse(res.data)
 console.log(data,'看下data是個啥')
 if (data.code == "success") {
 console.log('成功')
 }
 }
 })
},retDraw () {
 this.data.ctx.clearRect(0,700,730)
 this.data.ctx.draw()
 this.setData({
 tmpPath:''
 })
 },//畫兩點之間的線條;引數為:line,會繪製最近的開始的兩個點;
 pointToLine (line) {
 this.calcBethelLine(line);
 return;
 },//計算插值的方式;
 calcBethelLine (line) {
 if (line.length <= 1) {
 line[0].r = this.data.radius;
 return;
 }
 let x0,x1,x2,y0,y1,y2,r0,r1,r2,len,lastRadius,dis = 0,time = 0,curveValue = 0.5;
 if (line.length <= 2) {
 x0 = line[1].x
 y0 = line[1].y
 x2 = line[1].x + (line[0].x - line[1].x) * curveValue;
 y2 = line[1].y + (line[0].y - line[1].y) * curveValue;
 //x2 = line[1].x;
 //y2 = line[1].y;
 x1 = x0 + (x2 - x0) * curveValue;
 y1 = y0 + (y2 - y0) * curveValue;;

 } else {
 x0 = line[2].x + (line[1].x - line[2].x) * curveValue;
 y0 = line[2].y + (line[1].y - line[2].y) * curveValue;
 x1 = line[1].x;
 y1 = line[1].y;
 x2 = x1 + (line[0].x - x1) * curveValue;
 y2 = y1 + (line[0].y - y1) * curveValue;
 }
 //從計算公式看,三個點分別是(x0,y0),(x1,y1),(x2,y2) ;(x1,y1)這個是控制點,控制點不會落在曲線上;實際上,這個點還會手寫獲取的實際點,卻落在曲線上
 len = this.distance({ x: x2,y: y2 },{ x: x0,y: y0 });
 lastRadius = this.data.radius;
 for (let n = 0; n < line.length - 1; n++) {
 dis += line[n].dis;
 time += line[n].time - line[n + 1].time;
 if (dis > this.data.smoothness) break;
 }
 this.setData({
 radius: Math.min(time / len * this.data.pressure + this.data.lineMin,this.data.lineMax) * this.data.lineSize
 });
 line[0].r = this.data.radius;
 //計算筆跡半徑;
 if (line.length <= 2) {
 r0 = (lastRadius + this.data.radius) / 2;
 r1 = r0;
 r2 = r1;
 //return;
 } else {
 r0 = (line[2].r + line[1].r) / 2;
 r1 = line[1].r;
 r2 = (line[1].r + line[0].r) / 2;
 }
 let n = 5;
 let point = [];
 for (let i = 0; i < n; i++) {
 let t = i / (n - 1);
 let x = (1 - t) * (1 - t) * x0 + 2 * t * (1 - t) * x1 + t * t * x2;
 let y = (1 - t) * (1 - t) * y0 + 2 * t * (1 - t) * y1 + t * t * y2;
 let r = lastRadius + (this.data.radius - lastRadius) / n * i;
 point.push({ x: x,y: y,r: r });
 if (point.length == 3) {
 let a = this.ctaCalc(point[0].x,point[0].y,point[0].r,point[1].x,point[1].y,point[1].r,point[2].x,point[2].y,point[2].r);
 a[0].color = this.data.lineColor;
 // let bethelPoint = this.data.bethelPoint;
 // console.log(a)
 // console.log(this.data.bethelPoint)
 // bethelPoint = bethelPoint.push(a);
 this.bethelDraw(a,1);
 point = [{ x: x,r: r }];
 }
 }
 this.setData({
 currentLine: line
 })
 },//求兩點之間距離
 distance (a,b) {
 let x = b.x - a.x;
 let y = b.y - a.y;
 return Math.sqrt(x * x + y * y);
 },ctaCalc (x0,r2) {
 let a = [],vx01,vy01,norm,n_x0,n_y0,vx21,vy21,n_x2,n_y2;
 vx01 = x1 - x0;
 vy01 = y1 - y0;
 norm = Math.sqrt(vx01 * vx01 + vy01 * vy01 + 0.0001) * 2;
 vx01 = vx01 / norm * r0;
 vy01 = vy01 / norm * r0;
 n_x0 = vy01;
 n_y0 = -vx01;
 vx21 = x1 - x2;
 vy21 = y1 - y2;
 norm = Math.sqrt(vx21 * vx21 + vy21 * vy21 + 0.0001) * 2;
 vx21 = vx21 / norm * r2;
 vy21 = vy21 / norm * r2;
 n_x2 = -vy21;
 n_y2 = vx21;
 a.push({ mx: x0 + n_x0,my: y0 + n_y0,color: "#1A1A1A" });
 a.push({ c1x: x1 + n_x0,c1y: y1 + n_y0,c2x: x1 + n_x2,c2y: y1 + n_y2,ex: x2 + n_x2,ey: y2 + n_y2 });
 a.push({ c1x: x2 + n_x2 - vx21,c1y: y2 + n_y2 - vy21,c2x: x2 - n_x2 - vx21,c2y: y2 - n_y2 - vy21,ex: x2 - n_x2,ey: y2 - n_y2 });
 a.push({ c1x: x1 - n_x2,c1y: y1 - n_y2,c2x: x1 - n_x0,c2y: y1 - n_y0,ex: x0 - n_x0,ey: y0 - n_y0 });
 a.push({ c1x: x0 - n_x0 - vx01,c1y: y0 - n_y0 - vy01,c2x: x0 + n_x0 - vx01,c2y: y0 + n_y0 - vy01,ex: x0 + n_x0,ey: y0 + n_y0 });
 a[0].mx = a[0].mx.toFixed(1);
 a[0].mx = parseFloat(a[0].mx);
 a[0].my = a[0].my.toFixed(1);
 a[0].my = parseFloat(a[0].my);
 for (let i = 1; i < a.length; i++) {
 a[i].c1x = a[i].c1x.toFixed(1);
 a[i].c1x = parseFloat(a[i].c1x);
 a[i].c1y = a[i].c1y.toFixed(1);
 a[i].c1y = parseFloat(a[i].c1y);
 a[i].c2x = a[i].c2x.toFixed(1);
 a[i].c2x = parseFloat(a[i].c2x);
 a[i].c2y = a[i].c2y.toFixed(1);
 a[i].c2y = parseFloat(a[i].c2y);
 a[i].ex = a[i].ex.toFixed(1);
 a[i].ex = parseFloat(a[i].ex);
 a[i].ey = a[i].ey.toFixed(1);
 a[i].ey = parseFloat(a[i].ey);
 }
 return a;
 },bethelDraw (point,is_fill,color) {
 // 新增我的
 let that = this
 let ctx = this.data.ctx;
 ctx.beginPath();
 ctx.moveTo(point[0].mx,point[0].my);
 if (undefined != color) {
 ctx.setFillStyle(color);
 ctx.setStrokeStyle(color);
 } else {
 ctx.setFillStyle(point[0].color);
 ctx.setStrokeStyle(point[0].color);
 }
 for (let i = 1; i < point.length; i++) {
 ctx.bezierCurveTo(point[i].c1x,point[i].c1y,point[i].c2x,point[i].c2y,point[i].ex,point[i].ey);
 }
 ctx.stroke();
 if (undefined != is_fill) {
 ctx.fill(); //填充圖形 ( 後繪製的圖形會覆蓋前面的圖形,繪製時注意先後順序 )
 }
 ctx.draw(true)
 },selectColorEvent (event) {
 console.log(event)
 var color = event.currentTarget.dataset.colorValue;
 var colorSelected = event.currentTarget.dataset.color;
 this.setData({
 selectColor: colorSelected,lineColor: color
 })
 }
})
/* pages/canvas-test2/canvas-test2.wxss */
.canvasId {
 position: absolute;
 left: 50%;
 top: 0;
 transform: translate(-50%);
 z-index: 1;
 border: 2px dashed #ccc;
 border-radius: 8px;
 margin-bottom: 66px;
}

.handCenter {
 border: 1px solid red;
}

.handWriting {
 width: 100%;
}

.preview {
 width: 375px;
 height: 152px;
}

2.重點部分分析

(1)簽名基本實現,開始,移動,結束

// 筆跡開始
 uploadScaleStart (e) {
 if (e.type != 'touchstart') return false;
 let ctx = this.data.ctx;
 ctx.setFillStyle(this.data.lineColor); // 初始線條設定顏色
 ctx.setGlobalAlpha(this.data.transparent); // 設定半透明
 let currentPoint = {
 x: e.touches[0].x,currentLine: []
 })
},

記得要先在onload中初始化

程式碼拿走直接用

(2)重新簽署

大白話就是清空畫布

retDraw () {
 this.data.ctx.clearRect(0,730)
 this.data.ctx.draw()
 this.setData({
 tmpPath:''
 })
},

(3)簽署完成

subCanvas(){
 // 新增我的
 let that = this
 let ctx = this.data.ctx;
 ctx.draw(true,

裡邊的回撥比較重要哦:

防止拿不到畫布內容,可以設定延遲;
wx.canvasToTempFilePath方法獲取到畫布圖片內容;

(4)根據業務需求,可以將圖片上傳到後臺,在需要的地方展示

重點是如何上傳到後臺

// 新增將儲存的圖片路徑上傳到檔案伺服器
upImgs: function (imgurl,//後臺檔案上傳的路徑介面
 filePath: imgurl,

總結

微信小程式canvas實現簽名功能。

特別提醒:在真機除錯和體驗版中可能會出現卡頓情況,有條件要釋出至預釋出中檢視是否影響效能。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。