小程序 canvas type=2d 来做画画板,有橡皮擦,选颜色,选粗细,撤销和还原
canvas api 文档:https://www.canvasapi.cn/
组件里的 canvas
<canvas class="handWriting" id="handWriting" canvas-id="handWriting" type="2d" disable-scroll="true" @touchstart="uploadScaleStart" @touchmove="uploadScaleMove" @touchend="uploadScaleEnd" @touchcancel="uploadScaleEnd" @tap.stop />
this.handwriting = new Handwriting( { canvasName: 'handWriting', }, this )
import { systemObj, loadImgSync, canvasToUrlSync } from '../../utils/utils' class Handwriting { // 内置数据 ctx: any canvas: any canvasWidth = 300 canvasHeight = 600 canvasSaveImgs: any = [] drawCurrent = 0 beginPoint: any = {} points: any = [] canvasName = '' that = {} firstTouch = true // 第一次触发 lineColorVal = '' lineColorAlpha = 1 lineSize = 10 isClear = false // 橡皮擦 constructor(opts: any, _this: any) { this.canvasName = opts.canvasName || 'handWriting' this.that = _this this.init() } init() { const query = uni.createSelectorQuery().in(this.that) const config = { node: true, size: true } query .select(`#${this.canvasName}`) .fields(config, (res: any) => { // console.log('canvas init:=>', res, systemObj) const canvas = res.node const ctx = canvas.getContext('2d') const { pixelRatio: dpr } = systemObj const width = res.width * dpr const height = res.height * dpr canvas.width = width canvas.height = height ctx.scale(dpr, dpr) this.ctx = ctx this.canvas = canvas this.canvasWidth = width this.canvasHeight = height // ctx.fillRect(10, 10, 33, 33) // test }) .exec() } // 笔迹开始 uploadScaleStart(event: any) { // console.log('start', event) const e = event.mp if (e.type !== 'touchstart') return false const { x, y } = e.touches[0] this.points.push({ x, y }) this.beginPoint = { x, y } this.ctx.strokeStyle = `rgba(${this.lineColorVal}, ${this.lineColorAlpha})` this.ctx.lineWidth = this.lineSize if (systemObj.platform !== 'devtools') { this.ctx.lineCap = 'round' let type = 'source-over' this.isClear && (type = 'destination-out') this.ctx.globalCompositeOperation = type } // if (this.firstTouch) { // this.firstTouch = false // } } drawLine(beginPoint: any, controlPoint: any, endPoint: any) { this.ctx.beginPath() this.ctx.moveTo(beginPoint.x, beginPoint.y) this.ctx.quadraticCurveTo(controlPoint.x, controlPoint.y, endPoint.x, endPoint.y) this.ctx.stroke() this.ctx.closePath() } // 笔迹移动 uploadScaleMove(event: any) { // console.log('move') let e = event.mp if (e.type !== 'touchmove') return false if (e.cancelable) { // 判断默认行为是否已经被禁用 if (!e.defaultPrevented) { e.preventDefault() } } const { x, y } = e.touches[0] this.points.push({ x, y }) if (this.points.length > 3) { const lastTwoPoints = this.points.slice(-2) const controlPoint = lastTwoPoints[0] const endPoint = { x: (lastTwoPoints[0].x + lastTwoPoints[1].x) / 2, y: (lastTwoPoints[0].y + lastTwoPoints[1].y) / 2, } this.drawLine(this.beginPoint, controlPoint, endPoint) this.beginPoint = endPoint } } // 笔迹结束 async uploadScaleEnd(event: any) { const e = event.mp if (e.type != 'touchend') return 0 const { x, y } = e.changedTouches[0] this.points.push({ x, y }) if (this.points.length > 3) { const lastTwoPoints = this.points.slice(-2) const controlPoint = lastTwoPoints[0] const endPoint = lastTwoPoints[1] this.drawLine(this.beginPoint, controlPoint, endPoint) } this.beginPoint = null this.points = [] await this.drawEnd() } async getCanvasUrl() { return await canvasToUrlSync(this.canvas) } async drawEnd() { const saveSteps = this.canvasSaveImgs.length if (this.drawCurrent > 0) { this.canvasSaveImgs.length = saveSteps - this.drawCurrent this.drawCurrent = 0 } this.canvasSaveImgs.push(await this.getCanvasUrl()) } selectColorEvent(val: any) { this.lineColorVal = val this.isClear = false } selectLineSize(val: any) { this.lineSize = val } clearEvent() { this.isClear = true } noClearEvent() { this.isClear = false } clearAllCanvas() { this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight) } async stepChange(type: string) { const saveSteps = this.canvasSaveImgs.length if (type === 'last') { if (this.drawCurrent < saveSteps) { this.drawCurrent++ } else { return } } if (type === 'next') { if (this.drawCurrent > 0) { this.drawCurrent-- } else { return } } this.clearAllCanvas() this.ctx.globalCompositeOperation = 'source-over' if (this.drawCurrent === saveSteps) { return } const index = saveSteps - this.drawCurrent - 1 const { pixelRatio: dpr } = systemObj const img: any = await loadImgSync(this.canvas, this.canvasSaveImgs[index]) this.ctx.drawImage( img, 0, 0, this.canvasWidth, this.canvasHeight, 0, 0, this.canvasWidth / dpr, this.canvasHeight / dpr ) } getHelpfulData() { return { drawCurrent: this.drawCurrent, saveSteps: this.canvasSaveImgs.length, } } } export default Handwriting
utils.js
export function loadImgSync(canvas: any, url: string) { const img = canvas.createImage() img.src = url return new Promise((resolve, reject) => { img.onload = () => { resolve(img) } }) } export function canvasToUrlSync(canvas: any) { return new Promise((resolve, reject) => { wx.canvasToTempFilePath({ canvas, success: async (res: any) => { // console.log('tools-url:>', res.tempFilePath) // uni.previewImage({ // urls: [res.tempFilePath] // }) resolve(res.tempFilePath) }, fail: (err) => { console.log('图片生成失败:' + JSON.stringify(err)) reject() }, }) }) }
export const systemInfo = uni.getSystemInfoSync()