383 lines
9.8 KiB
Vue
383 lines
9.8 KiB
Vue
|
<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
|
||
|
class="offscreen"
|
||
|
canvas-id="offscreen"
|
||
|
id="offscreen"
|
||
|
:style="'width:' + offscreenSize[0] + 'px;height:' + offscreenSize[1] + 'px'"
|
||
|
:width="offscreenSize[0]"
|
||
|
:height="offscreenSize[1]">
|
||
|
</canvas>
|
||
|
<!-- #endif -->
|
||
|
<!-- #ifdef APP-VUE -->
|
||
|
<view
|
||
|
:id="canvasId"
|
||
|
:disableScroll="disableScroll"
|
||
|
:rparam="param"
|
||
|
:change:rparam="sign.update"
|
||
|
|
||
|
:rclear="rclear"
|
||
|
:change:rclear="sign.clear"
|
||
|
|
||
|
:rundo="rundo"
|
||
|
:change:rundo="sign.undo"
|
||
|
|
||
|
:rsave="rsave"
|
||
|
:change:rsave="sign.save"
|
||
|
|
||
|
:rempty="rempty"
|
||
|
:change:rempty="sign.isEmpty"
|
||
|
|
||
|
></view>
|
||
|
<!-- #endif -->
|
||
|
<!-- #ifdef APP-NVUE -->
|
||
|
<web-view
|
||
|
src="/uni_modules/lime-signature/static/index.html"
|
||
|
class="lime-signature__canvas"
|
||
|
ref="webview"
|
||
|
@pagefinish="onPageFinish"
|
||
|
@error="onError"
|
||
|
@onPostMessage="onMessage"
|
||
|
></web-view>
|
||
|
<!-- #endif -->
|
||
|
</view>
|
||
|
</template>
|
||
|
<!-- #ifdef APP-VUE -->
|
||
|
<script module="sign" lang="renderjs">
|
||
|
export {default} from './render'
|
||
|
</script>
|
||
|
<!-- #endif -->
|
||
|
|
||
|
<script>
|
||
|
// #ifndef APP-NVUE
|
||
|
import {canIUseCanvas2d, wrapEvent, requestAnimationFrame, sleep} from './utils'
|
||
|
import {Signature} from './signature'
|
||
|
// import {Signature} from '@signature';
|
||
|
import {uniContext, createImage, toDataURL} from './context'
|
||
|
// #endif
|
||
|
import props from './props';
|
||
|
import {base64ToPath, getRect} from './utils'
|
||
|
export default {
|
||
|
props,
|
||
|
data() {
|
||
|
return {
|
||
|
canvasWidth: null,
|
||
|
canvasHeight: null,
|
||
|
useCanvas2d: true,
|
||
|
show: true,
|
||
|
offscreenStyles: '',
|
||
|
// #ifdef APP-PLUS
|
||
|
rclear: 0,
|
||
|
rundo: 0,
|
||
|
rsave: 0,
|
||
|
rempty: 0,
|
||
|
risEmpty: true,
|
||
|
toDataURL: null,
|
||
|
tempFilePath: [],
|
||
|
// #endif
|
||
|
}
|
||
|
},
|
||
|
computed: {
|
||
|
canvasId() {
|
||
|
return `lime-signature${this._uid||this._.uid}`
|
||
|
},
|
||
|
offscreenId() {
|
||
|
return this.canvasId + 'offscreen'
|
||
|
},
|
||
|
offscreenSize() {
|
||
|
const {canvasWidth, canvasHeight} = this
|
||
|
return this.landscape ? [canvasHeight, canvasWidth] : [canvasWidth, canvasHeight]
|
||
|
},
|
||
|
canvasStyle() {
|
||
|
const {canvasWidth, canvasHeight, backgroundColor} = this
|
||
|
return {
|
||
|
width: canvasWidth && (canvasWidth + 'px'),
|
||
|
height: canvasHeight && (canvasHeight + 'px'),
|
||
|
background: backgroundColor
|
||
|
}
|
||
|
},
|
||
|
param() {
|
||
|
const {penColor, penSize, backgroundColor, landscape, openSmooth, minLineWidth, maxLineWidth, minSpeed, maxWidthDiffRate, maxHistoryLength, disableScroll} = this
|
||
|
return JSON.parse(JSON.stringify({penColor, penSize, backgroundColor, landscape, openSmooth, minLineWidth, maxLineWidth, minSpeed, maxWidthDiffRate, maxHistoryLength, disableScroll}))
|
||
|
}
|
||
|
},
|
||
|
// #ifdef APP-NVUE
|
||
|
watch: {
|
||
|
param(v) {
|
||
|
this.$refs.webview.evalJS(`update(${JSON.stringify(v)})`)
|
||
|
}
|
||
|
},
|
||
|
// #endif
|
||
|
// #ifndef APP-PLUS
|
||
|
created() {
|
||
|
this.useCanvas2d = this.type == '2d' && canIUseCanvas2d()
|
||
|
},
|
||
|
// #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.canvasWidth = this.signature.canvas.get('width')
|
||
|
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;
|
||
|
},
|
||
|
// #endif
|
||
|
// #ifdef VUE2
|
||
|
beforeDestroy() {
|
||
|
this.stopWatch && this.stopWatch()
|
||
|
this.signature.destroy()
|
||
|
this.show = false;
|
||
|
this.signature = null
|
||
|
},
|
||
|
// #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
|
||
|
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
|
||
|
},
|
||
|
canvasToTempFilePath(param) {
|
||
|
const isEmpty = this.isEmpty()
|
||
|
// #ifdef APP-NVUE
|
||
|
this.$refs.webview.evalJS(`save()`)
|
||
|
// #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()
|
||
|
})
|
||
|
this.rsave += 1
|
||
|
// #endif
|
||
|
// #ifndef APP-VUE || APP-NVUE
|
||
|
const success = (success) => param.success && param.success(success)
|
||
|
const fail = (fail) => param.fail && param.fail(err)
|
||
|
const {canvas} = this.signature.canvas.get('el')
|
||
|
const {backgroundColor, landscape} = this
|
||
|
const width = this.signature.canvas.get('width')
|
||
|
const height = this.signature.canvas.get('height')
|
||
|
|
||
|
const canvasToTempFilePath = (image) => {
|
||
|
const context = uni.createCanvasContext('offscreen', this)
|
||
|
context.save()
|
||
|
context.setTransform(1,0,0,1,0,0)
|
||
|
if(landscape) {
|
||
|
context.translate(0, width)
|
||
|
context.rotate(-Math.PI / 2)
|
||
|
}
|
||
|
if(backgroundColor) {
|
||
|
context.fillStyle = backgroundColor
|
||
|
context.fillRect(0,0, width, height)
|
||
|
}
|
||
|
context.drawImage(image, 0, 0, width, height);
|
||
|
|
||
|
context.draw(false, () => {
|
||
|
toDataURL('offscreen', this, param).then((res) => {
|
||
|
const size = Math.max(width, height)
|
||
|
context.restore()
|
||
|
context.clearRect(0,0, size, size)
|
||
|
success({tempFilePath: res, isEmpty})
|
||
|
})
|
||
|
})
|
||
|
}
|
||
|
|
||
|
if(this.useCanvas2d) {
|
||
|
try{
|
||
|
// #ifndef MP-ALIPAY
|
||
|
base64ToPath(canvas.toDataURL()).then(canvasToTempFilePath)
|
||
|
// #endif
|
||
|
// #ifdef MP-ALIPAY
|
||
|
canvas.toTempFilePath({
|
||
|
canvasid: this.canvasid,
|
||
|
success(res){
|
||
|
canvasToTempFilePath(res.tempFilePath)
|
||
|
},
|
||
|
fail
|
||
|
})
|
||
|
// #endif
|
||
|
} catch(err){fail(err)}
|
||
|
} else {
|
||
|
toDataURL(this.canvasId, this).then(canvasToTempFilePath).catch(fail)
|
||
|
}
|
||
|
// #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(uni.createCanvasContext(this.canvasId, this))
|
||
|
canvas = {
|
||
|
createImage,
|
||
|
toDataURL: () => toDataURL(this.canvasId, this),
|
||
|
requestAnimationFrame
|
||
|
}
|
||
|
}
|
||
|
// 支付宝小程序 使用stroke有个默认背景色
|
||
|
context.clearRect(0,0,width,height)
|
||
|
return { left, top, right, width, height, context, canvas, pixelRatio};
|
||
|
}
|
||
|
})
|
||
|
},
|
||
|
touchStart(e) {
|
||
|
if(!this.canvasEl) return
|
||
|
this.isStart = true
|
||
|
this.canvasEl.dispatchEvent('touchstart', wrapEvent(e))
|
||
|
},
|
||
|
touchMove(e) {
|
||
|
if(!this.canvasEl || !this.isStart && this.canvasEl) return
|
||
|
this.canvasEl.dispatchEvent('touchmove', wrapEvent(e))
|
||
|
},
|
||
|
touchEnd(e) {
|
||
|
if(!this.canvasEl) return
|
||
|
this.isStart = false
|
||
|
this.canvasEl.dispatchEvent('touchend', wrapEvent(e))
|
||
|
},
|
||
|
// #endif
|
||
|
}
|
||
|
}
|
||
|
</script>
|
||
|
<style lang="stylus">
|
||
|
.lime-signature,.lime-signature__canvas
|
||
|
// #ifndef APP-NVUE
|
||
|
width: 100%;
|
||
|
height: 100%
|
||
|
// #endif
|
||
|
// #ifdef APP-NVUE
|
||
|
flex: 1;
|
||
|
// #endif
|
||
|
.offscreen {
|
||
|
position: fixed;
|
||
|
top: 0
|
||
|
left: 1500rpx;
|
||
|
}
|
||
|
</style>
|