This is the 5th day of my participation in the August More Text Challenge
background
The small program service requires users to sign an order to confirm the order. After confirming the order, a picture is displayed. Take stock of the implementation process and problems encountered. The realization effect is shown as follows:
Code implementation
1. Requirements analysis
Look at this demand, first disassemble the demand and sort out the implementation process. Referring to the figure above, it can be seen that the implementation steps are generally as follows:
- Page landscape, page layout, initialization
Canvas
The container; Canvas
Draw operation reminders and other text;touchstart()
Event creates the starting point of the path, storing its coordinates in the path coordinate array.touchmove()
Event to continuously store path coordinates into the array.touchend()
Event empties the array for the next drawing;
2. Hand operation
1. Landscape, layout, and initializationCanvas
The container
First is the first step, because it is in the small program, the mobile phone needs to be horizontal screen to obtain a larger space. “pageOrientation” can be set in the small program: “Landscape” is the most convenient. If the canvas is rotated, the resulting image is also rotated and needs to be rotated back. There is also a small Canvas component (the full code is attached at the end of this article, only the JS part is described here).
initCanvas() {
let {
width,
height
} = this.data
width = wx.getSystemInfoSync().windowWidth
height = wx.getSystemInfoSync().windowHeight
this.data.ctx = wx.createCanvasContext('canvas'.this)
this.setData({
width,
height
})
this.clearCanvas()
},
Copy the code
Because CTX objects need to be shared among multiple methods, they can be defined in data or external variables.
2.Canvas
Draw text such as operation reminders
You can set your setTextAlign(‘ Center ‘) and fillText to the position of the x axis at half the width.
clearCanvas() {
this.data.drawCount = 0
this.data.ctx.setTextBaseline('top')
this.data.ctx.setTextAlign('center')
this.data.ctx.setFontSize(20)
this.data.ctx.setFillStyle('# 616165');
this.data.ctx.fillText("Please sign in the gray area.".this.data.width / 2.30)
this.data.ctx.draw(false)},Copy the code
3.touchstart()
The event creates the starting point of the path, storing its coordinates in the path coordinate array
The warning text drawn in the previous step is similar to the input placeholder and needs to be cleared when the Canvas is triggered. Therefore, call the draw(false) event if it is triggered for the first time. False means to overwrite the previous drawing, and true means to retain the previous drawing
catchtouchstart(e) {
if (this.data.drawCount == 0) {
this.data.ctx.draw(false)}this.data.drawCount++
this.data.points.push(e.changedTouches[0])},Copy the code
- Touches: A list of all touches on the current screen;
- ChangedTouches: List of touches that trigger the current event
Example: Two fingers trigger the event at the same time, then there are two elements in both attributes; When two fingers trigger an event, the first finger doesn’t leave the screen, the second finger triggers only one changedTouches element, which represents the touch point of the currently triggered event, and two Touches, which represent the touch points of the current two fingers.
4.touchmove()
Event to continuously store path coordinates into the array
Because the path needs to be plotted in real time as the gesture moves, the Draw () event is executed inside the TouchMove () event. The setShadow() event sets a shadow on the track to make the handwriting look smoother.
catchtouchmove(e) {
if (e.touches.length > 1) {
return
}
this.data.points.push(e.changedTouches[0])
let points = this.data.points
for (let i in points) {
if (i == 0) {
this.data.ctx.moveTo(points[0].clientX, points[0].clientY)
} else {
this.data.ctx.lineTo(points[i].clientX, points[i].clientY)
}
}
this.data.ctx.setStrokeStyle('# 000000');
this.data.ctx.setLineWidth(3);
this.data.ctx.setShadow(0.0.0.5.'# 000000')
this.data.ctx.setLineCap('round');
this.data.ctx.setLineJoin('round');
this.data.ctx.stroke()
this.data.ctx.draw(true)},Copy the code
5.touchend()
The event empties the array for the next drawing
catchtouchend(e) {
this.data.points = []
},
Copy the code
3. Code test optimization
When the code is run in real time, it is found that the longer the trajectory is drawn, the more it starts to generate stutter:
Because all trace points are drawn each time, more trace points will consume more performance. Therefore, another idea is to connect two points to form a line segment. When the touchStart () event is triggered, it will be the starting point of the first line segment, and when the touchmove() event is triggered, it will be the end point of the first line segment. After the line segment is drawn, this point will be set as the starting point of the next line segment. This obviously solves the caton problem.
catchtouchstart(e) {
if (this.data.drawCount == 0) {
this.data.ctx.draw(false)}this.data.drawCount++
this.data.ctx.moveTo(e.changedTouches[0].clientX, e.changedTouches[0].clientY)
},
catchtouchmove(e) {
if (this.data.drawState == "stop") return
this.data.drawState = "ing"
if (e.touches.length > 1) {
return
}
this.data.ctx.setStrokeStyle('# 000000');
this.data.ctx.setLineWidth(3);
this.data.ctx.setShadow(0.0.0.5.'# 000000')
this.data.ctx.setLineCap('round');
this.data.ctx.setLineJoin('round');
this.data.ctx.lineTo(e.changedTouches[0].clientX, e.changedTouches[0].clientY)
this.data.ctx.stroke()
this.data.ctx.draw(true)
this.data.ctx.moveTo(e.changedTouches[0].clientX, e.changedTouches[0].clientY)
},
Copy the code
Modified effect:
At the end
The solution was to compare the coordinates of changedTouches[0] with those of the last one created, and assume that the same finger touches [0] were created when they were smaller than a certain value. However, the scheme was rejected by the product, and the probability of scene occurrence is low. It is only necessary to judge the single touch before drawing.
The complete code is attached at the end:
<canvas canvas-id="canvas" style="width:{{width+'px'}}; height:{{height+'px'}}" catchtouchstart="catchtouchstart" catchtouchmove="catchtouchmove" catchtouchend="catchtouchend"></canvas>
<view class="btn-reset" catchtap="clearCanvas">redraw</view>
<view class="btn-ok" catchtap="canvasToImg">confirm</view>
Copy the code
Page({
data: {
ctx: null.width: null.height: null.drawCount: 0.drawState: "init"
},
onShow: function () {
this.initCanvas()
},
initCanvas() {
let {
width,
height
} = this.data
width = wx.getSystemInfoSync().windowWidth
height = wx.getSystemInfoSync().windowHeight
console.log(wx.getSystemInfoSync())
this.data.ctx = wx.createCanvasContext('canvas')
this.setData({
width,
height
})
this.clearCanvas()
},
clearCanvas() {
this.data.drawCount = 0
this.data.drawState = "ing"
this.data.ctx.setTextBaseline('top')
this.data.ctx.setTextAlign('center')
this.data.ctx.setFontSize(20)
this.data.ctx.setFillStyle('# 616165');
this.data.ctx.fillText("Please sign in the gray area.".this.data.width / 2.30)
this.data.ctx.draw(false)},catchtouchstart(e) {
if (this.data.drawCount == 0) {
this.data.ctx.draw(false)}this.data.drawCount++
this.data.ctx.moveTo(e.changedTouches[0].clientX, e.changedTouches[0].clientY)
},
catchtouchmove(e) {
if (this.data.drawState == "stop") return
this.data.drawState = "ing"
if (e.touches.length > 1) {
return
}
this.data.ctx.setStrokeStyle('# 000000');
this.data.ctx.setLineWidth(3);
this.data.ctx.setShadow(0.0.0.5.'# 000000')
this.data.ctx.setLineCap('round');
this.data.ctx.setLineJoin('round');
this.data.ctx.lineTo(e.changedTouches[0].clientX, e.changedTouches[0].clientY)
this.data.ctx.stroke()
this.data.ctx.draw(true)
this.data.ctx.moveTo(e.changedTouches[0].clientX, e.changedTouches[0].clientY)
},
canvasToImg() {
if (this.data.drawState == "init") return
this.data.drawState = "stop"
wx.canvasToTempFilePath({
canvasId: 'canvas'.success: res= > {
console.log(res.tempFilePath)
// ...
// Upload server
wx.navigateTo({
url: '/pages/showImg/showImg? src=' + res.tempFilePath,
})
}
})
}
})
Copy the code
page{
position: relative;
background-color: #f2f2f2;
width: 100%;
height: 100%;
}
canvas{
width: 100%;
height: 100vh;
}
.btn-reset{
width: 100rpx;
position: absolute;
bottom: 10rpx;
right: 160rpx;
padding: 8rpx;
text-align: center;
border: 1rpx solid #4965B3;
color: #4965B3;
font-size: 18rpx;
border-radius: 8rpx;
box-sizing: border-box;
}
.btn-ok{
width: 100rpx;
position: absolute;
bottom: 10rpx;
right: 30rpx;
padding: 8rpx;
text-align: center;
background-color: #4965B3;
border: 1rpx solid #4965B3;
color: #fff;
font-size: 18rpx;
border-radius: 8rpx;
box-sizing: border-box;
}
Copy the code