| | |
| | | <canvas |
| | | class="drawing" |
| | | ref="drawing" |
| | | :canvasId="cavId" |
| | | :width="cavWidth" |
| | | :height="cavHeight" |
| | | type="2d" |
| | | :id="cavId" |
| | | :disableScroll="true" |
| | | v-if="cavShow" |
| | | @touchstart="evt => handleWriteStart(evt)" |
| | | @touchmove="evt => handleWriteMove(evt)" |
| | | @touchend="evt => handleWriteEnd(evt)" |
| | |
| | | import Taro from '@tarojs/taro'; |
| | | import { $ } from '@tarojs/extend'; |
| | | import { AtFloatLayout, AtButton } from 'taro-ui-vue'; |
| | | import './cSignatureLayer.scss'; |
| | | import { Tools } from '@components/common/Tools'; |
| | | import './cSignatureLayer.scss'; |
| | | |
| | | export default { |
| | | name: 'CSignatureLayer', |
| | |
| | | return { |
| | | cavId: 'signCanvas-' + Date.now() + '-' + parseInt(Math.random() * 10000), |
| | | layerOpened: false, |
| | | cavWidth: 0, |
| | | cavHeight: 0, |
| | | cavShow: false, |
| | | curPoint: {}, |
| | | lastPoint: {}, |
| | | curLine: [], |
| | |
| | | chirography: [], |
| | | // 初始画圆的半径 |
| | | radius: 1, |
| | | canvasContext: null, |
| | | }; |
| | | }, |
| | | methods: { |
| | | _initDraw() { |
| | | this.canvasContext = Taro.createCanvasContext(this.cavId, this); |
| | | const $container = $(this.$refs.drawing).parent(); |
| | | setTimeout(() => { |
| | | $container.width().then(w => (this.cavWidth = w)); |
| | | $container.height().then(h => (this.cavHeight = h)); |
| | | }, 0); |
| | | const query = Taro.createSelectorQuery(); |
| | | query |
| | | .select(`#${this.cavId}`) |
| | | .fields({ node: true, size: true }) |
| | | .exec(res => { |
| | | // Canvas 对象 |
| | | const canvas = res[0].node; |
| | | // Canvas 画布的实际绘制宽高 |
| | | const renderWidth = res[0].width; |
| | | const renderHeight = res[0].height; |
| | | // Canvas 绘制上下文 |
| | | this.canvasContext = canvas.getContext('2d'); |
| | | this.canvas = canvas; |
| | | // 初始化画布大小 |
| | | const dpr = Taro.getSystemInfoSync().pixelRatio; |
| | | canvas.width = renderWidth * dpr; |
| | | canvas.height = renderHeight * dpr; |
| | | this.canvasContext.scale(dpr, dpr); |
| | | // 初始化变量 |
| | | this.handleRestDraw(); |
| | | }); |
| | | }, |
| | | $onDraw(callback) { |
| | | this._initDraw(); |
| | | this.layerOpened = true; |
| | | this._callback = callback; |
| | | setTimeout(() => { |
| | | this.handleRestDraw(); |
| | | }, 10); |
| | | this.$nextTick(() => { |
| | | setTimeout(() => { |
| | | this.cavShow = true; |
| | | setTimeout(() => { |
| | | this._initDraw(); |
| | | }, 100); |
| | | }, 300); |
| | | }); |
| | | }, |
| | | handleRestDraw() { |
| | | this.firstTouch = true; |
| | | this.curLine = []; |
| | | this.chirography = []; |
| | | this.canvasContext.clearRect(0, 0, this.cavWidth, this.cavHeight); |
| | | this.canvasContext.setFillStyle('#ffffff'); |
| | | this.canvasContext.fillRect(0, 0, this.cavWidth, this.cavHeight); |
| | | this.canvasContext.draw(); |
| | | this.canvasContext.clearRect(0, 0, this.canvas.width, this.canvas.height); |
| | | }, |
| | | handleClose() { |
| | | this.layerOpened = false; |
| | | this.cavShow = false; |
| | | setTimeout(() => { |
| | | this.canvasContext.clearRect(0, 0, this.canvas.width, this.canvas.height); |
| | | }, 100); |
| | | }, |
| | | handleWriteStart(evt) { |
| | | if (evt.type != 'touchstart') { |
| | | return false; |
| | | if (evt.type !== 'touchstart' || !this.canvasContext) { |
| | | return; |
| | | } |
| | | this.canvasContext.setFillStyle('#1A1A1A'); |
| | | this.canvasContext.fillStyle = '#111111'; |
| | | const point = { |
| | | x: evt.touches[0].x, |
| | | y: evt.touches[0].y, |
| | |
| | | this._pointToLine(this.curLine); |
| | | }, |
| | | handleWriteMove(evt) { |
| | | if (evt.type != 'touchmove') { |
| | | return false; |
| | | if (evt.type !== 'touchmove') { |
| | | return; |
| | | } |
| | | if (evt.cancelable) { |
| | | // 判断默认行为是否已经被禁用 |
| | |
| | | this._pointToLine(this.curLine); |
| | | }, |
| | | handleWriteEnd(evt) { |
| | | if (evt.type != 'touchend') { |
| | | return 0; |
| | | if (evt.type !== 'touchend') { |
| | | return; |
| | | } |
| | | const point = { |
| | | x: evt.changedTouches[0].x, |
| | |
| | | if (point.x > this.cutArea.right) { |
| | | this.cutArea.right = point.x; |
| | | } |
| | | if (this.cavWidth - point.x <= 0) { |
| | | this.cutArea.right = this.cavWidth; |
| | | if (this.canvas.width - point.x <= 0) { |
| | | this.cutArea.right = this.canvas.width; |
| | | } |
| | | if (point.y > this.cutArea.bottom) { |
| | | this.cutArea.bottom = point.y; |
| | | } |
| | | if (this.cavHeight - point.y <= 0) { |
| | | this.cutArea.bottom = this.cavHeight; |
| | | if (this.canvas.height - point.y <= 0) { |
| | | this.cutArea.bottom = this.canvas.height; |
| | | } |
| | | if (point.x < this.cutArea.left) { |
| | | this.cutArea.left = point.x; |
| | |
| | | r2 = (line[1].r + line[0].r) / 2; |
| | | } |
| | | let n = 5; |
| | | let point = []; |
| | | let points = []; |
| | | 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.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, |
| | | points.push({ x: x, y: y, r: r }); |
| | | if (points.length == 3) { |
| | | this._bethelDraw( |
| | | this._ctaCalc( |
| | | points[0].x, |
| | | points[0].y, |
| | | points[0].r, |
| | | points[1].x, |
| | | points[1].y, |
| | | points[1].r, |
| | | points[2].x, |
| | | points[2].y, |
| | | points[2].r, |
| | | ), |
| | | ); |
| | | this._bethelDraw(a, true); |
| | | point = [{ x: x, y: y, r: r }]; |
| | | points = [{ x: x, y: y, r: r }]; |
| | | } |
| | | } |
| | | this.curLine = line; |
| | |
| | | } |
| | | return a; |
| | | }, |
| | | _bethelDraw(point, isFill) { |
| | | _bethelDraw(points) { |
| | | const ctx = this.canvasContext; |
| | | ctx.beginPath(); |
| | | ctx.moveTo(point[0].mx, point[0].my); |
| | | for (let i = 1; i < point.length; i++) { |
| | | ctx.moveTo(points[0].mx, points[0].my); |
| | | for (let i = 1; i < points.length; i++) { |
| | | ctx.bezierCurveTo( |
| | | point[i].c1x, |
| | | point[i].c1y, |
| | | point[i].c2x, |
| | | point[i].c2y, |
| | | point[i].ex, |
| | | point[i].ey, |
| | | points[i].c1x, |
| | | points[i].c1y, |
| | | points[i].c2x, |
| | | points[i].c2y, |
| | | points[i].ex, |
| | | points[i].ey, |
| | | ); |
| | | } |
| | | ctx.closePath(); |
| | | ctx.stroke(); |
| | | if (isFill !== undefined) { |
| | | // 后绘制的图形会覆盖前面的图形, 绘制时注意先后顺序 |
| | | ctx.fill(); |
| | | } |
| | | ctx.draw(true); |
| | | ctx.fill(); |
| | | }, |
| | | _brushingGround(callback) { |
| | | Taro.canvasToTempFilePath({ |
| | | canvas: this.canvas, |
| | | x: 0, |
| | | y: 0, |
| | | width: Math.ceil(this.canvas.width), |
| | | height: Math.ceil(this.canvas.height), |
| | | destWidth: Math.ceil(this.canvas.width), |
| | | destHeight: Math.ceil(this.canvas.height), |
| | | quality: 1, |
| | | fileType: 'png', |
| | | success: res => { |
| | | const ctx = this.canvasContext; |
| | | ctx.fillStyle = '#ffffff'; |
| | | ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); |
| | | // 重新绘制 |
| | | const image = this.canvas.createImage(); |
| | | image.onload = () => { |
| | | const dpr = Taro.getSystemInfoSync().pixelRatio; |
| | | ctx.drawImage( |
| | | image, |
| | | 0, |
| | | 0, |
| | | this.canvas.width / dpr, |
| | | this.canvas.height / dpr, |
| | | ); |
| | | callback(); |
| | | }; |
| | | image.src = res.tempFilePath; |
| | | }, |
| | | }); |
| | | }, |
| | | handleSaveDraw() { |
| | | if (this.firstTouch) { |
| | | Tools.toast('请书写签名!'); |
| | | return; |
| | | } |
| | | const delta = 20; |
| | | const clipArea = { x: 0, y: 0, w: 0, h: 0 }; |
| | | clipArea.x = Math.max(this.cutArea.left - delta, 0); |
| | | clipArea.y = Math.max(this.cutArea.top - delta, 0); |
| | | const realRight = Math.min(this.cutArea.right + delta, this.cavWidth); |
| | | const realBottom = Math.min(this.cutArea.bottom + delta, this.cavHeight); |
| | | clipArea.w = realRight - clipArea.x; |
| | | clipArea.h = realBottom - clipArea.y; |
| | | Taro.canvasToTempFilePath({ |
| | | canvasId: this.cavId, |
| | | x: clipArea.x, |
| | | y: clipArea.y, |
| | | width: clipArea.w, |
| | | height: clipArea.h, |
| | | destWidth: clipArea.w, |
| | | destHeight: clipArea.h, |
| | | quality: 0.6, |
| | | fileType: 'jpeg', |
| | | success: res => { |
| | | this._callback(res.tempFilePath); |
| | | this.layerOpened = false; |
| | | }, |
| | | this._brushingGround(() => { |
| | | const delta = 20; |
| | | const clipArea = { x: 0, y: 0, w: 0, h: 0 }; |
| | | clipArea.x = Math.max(this.cutArea.left - delta, 0); |
| | | clipArea.y = Math.max(this.cutArea.top - delta, 0); |
| | | const realRight = Math.min(this.cutArea.right + delta, this.canvas.width); |
| | | const realBottom = Math.min( |
| | | this.cutArea.bottom + delta, |
| | | this.canvas.height, |
| | | ); |
| | | clipArea.w = realRight - clipArea.x; |
| | | clipArea.h = realBottom - clipArea.y; |
| | | Taro.canvasToTempFilePath({ |
| | | canvas: this.canvas, |
| | | x: clipArea.x, |
| | | y: clipArea.y, |
| | | width: clipArea.w, |
| | | height: clipArea.h, |
| | | destWidth: clipArea.w, |
| | | destHeight: clipArea.h, |
| | | quality: 0.6, |
| | | fileType: 'jpeg', |
| | | success: res => { |
| | | this._callback(res.tempFilePath); |
| | | this.layerOpened = false; |
| | | }, |
| | | }); |
| | | }); |
| | | }, |
| | | }, |
| | | mounted() { |
| | | this.$nextTick(() => { |
| | | this._initDraw(); |
| | | }); |
| | | }, |
| | | mounted() {}, |
| | | }; |
| | | </script> |