|
|
@@ -0,0 +1,728 @@
|
|
|
+<template>
|
|
|
+ <view class="lime-signature" v-if="show" :style="[canvasStyle, styles]" ref="limeSignature">
|
|
|
+ <!-- #ifndef APP-VUE || APP-NVUE -->
|
|
|
+ <canvas v-if="useCanvas2d" class="lime-signature__canvas" :id="canvasId" type="2d"
|
|
|
+ :disableScroll="disableScroll" @touchstart="touchStart" @touchmove="touchMove"
|
|
|
+ @touchend="touchEnd"></canvas>
|
|
|
+ <canvas v-else :disableScroll="disableScroll" class="lime-signature__canvas" :canvas-id="canvasId"
|
|
|
+ :id="canvasId" :width="canvasWidth" :height="canvasHeight" @touchstart="touchStart" @touchmove="touchMove"
|
|
|
+ @touchend="touchEnd" @mousedown="touchStart" @mousemove="touchMove" @mouseup="touchEnd"></canvas>
|
|
|
+ <canvas v-if="showOffscreen" class="offscreen" canvas-id="offscreen" id="offscreen"
|
|
|
+ :style="'width:' + offscreenSize[0] + 'px;height:' + offscreenSize[1] + 'px'" :width="offscreenSize[0]"
|
|
|
+ :height="offscreenSize[1]">
|
|
|
+ </canvas>
|
|
|
+ <view v-if="showMask" class="mask" @touchstart="touchStart" @touchmove.stop.prevent="touchMove"
|
|
|
+ @touchend="touchEnd"></view>
|
|
|
+ <!-- #endif -->
|
|
|
+ <!-- #ifdef APP-VUE -->
|
|
|
+ <view :id="canvasId" :disableScroll="disableScroll" :rparam="param" :change:rparam="sign.update"
|
|
|
+ :rclear="rclear" :change:rclear="sign.clear" :rundo="rundo" :rredo="rredo" :change:rredo="sign.redo"
|
|
|
+ :change:rundo="sign.undo" :rsave="rsave" :rmask="rmask" :change:rsave="sign.save" :change:rmask="sign.mask"
|
|
|
+ :rdestroy="rdestroy" :change:rdestroy="sign.destroy" :rempty="rempty" :change:rempty="sign.isEmpty">
|
|
|
+ </view>
|
|
|
+ <!-- #endif -->
|
|
|
+ <!-- #ifdef APP-NVUE -->
|
|
|
+ <web-view src="/uni_modules/lime-signature/hybrid/html/index.html" class="lime-signature__canvas" ref="webview"
|
|
|
+ @pagefinish="onPageFinish" @error="onError" @onPostMessage="onMessage"></web-view>
|
|
|
+ <!-- #endif -->
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+ // #ifndef APP-NVUE
|
|
|
+ import {
|
|
|
+ canIUseCanvas2d,
|
|
|
+ wrapEvent,
|
|
|
+ requestAnimationFrame,
|
|
|
+ sleep,
|
|
|
+ isTransparent
|
|
|
+ } from './utils'
|
|
|
+ import {
|
|
|
+ Signature
|
|
|
+ } from './signature.js'
|
|
|
+ // import {Signature} from '@signature';
|
|
|
+ import {
|
|
|
+ uniContext,
|
|
|
+ createImage,
|
|
|
+ toDataURL
|
|
|
+ } from './context'
|
|
|
+ // #endif
|
|
|
+ import props from './props';
|
|
|
+ import {
|
|
|
+ base64ToPath,
|
|
|
+ getRect
|
|
|
+ } from './utils'
|
|
|
+import { nextTick } from 'vue';
|
|
|
+
|
|
|
+ /**
|
|
|
+ * LimeSignature 手写板签名
|
|
|
+ * @description 手写板签名插件:一款能跑在uniapp各端中的签名插件,支持横屏、背景色、笔画颜色、笔画大小等功能,可生成有内容的区域,减小图片尺寸,节省空间。
|
|
|
+ * @property {Number} penSize 画笔大小
|
|
|
+ * @property {Number} minLineWidth 线条最小宽
|
|
|
+ * @property {Number} maxLineWidth 线条最大宽
|
|
|
+ * @property {String} penColor 画笔颜色
|
|
|
+ * @property {String} backgroundColor 背景颜色,不填则为透明
|
|
|
+ * @property {type} 指定 canvas 类型
|
|
|
+ * @value 2d canvas 2d
|
|
|
+ * @value '' 非 canvas 2d 旧接口,微信不再维护
|
|
|
+ * @property {Boolean} openSmooth 模拟笔锋
|
|
|
+ * @property {Number} beforeDelay 延时初始化,在放在弹窗里可以使用 (毫秒)
|
|
|
+ * @property {Number} maxHistoryLength 限制历史记录数,即最大可撤销数,传入0则关闭历史记录功能
|
|
|
+ * @property {Boolean} landscape 横屏,使用后在最后生成图片时会图片旋转90度
|
|
|
+ * @property {Boolean} disableScroll 当在写字时,禁止屏幕滚动以及下拉刷新,nvue无效
|
|
|
+ * @property {Boolean} boundingBox 只生成内容区域,即未画部分不生成,有性能的损耗
|
|
|
+ */
|
|
|
+ export default {
|
|
|
+ props,
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ canvasWidth: null,
|
|
|
+ canvasHeight: null,
|
|
|
+ offscreenWidth: null,
|
|
|
+ offscreenHeight: null,
|
|
|
+ useCanvas2d: true,
|
|
|
+ show: true,
|
|
|
+ offscreenStyles: '',
|
|
|
+ showMask: false,
|
|
|
+ showOffscreen: false,
|
|
|
+ isPC: false,
|
|
|
+ // #ifdef APP-PLUS
|
|
|
+ rclear: 0,
|
|
|
+ rdestroy: 0,
|
|
|
+ rundo: 0,
|
|
|
+ rredo: 0,
|
|
|
+ rsave: JSON.stringify({
|
|
|
+ n: 0,
|
|
|
+ fileType: 'png',
|
|
|
+ quality: 1,
|
|
|
+ destWidth: 0,
|
|
|
+ destHeight: 0,
|
|
|
+ }),
|
|
|
+ rmask: JSON.stringify({
|
|
|
+ n: 0,
|
|
|
+ destWidth: 0,
|
|
|
+ destHeight: 0,
|
|
|
+ }),
|
|
|
+ rempty: 0,
|
|
|
+ risEmpty: true,
|
|
|
+ toDataURL: null,
|
|
|
+ tempFilePath: [],
|
|
|
+ // #endif
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ canvasId() {
|
|
|
+ // #ifdef VUE2
|
|
|
+ return `lime-signature${this._uid}`
|
|
|
+ // #endif
|
|
|
+ // #ifdef VUE3
|
|
|
+ return `lime-signature${this._.uid}`
|
|
|
+ // #endif
|
|
|
+ },
|
|
|
+ offscreenId() {
|
|
|
+ return this.canvasId + 'offscreen'
|
|
|
+ },
|
|
|
+ offscreenSize() {
|
|
|
+ const {
|
|
|
+ offscreenWidth,
|
|
|
+ offscreenHeight
|
|
|
+ } = this
|
|
|
+ return this.landscape ? [offscreenHeight, offscreenWidth] : [offscreenWidth, offscreenHeight]
|
|
|
+ },
|
|
|
+ canvasStyle() {
|
|
|
+ const {
|
|
|
+ canvasWidth,
|
|
|
+ canvasHeight,
|
|
|
+ backgroundColor
|
|
|
+ } = this
|
|
|
+ return {
|
|
|
+ width: canvasWidth && (canvasWidth + 'px'),
|
|
|
+ height: canvasHeight && (canvasHeight + 'px'),
|
|
|
+ background: backgroundColor
|
|
|
+ }
|
|
|
+ },
|
|
|
+ param() {
|
|
|
+ const {
|
|
|
+ penColor,
|
|
|
+ penSize,
|
|
|
+ backgroundColor,
|
|
|
+ backgroundImage,
|
|
|
+ landscape,
|
|
|
+ boundingBox,
|
|
|
+ openSmooth,
|
|
|
+ minLineWidth,
|
|
|
+ maxLineWidth,
|
|
|
+ minSpeed,
|
|
|
+ maxWidthDiffRate,
|
|
|
+ maxHistoryLength,
|
|
|
+ disableScroll,
|
|
|
+ disabled
|
|
|
+ } = this
|
|
|
+ return JSON.parse(JSON.stringify({
|
|
|
+ penColor,
|
|
|
+ penSize,
|
|
|
+ backgroundColor,
|
|
|
+ backgroundImage,
|
|
|
+ landscape,
|
|
|
+ boundingBox,
|
|
|
+ openSmooth,
|
|
|
+ minLineWidth,
|
|
|
+ maxLineWidth,
|
|
|
+ minSpeed,
|
|
|
+ maxWidthDiffRate,
|
|
|
+ maxHistoryLength,
|
|
|
+ disableScroll,
|
|
|
+ disabled
|
|
|
+ }))
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // #ifdef APP-NVUE
|
|
|
+ watch: {
|
|
|
+ param(v) {
|
|
|
+ this.$refs.webview.evalJS(`update(${JSON.stringify(v)})`)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // #endif
|
|
|
+ // #ifndef APP-PLUS
|
|
|
+ created() {
|
|
|
+ const {
|
|
|
+ platform
|
|
|
+ } = uni.getSystemInfoSync()
|
|
|
+ this.isPC = /windows|mac/.test(platform)
|
|
|
+ this.useCanvas2d = this.type == '2d' && canIUseCanvas2d() && !this.isPC
|
|
|
+ // #ifndef H5
|
|
|
+ this.showMask = this.isPC
|
|
|
+ // #endif
|
|
|
+
|
|
|
+ },
|
|
|
+ // #endif
|
|
|
+ // #ifndef APP-PLUS
|
|
|
+ async mounted() {
|
|
|
+ if (this.beforeDelay) {
|
|
|
+ await sleep(this.beforeDelay)
|
|
|
+ }
|
|
|
+ const config = await this.getContext()
|
|
|
+ this.signature = new Signature(config)
|
|
|
+ this.canvasEl = this.signature.canvas.get('el')
|
|
|
+ this.offscreenWidth = this.canvasWidth = this.signature.canvas.get('width')
|
|
|
+ this.offscreenHeight = this.canvasHeight = this.signature.canvas.get('height')
|
|
|
+
|
|
|
+ this.stopWatch = this.$watch('param', (v) => {
|
|
|
+ this.signature.pen.setOption(v)
|
|
|
+ }, {
|
|
|
+ immediate: true
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // #endif
|
|
|
+ // #ifndef APP-PLUS
|
|
|
+ // #ifdef VUE3
|
|
|
+ beforeUnmount() {
|
|
|
+ this.stopWatch && this.stopWatch()
|
|
|
+ this.signature.destroy()
|
|
|
+ this.signature = null
|
|
|
+ this.show = false;
|
|
|
+ // #ifdef APP-VUE || APP-NVUE
|
|
|
+ this.rdestroy++
|
|
|
+ // #endif
|
|
|
+ },
|
|
|
+ // #endif
|
|
|
+ // #ifdef VUE2
|
|
|
+ beforeDestroy() {
|
|
|
+ this.stopWatch && this.stopWatch()
|
|
|
+ this.signature.destroy()
|
|
|
+ this.show = false;
|
|
|
+ this.signature = null
|
|
|
+ // #ifdef APP-VUE || APP-NVUE
|
|
|
+ this.rdestroy++
|
|
|
+ // #endif
|
|
|
+ },
|
|
|
+ // #endif
|
|
|
+ // #endif
|
|
|
+ methods: {
|
|
|
+ // #ifdef MP-QQ
|
|
|
+ // toJSON() { return this },
|
|
|
+ // #endif
|
|
|
+ // #ifdef APP-PLUS
|
|
|
+ onPageFinish() {
|
|
|
+ this.$refs.webview.evalJS(`update(${JSON.stringify(this.param)})`)
|
|
|
+ },
|
|
|
+ onMessage(e = {}) {
|
|
|
+ const {
|
|
|
+ detail: {
|
|
|
+ data: [res]
|
|
|
+ }
|
|
|
+ } = e
|
|
|
+ if (res.event?.save) {
|
|
|
+ this.toDataURL = res.event.save
|
|
|
+ }
|
|
|
+ if (res.event?.changeSize) {
|
|
|
+ const {
|
|
|
+ width,
|
|
|
+ height
|
|
|
+ } = res.event.changeSize
|
|
|
+ }
|
|
|
+ if (res.event.hasOwnProperty('isEmpty')) {
|
|
|
+ this.risEmpty = res.event.isEmpty
|
|
|
+ }
|
|
|
+ if (res.event?.file) {
|
|
|
+ this.tempFilePath.push(res.event.file)
|
|
|
+ if (this.tempFilePath.length > 7) {
|
|
|
+ this.tempFilePath.shift()
|
|
|
+ }
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (res.event?.success) {
|
|
|
+ if (res.event.success) {
|
|
|
+ this.tempFilePath.push(res.event.success)
|
|
|
+ if (this.tempFilePath.length > 8) {
|
|
|
+ this.tempFilePath.shift()
|
|
|
+ }
|
|
|
+ this.toDataURL = this.tempFilePath.join('')
|
|
|
+ this.tempFilePath = []
|
|
|
+ } else {
|
|
|
+ this.$emit('fail', 'canvas no data')
|
|
|
+ }
|
|
|
+ return
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // #endif
|
|
|
+ redo() {
|
|
|
+ // #ifdef APP-VUE || APP-NVUE
|
|
|
+ this.rredo += 1
|
|
|
+ // #endif
|
|
|
+ // #ifdef APP-NVUE
|
|
|
+ this.$refs.webview.evalJS(`redo()`)
|
|
|
+ // #endif
|
|
|
+ // #ifndef APP-VUE
|
|
|
+ if (this.signature)
|
|
|
+ this.signature.redo()
|
|
|
+ // #endif
|
|
|
+ },
|
|
|
+ restore() {
|
|
|
+ this.redo()
|
|
|
+ },
|
|
|
+ undo() {
|
|
|
+ // #ifdef APP-VUE || APP-NVUE
|
|
|
+ this.rundo += 1
|
|
|
+ // #endif
|
|
|
+ // #ifdef APP-NVUE
|
|
|
+ this.$refs.webview.evalJS(`undo()`)
|
|
|
+ // #endif
|
|
|
+ // #ifndef APP-VUE
|
|
|
+ if (this.signature)
|
|
|
+ this.signature.undo()
|
|
|
+ // #endif
|
|
|
+ },
|
|
|
+ clear() {
|
|
|
+ // #ifdef APP-VUE || APP-NVUE
|
|
|
+ this.rclear += 1
|
|
|
+ // #endif
|
|
|
+ // #ifdef APP-NVUE
|
|
|
+ this.$refs.webview.evalJS(`clear()`)
|
|
|
+ // #endif
|
|
|
+ // #ifndef APP-VUE
|
|
|
+ if (this.signature)
|
|
|
+ this.signature.clear()
|
|
|
+ // #endif
|
|
|
+ },
|
|
|
+ isEmpty() {
|
|
|
+ // #ifdef APP-NVUE
|
|
|
+ this.$refs.webview.evalJS(`isEmpty()`)
|
|
|
+ // #endif
|
|
|
+ // #ifdef APP-VUE || APP-NVUE
|
|
|
+ this.rempty += 1
|
|
|
+ // #endif
|
|
|
+ // #ifndef APP-VUE || APP-NVUE
|
|
|
+ return this.signature.isEmpty()
|
|
|
+ // #endif
|
|
|
+ },
|
|
|
+ async canvasToMaskPath(param = {}) {
|
|
|
+ const isEmpty = this.isEmpty()
|
|
|
+ // #ifdef APP-NVUE
|
|
|
+ this.$refs.webview.evalJS(`mask(${JSON.stringify(param)})`)
|
|
|
+ // #endif
|
|
|
+ // #ifdef APP-VUE || APP-NVUE
|
|
|
+ const stopURLWatch = this.$watch('toDataURL', (v, n) => {
|
|
|
+ if (v && v !== n) {
|
|
|
+ // if(param.pathType == 'url') {
|
|
|
+ base64ToPath(v).then(res => {
|
|
|
+ param.success({
|
|
|
+ tempFilePath: res,
|
|
|
+ isEmpty: this.risEmpty
|
|
|
+ })
|
|
|
+ })
|
|
|
+ // } else {
|
|
|
+ // param.success({tempFilePath: v,isEmpty: this.risEmpty })
|
|
|
+ // }
|
|
|
+ this.toDataURL = ''
|
|
|
+ }
|
|
|
+ stopURLWatch && stopURLWatch()
|
|
|
+ })
|
|
|
+ const {
|
|
|
+ fileType,
|
|
|
+ quality
|
|
|
+ } = param
|
|
|
+ const rmask = JSON.parse(this.rmask)
|
|
|
+ rmask.n++
|
|
|
+ rmask.destWidth = param.destWidth ?? 0
|
|
|
+ rmask.destHeight = param.destHeight ?? 0
|
|
|
+ // rmask.fileType = fileType
|
|
|
+ // rmask.quality = quality
|
|
|
+ this.rmask = JSON.stringify(rmask)
|
|
|
+ // #endif
|
|
|
+ // #ifndef APP-VUE || APP-NVUE
|
|
|
+ this.showOffscreen = true
|
|
|
+
|
|
|
+ let width = this.signature.canvas.get('width')
|
|
|
+ let height = this.signature.canvas.get('height')
|
|
|
+ let {
|
|
|
+ pixelRatio
|
|
|
+ } = uni.getSystemInfoSync()
|
|
|
+ if (this.useCanvas2d) {
|
|
|
+ this.offscreenWidth = width * pixelRatio
|
|
|
+ this.offscreenHeight = height * pixelRatio
|
|
|
+ } else {
|
|
|
+ this.offscreenWidth = width
|
|
|
+ this.offscreenHeight = height
|
|
|
+ }
|
|
|
+ await sleep(100)
|
|
|
+ const context = uni.createCanvasContext('offscreen', this)
|
|
|
+ const size = Math.max(this.offscreenWidth, this.offscreenHeight)
|
|
|
+ const success = (success) => param.success && param.success(success)
|
|
|
+ const fail = (fail) => param.fail && param.fail(fail)
|
|
|
+
|
|
|
+ this.signature.pen.getMaskedImageData((imageData) => {
|
|
|
+ let canvasPutImageData = (options, comp) => {
|
|
|
+ if (uni.canvasPutImageData) {
|
|
|
+ uni.canvasPutImageData(options, comp)
|
|
|
+ } else if (context.putImageData) {
|
|
|
+ context.putImageData(options)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ canvasPutImageData({
|
|
|
+ canvasId: 'offscreen',
|
|
|
+ x: 0,
|
|
|
+ y: 0,
|
|
|
+ width: width,
|
|
|
+ height:height,
|
|
|
+ data: imageData,
|
|
|
+ fail(err) {
|
|
|
+ fail(err)
|
|
|
+ },
|
|
|
+ success: (re) => {
|
|
|
+ toDataURL('offscreen', this, param).then((res) => {
|
|
|
+ context.restore()
|
|
|
+ context.clearRect(0, 0, size, size)
|
|
|
+ this.offscreenWidth = width
|
|
|
+ this.offscreenHeight = height
|
|
|
+ this.showOffscreen = false
|
|
|
+ success({
|
|
|
+ tempFilePath: res,
|
|
|
+ isEmpty
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }, this)
|
|
|
+
|
|
|
+ })
|
|
|
+ // #endif
|
|
|
+ },
|
|
|
+ canvasToTempFilePath(param = {}) {
|
|
|
+
|
|
|
+ const isEmpty = this.isEmpty()
|
|
|
+ // #ifdef APP-NVUE
|
|
|
+ this.$refs.webview.evalJS(`save(${JSON.stringify(param)})`)
|
|
|
+ // #endif
|
|
|
+ // #ifdef APP-VUE || APP-NVUE
|
|
|
+ const stopURLWatch = this.$watch('toDataURL', (v, n) => {
|
|
|
+ if (v && v !== n) {
|
|
|
+ if (this.preferToDataURL) {
|
|
|
+ param.success({
|
|
|
+ tempFilePath: v,
|
|
|
+ isEmpty: this.risEmpty
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ base64ToPath(v).then(res => {
|
|
|
+ param.success({
|
|
|
+ tempFilePath: res,
|
|
|
+ isEmpty: this.risEmpty
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ this.toDataURL = ''
|
|
|
+ }
|
|
|
+ stopURLWatch && stopURLWatch()
|
|
|
+ })
|
|
|
+ const {
|
|
|
+ fileType,
|
|
|
+ quality
|
|
|
+ } = param
|
|
|
+ const rsave = JSON.parse(this.rsave)
|
|
|
+ rsave.n++
|
|
|
+ rsave.fileType = fileType
|
|
|
+ rsave.quality = quality
|
|
|
+ rsave.destWidth = param.destWidth ?? 0
|
|
|
+ rsave.destHeight = param.destHeight ?? 0
|
|
|
+ this.rsave = JSON.stringify(rsave)
|
|
|
+ // #endif
|
|
|
+ // #ifndef APP-VUE || APP-NVUE
|
|
|
+ const useCanvas2d = this.useCanvas2d
|
|
|
+ const success = (success) => param.success && param.success(success)
|
|
|
+ const fail = (err) => param.fail && param.fail(err)
|
|
|
+ const {
|
|
|
+ canvas
|
|
|
+ } = this.signature.canvas.get('el')
|
|
|
+ const {
|
|
|
+ backgroundColor,
|
|
|
+ landscape,
|
|
|
+ boundingBox
|
|
|
+ } = this
|
|
|
+ let width = this.signature.canvas.get('width')
|
|
|
+ let height = this.signature.canvas.get('height')
|
|
|
+ let x = 0
|
|
|
+ let y = 0
|
|
|
+ const devtools = uni.getSystemInfoSync().platform == 'devtools'
|
|
|
+ let preferToDataURL = this.preferToDataURL
|
|
|
+ let scale = 1
|
|
|
+ // #ifdef MP-TOUTIAO
|
|
|
+ scale = devtools ? uni.getSystemInfoSync().pixelRatio : scale
|
|
|
+ // 由于抖音不支持canvasToTempFilePath故优先使用createOffscreenCanvas
|
|
|
+ preferToDataURL = true
|
|
|
+ // #endif
|
|
|
+ const canvasToTempFilePath = async (image) => {
|
|
|
+ const createCanvasContext = () => {
|
|
|
+ const useOffscreen = (useCanvas2d && !!uni.createOffscreenCanvas && preferToDataURL)
|
|
|
+ if (useOffscreen && !devtools) {
|
|
|
+ const offCanvas = uni.createOffscreenCanvas({
|
|
|
+ type: '2d'
|
|
|
+ });
|
|
|
+ offCanvas.width = this.offscreenSize[0] * scale
|
|
|
+ offCanvas.height = this.offscreenSize[1] * scale
|
|
|
+ const context = offCanvas.getContext("2d");
|
|
|
+ return [context, offCanvas]
|
|
|
+ } else {
|
|
|
+ const context = uni.createCanvasContext('offscreen', this)
|
|
|
+ return [context]
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (boundingBox && !this.isPC || landscape || backgroundColor && !isTransparent(backgroundColor)) {
|
|
|
+
|
|
|
+ this.showOffscreen = true
|
|
|
+ await sleep(100)
|
|
|
+ const [context, offCanvas] = createCanvasContext()
|
|
|
+ context.save()
|
|
|
+ context.setTransform(1, 0, 0, 1, 0, 0)
|
|
|
+ if (landscape) {
|
|
|
+ context.translate(0, width * scale)
|
|
|
+ context.rotate(-Math.PI / 2)
|
|
|
+ }
|
|
|
+ if (backgroundColor && !isTransparent(backgroundColor)) {
|
|
|
+ context.fillStyle = backgroundColor
|
|
|
+ context.fillRect(0, 0, width, height)
|
|
|
+ }
|
|
|
+ if (offCanvas) {
|
|
|
+ const img = canvas.createImage();
|
|
|
+ img.src = image
|
|
|
+ img.onload = () => {
|
|
|
+ context.drawImage(img, 0, 0, width * scale, height * scale);
|
|
|
+ const tempFilePath = offCanvas.toDataURL()
|
|
|
+ this.showOffscreen = false
|
|
|
+ success({
|
|
|
+ tempFilePath,
|
|
|
+ isEmpty
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ context.drawImage(image, 0, 0, width * scale, height * scale);
|
|
|
+ context.draw(false, () => {
|
|
|
+ toDataURL('offscreen', this, param).then((res) => {
|
|
|
+ const size = Math.max(width, height)
|
|
|
+ context.restore()
|
|
|
+ context.clearRect(0, 0, size, size)
|
|
|
+ this.showOffscreen = false
|
|
|
+ success({
|
|
|
+ tempFilePath: res,
|
|
|
+ isEmpty
|
|
|
+ })
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ success({
|
|
|
+ tempFilePath: image,
|
|
|
+ isEmpty
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const next = async () => {
|
|
|
+ if (this.offscreenWidth != width || this.offscreenHeight != height) {
|
|
|
+ this.offscreenWidth = width
|
|
|
+ this.offscreenHeight = height
|
|
|
+ await sleep(100)
|
|
|
+ }
|
|
|
+
|
|
|
+ // #ifndef MP-WEIXIN
|
|
|
+ const param = {
|
|
|
+ x,
|
|
|
+ y,
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ canvas,
|
|
|
+ preferToDataURL
|
|
|
+ }
|
|
|
+ // #endif
|
|
|
+
|
|
|
+ // #ifdef MP-WEIXIN
|
|
|
+ const param = {
|
|
|
+ x,
|
|
|
+ y,
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ canvas: useCanvas2d ? canvas : null,
|
|
|
+ preferToDataURL
|
|
|
+ }
|
|
|
+ // #endif
|
|
|
+ toDataURL(this.canvasId, this, param).then(canvasToTempFilePath).catch(fail)
|
|
|
+ }
|
|
|
+ // PC端小程序获取不到 ImageData 数据,长度为0
|
|
|
+ if (boundingBox && !this.isPC) {
|
|
|
+ this.signature.getContentBoundingBox(async res => {
|
|
|
+ this.offscreenWidth = width = res.width
|
|
|
+ this.offscreenHeight = height = res.height
|
|
|
+
|
|
|
+ x = res.startX
|
|
|
+ y = res.startY
|
|
|
+
|
|
|
+ next()
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ next()
|
|
|
+ }
|
|
|
+ // #endif
|
|
|
+ },
|
|
|
+ // #ifndef APP-PLUS
|
|
|
+ getContext() {
|
|
|
+ return getRect(`#${this.canvasId}`, {
|
|
|
+ context: this,
|
|
|
+ type: this.useCanvas2d ? 'fields' : 'boundingClientRect'
|
|
|
+ }).then(res => {
|
|
|
+ if (res) {
|
|
|
+ let {
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ node: canvas,
|
|
|
+ left,
|
|
|
+ top,
|
|
|
+ right
|
|
|
+ } = res
|
|
|
+ let {
|
|
|
+ pixelRatio
|
|
|
+ } = uni.getSystemInfoSync()
|
|
|
+ let context;
|
|
|
+ if (canvas) {
|
|
|
+ context = canvas.getContext('2d')
|
|
|
+ canvas.width = width * pixelRatio;
|
|
|
+ canvas.height = height * pixelRatio;
|
|
|
+ } else {
|
|
|
+ pixelRatio = 1
|
|
|
+ context = uniContext(this.canvasId, this)
|
|
|
+ canvas = {
|
|
|
+ getContext: (type) => type == '2d' ? context : null,
|
|
|
+ createImage,
|
|
|
+ toDataURL: () => toDataURL(this.canvasId, this),
|
|
|
+ requestAnimationFrame
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 支付宝小程序 使用stroke有个默认背景色
|
|
|
+ context.clearRect(0, 0, width, height)
|
|
|
+ return {
|
|
|
+ left,
|
|
|
+ top,
|
|
|
+ right,
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ context,
|
|
|
+ canvas,
|
|
|
+ pixelRatio
|
|
|
+ };
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ getTouch(e) {
|
|
|
+ if (this.isPC && this.canvasRect) {
|
|
|
+ e.touches = e.touches.map(item => {
|
|
|
+ return {
|
|
|
+ ...item,
|
|
|
+ x: item.clientX - this.canvasRect.left,
|
|
|
+ y: item.clientY - this.canvasRect.top,
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ return e
|
|
|
+ },
|
|
|
+ touchStart(e) {
|
|
|
+ if (!this.canvasEl) return
|
|
|
+ this.isStart = true
|
|
|
+ // 微信小程序PC端不支持事件,使用这方法模拟一下
|
|
|
+ if (this.isPC) {
|
|
|
+ getRect(`#${this.canvasId}`, {
|
|
|
+ context: this
|
|
|
+ }).then(res => {
|
|
|
+ this.canvasRect = res
|
|
|
+ this.canvasEl.dispatchEvent('touchstart', wrapEvent(this.getTouch(e)))
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.canvasEl.dispatchEvent('touchstart', wrapEvent(e))
|
|
|
+ },
|
|
|
+ touchMove(e) {
|
|
|
+ if (!this.canvasEl || !this.isStart && this.canvasEl) return
|
|
|
+ this.canvasEl.dispatchEvent('touchmove', wrapEvent(this.getTouch(e)))
|
|
|
+ },
|
|
|
+ touchEnd(e) {
|
|
|
+ if (!this.canvasEl) return
|
|
|
+ this.isStart = false
|
|
|
+ this.canvasEl.dispatchEvent('touchend', wrapEvent(e))
|
|
|
+ },
|
|
|
+ // #endif
|
|
|
+ }
|
|
|
+ }
|
|
|
+</script>
|
|
|
+<!-- #ifdef APP-VUE -->
|
|
|
+<script module="sign" lang="renderjs">
|
|
|
+ import sign from './render'
|
|
|
+ export default sign
|
|
|
+</script>
|
|
|
+<!-- #endif -->
|
|
|
+<style lang="scss">
|
|
|
+ .lime-signature,
|
|
|
+ .lime-signature__canvas {
|
|
|
+ /* #ifndef APP-NVUE */
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ /* #endif */
|
|
|
+ /* #ifdef APP-NVUE */
|
|
|
+ flex: 1;
|
|
|
+ /* #endif */
|
|
|
+ }
|
|
|
+
|
|
|
+ .mask {
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ top: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .offscreen {
|
|
|
+ position: fixed;
|
|
|
+ top: 0;
|
|
|
+ // left: 0;
|
|
|
+ pointer-events:none;
|
|
|
+ // background: rgba(0,255,0,0.5);
|
|
|
+ left: 9999px;
|
|
|
+ }
|
|
|
+</style>
|