123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- <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/hybrid/html/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="scss">
- .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>
|