Some checks are pending
Check / lint (push) Waiting to run
Check / typecheck (push) Waiting to run
Check / build (build, 18.x, ubuntu-latest) (push) Waiting to run
Check / build (build, 18.x, windows-latest) (push) Waiting to run
Check / build (build:app, 18.x, ubuntu-latest) (push) Waiting to run
Check / build (build:app, 18.x, windows-latest) (push) Waiting to run
Check / build (build:mp-weixin, 18.x, ubuntu-latest) (push) Waiting to run
Check / build (build:mp-weixin, 18.x, windows-latest) (push) Waiting to run
418 lines
9.8 KiB
Vue
418 lines
9.8 KiB
Vue
<template>
|
||
<view @mouseleave="touchsend" @touchcancel="touchsend" ref="tmspin" :style="{ width: `${props.width}rpx`, height: `${props.height}rpx` }">
|
||
<!-- #ifdef APP-NVUE -->
|
||
<gcanvas
|
||
v-if="show"
|
||
@touchstart="touchstart"
|
||
@touchmove="touchsmove"
|
||
@touchend="touchsend"
|
||
:id="canvasId"
|
||
:ref="canvasId"
|
||
class="canvas"
|
||
:style="{ width: `${props.width}rpx`, height: `${props.height}rpx` }"
|
||
>
|
||
</gcanvas>
|
||
<!-- #endif -->
|
||
<!-- #ifdef MP-WEIXIN || MP-ALIPAY || MP-QQ -->
|
||
<canvas
|
||
@touchstart="touchstart"
|
||
@touchmove="touchsmove"
|
||
@touchend="touchsend"
|
||
@mousedown="touchstart"
|
||
@mousemove.stop="touchsmove"
|
||
@mouseup.stop="touchsend"
|
||
type="2d"
|
||
id="canvasId"
|
||
canvas-id="canvasId"
|
||
class="canvas"
|
||
:style="{ width: `${props.width}rpx`, height: `${props.height}rpx` }"
|
||
></canvas>
|
||
<!-- #endif -->
|
||
<!-- #ifndef MP-WEIXIN || MP-ALIPAY || MP-QQ || APP-NVUE -->
|
||
<canvas
|
||
@touchstart.stop="touchstart"
|
||
@touchmove.stop="touchsmove"
|
||
@touchend.stop="touchsend"
|
||
@mousedown.stop="touchstart"
|
||
@mousemove.stop="touchsmove"
|
||
@mouseup.stop="touchsend"
|
||
@mouseleave.stop="touchsend"
|
||
@touchcancel.stop="touchsend"
|
||
:id="canvasId"
|
||
:canvas-id="canvasId"
|
||
class="canvas"
|
||
:style="{
|
||
width: `${props.width}rpx`,
|
||
height: `${props.height}rpx`,
|
||
'touch-action': 'none'
|
||
}"
|
||
></canvas>
|
||
<!-- #endif -->
|
||
</view>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
/**
|
||
* 签名板
|
||
* @description 方便签名业务。在当前截止最新的3.5.0版本,uni在h5端有自身的bug无法使用。等官方修复 即可使用。
|
||
*/
|
||
import { number } from 'echarts'
|
||
import {
|
||
getCurrentInstance,
|
||
computed,
|
||
ref,
|
||
ComponentInternalInstance,
|
||
inject,
|
||
onUpdated,
|
||
onMounted,
|
||
onUnmounted,
|
||
nextTick,
|
||
watch,
|
||
ssrContextKey
|
||
} from 'vue'
|
||
|
||
// #ifdef APP-NVUE
|
||
import CanvasRenderingContext2D from '../../tool/gcanvas/context-2d/RenderingContext.js'
|
||
import { enable, WeexBridge } from '../../tool/gcanvas/index.js'
|
||
const dom = uni.requireNativePlugin('dom')
|
||
// #endif
|
||
|
||
const proxy = getCurrentInstance()?.proxy ?? null
|
||
|
||
const props = defineProps({
|
||
width: {
|
||
type: Number,
|
||
default: 750
|
||
},
|
||
height: {
|
||
type: Number,
|
||
default: 500
|
||
},
|
||
lineWidth: {
|
||
type: Number,
|
||
default: 5
|
||
},
|
||
lineColor: {
|
||
type: String,
|
||
default: 'red'
|
||
}
|
||
})
|
||
|
||
const canvasId = ref('canvasId')
|
||
// #ifndef MP-WEIXIN || MP-ALIPAY || MP-QQ
|
||
canvasId.value = 'tm' + uni.$tm.u.getUid(5)
|
||
// #endif
|
||
|
||
let ctx: CanvasRenderingContext2D
|
||
let canvasObject: HTMLCanvasElement
|
||
let ctxLeft = 0
|
||
let ctxTop = 0
|
||
let drawhd: draw
|
||
const show = ref(false) //安卓上首次要隐藏不然卡。
|
||
let isAndroid = false
|
||
// #ifdef APP-NVUE
|
||
isAndroid = uni.getSystemInfoSync().osName == 'android'
|
||
// #endif
|
||
onMounted(() => {
|
||
// #ifdef APP-NVUE
|
||
if (isAndroid) {
|
||
setTimeout(() => {
|
||
show.value = true
|
||
setTimeout(function () {
|
||
drawNvue_init()
|
||
}, 100)
|
||
}, 200)
|
||
} else {
|
||
show.value = true
|
||
setTimeout(() => drawNvue_init(), 250)
|
||
}
|
||
// #endif
|
||
|
||
// #ifdef MP-WEIXIN || MP-ALIPAY || MP-QQ
|
||
setTimeout(() => MpWeix_init(), 100)
|
||
// #endif
|
||
// #ifndef MP-WEIXIN || MP-ALIPAY || MP-QQ || APP-NVUE
|
||
setTimeout(() => appvueH5Other(), 50)
|
||
// #endif
|
||
})
|
||
//appvue,h5,和其它平台。
|
||
function appvueH5Other() {
|
||
ctx = uni.createCanvasContext(canvasId.value, proxy)
|
||
uni.createSelectorQuery()
|
||
.in(proxy)
|
||
.select('#' + canvasId.value)
|
||
.boundingClientRect((result) => {
|
||
ctxLeft = result.left ?? 0
|
||
ctxTop = result.top ?? 0
|
||
drawhd = new draw(ctx, uni.upx2px(props.width), uni.upx2px(props.height))
|
||
})
|
||
.exec()
|
||
}
|
||
function drawNvue_init() {
|
||
/*获取元素引用*/
|
||
var ganvas = proxy?.$refs[canvasId.value]
|
||
/*通过元素引用获取canvas对象*/
|
||
var canvasObj = enable(ganvas, {
|
||
bridge: WeexBridge
|
||
})
|
||
canvasObject = canvasObj
|
||
ctx = canvasObj.getContext('2d')
|
||
nextTick(function () {
|
||
setTimeout(function () {
|
||
dom?.getComponentRect(proxy?.$refs.tmspin, function (res: any) {
|
||
if (res?.size) {
|
||
ctxLeft = Math.floor(res.size.left)
|
||
ctxTop = Math.floor(res.size.top)
|
||
drawhd = new draw(ctx, uni.upx2px(props.width), uni.upx2px(props.height))
|
||
}
|
||
})
|
||
}, 200)
|
||
})
|
||
}
|
||
//支付宝和微信,QQ 支持2d渲染。
|
||
function MpWeix_init() {
|
||
const query = uni.createSelectorQuery().in(proxy)
|
||
|
||
// #ifdef MP-ALIPAY
|
||
query
|
||
.select('#canvasId')
|
||
.node()
|
||
.exec((res2) => {
|
||
const canvas = res2[0].node
|
||
let ctxvb: UniApp.CanvasContext = canvas.getContext('2d')
|
||
ctx = ctxvb
|
||
drawhd = new draw(ctx, uni.upx2px(props.width), uni.upx2px(props.height))
|
||
})
|
||
// #endif
|
||
// #ifdef MP-WEIXIN || MP-QQ
|
||
query
|
||
.select('#canvasId')
|
||
.fields({
|
||
node: true,
|
||
size: true,
|
||
rect: true,
|
||
context: true
|
||
})
|
||
.exec((res: Array<UniApp.NodeInfo>) => {
|
||
ctxLeft = res[0].left
|
||
ctxTop = res[0].top
|
||
// #ifndef MP-QQ
|
||
const canvas = res[0].node
|
||
const ctxvb = canvas.getContext('2d')
|
||
const dpr = uni.getSystemInfoSync().pixelRatio
|
||
canvas.width = res[0].width * dpr
|
||
canvas.height = res[0].height * dpr
|
||
ctxvb.scale(dpr, dpr)
|
||
ctx = ctxvb
|
||
canvasObject = canvas
|
||
drawhd = new draw(ctx, uni.upx2px(props.width), uni.upx2px(props.height))
|
||
// #endif
|
||
// #ifdef MP-QQ
|
||
ctx = res[0].context
|
||
drawhd = new draw(ctx, uni.upx2px(props.width), uni.upx2px(props.height))
|
||
// #endif
|
||
})
|
||
// #endif
|
||
}
|
||
|
||
function touchstart(event: TouchEvent | MouseEvent) {
|
||
if (!drawhd) return
|
||
if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) {
|
||
var touch = event.changedTouches[0]
|
||
// #ifdef APP-NVUE
|
||
if (isAndroid) {
|
||
drawhd.down(touch.pageX, touch.pageY)
|
||
} else {
|
||
drawhd.down(touch.pageX - ctxLeft, touch.pageY - ctxTop)
|
||
}
|
||
// #endif
|
||
// #ifndef APP-NVUE
|
||
drawhd.down(touch.x, touch.y)
|
||
// #endif
|
||
} else {
|
||
drawhd.down(event.pageX - event.currentTarget.offsetLeft - ctxLeft, event.pageY - event.currentTarget.offsetTop - ctxTop)
|
||
}
|
||
}
|
||
function touchsmove(event: TouchEvent | MouseEvent) {
|
||
if (!drawhd) return
|
||
try{
|
||
if (event?.preventDefault) event?.preventDefault()
|
||
if (event?.stopPropagation) event?.stopPropagation()
|
||
}catch(e){
|
||
//TODO handle the exception
|
||
}
|
||
if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) {
|
||
var touch = event.changedTouches[0]
|
||
// #ifdef APP-NVUE
|
||
if (isAndroid) {
|
||
drawhd.move(touch.pageX, touch.pageY)
|
||
} else {
|
||
drawhd.move(touch.pageX - ctxLeft, touch.pageY - ctxTop)
|
||
}
|
||
// #endif
|
||
// #ifndef APP-NVUE
|
||
drawhd.move(touch.x, touch.y)
|
||
// #endif
|
||
} else {
|
||
drawhd.move(event.pageX - event.currentTarget.offsetLeft - ctxLeft, event.pageY - event.currentTarget.offsetTop - ctxTop)
|
||
}
|
||
}
|
||
function touchsend(event: TouchEvent | MouseEvent) {
|
||
if (!drawhd) return
|
||
if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) {
|
||
var touch = event.changedTouches[0]
|
||
|
||
// #ifdef APP-NVUE
|
||
if (isAndroid) {
|
||
drawhd.up(touch.pageX, touch.pageY)
|
||
} else {
|
||
drawhd.up(touch.pageX - ctxLeft, touch.pageY - ctxTop)
|
||
}
|
||
// #endif
|
||
// #ifndef APP-NVUE
|
||
drawhd.up(touch.x, touch.y)
|
||
// #endif
|
||
} else {
|
||
drawhd.up(event.pageX - event.currentTarget.offsetLeft - ctxLeft, event.pageY - event.currentTarget.offsetTop - ctxTop)
|
||
}
|
||
}
|
||
/**
|
||
* 清除当前画板
|
||
*/
|
||
function clear() {
|
||
if (!ctx) {
|
||
uni.showToast({ title: '初始化失败', icon: 'none' })
|
||
return
|
||
}
|
||
ctx.clearRect(0, 0, uni.upx2px(props.width), uni.upx2px(props.height))
|
||
// #ifndef MP-WEIXIN || MP-ALIPAY || MP-QQ
|
||
ctx.draw(false)
|
||
// #endif
|
||
}
|
||
/**
|
||
* 保存当前画板内容
|
||
* 在h5端面返回的是base64位图片内容,其它端返回的是临时路径。
|
||
*/
|
||
interface imageData {
|
||
width: number
|
||
height: number
|
||
data: ArrayBuffer
|
||
}
|
||
function save(): Promise<string> {
|
||
return new Promise((su, fa) => {
|
||
if (!ctx) {
|
||
uni.showToast({ title: '初始化失败', icon: 'none' })
|
||
fa('初始化失败')
|
||
return
|
||
}
|
||
// #ifdef APP-NVUE
|
||
uni.showLoading({ title: '...' })
|
||
// ctx.getImageData(0,0,props.width,props.height,function(res:imageData){
|
||
// console.log(ArrayBufferToBase64(res.data).length)
|
||
// fa(true)
|
||
// uni.hideLoading()
|
||
// })
|
||
ctx.toTempFilePath(0, 0, props.width, props.height, uni.upx2px(props.width), uni.upx2px(props.height), 'png', 1, function (res) {
|
||
uni.hideLoading()
|
||
console.log(res.errMsg)
|
||
if (res.errMsg == 'canvasToTempFilePath:ok') {
|
||
su(res.tempFilePath)
|
||
} else {
|
||
fa(res.errMsg)
|
||
}
|
||
})
|
||
|
||
// #endif
|
||
|
||
//webgl保存图片的方法
|
||
// #ifndef APP-NVUE
|
||
uni.canvasToTempFilePath(
|
||
{
|
||
x: 0,
|
||
y: 0,
|
||
destWidth: uni.upx2px(props.width),
|
||
destHeight: uni.upx2px(props.height),
|
||
width: props.width,
|
||
height: props.height,
|
||
// #ifndef MP-ALIPAY
|
||
canvasId: canvasId.value,
|
||
// #endif
|
||
canvas: canvasObject,
|
||
success: function (res) {
|
||
// 在H5平台下,tempFilePath 为 base64
|
||
su(res.tempFilePath)
|
||
},
|
||
fail: (res) => {
|
||
console.error(res)
|
||
fa(res)
|
||
}
|
||
},
|
||
proxy
|
||
)
|
||
// #endif
|
||
})
|
||
}
|
||
defineExpose({ save, clear })
|
||
|
||
interface points {
|
||
x: number
|
||
y: number
|
||
}
|
||
class draw {
|
||
_x = 0
|
||
_y = 0
|
||
_lineWidth = 2
|
||
_lineColor = 'black'
|
||
cx: CanvasRenderingContext2D
|
||
width = 0
|
||
height = 0
|
||
_isDown = false
|
||
_points: Array<points> = []
|
||
constructor(ctx: CanvasRenderingContext2D, w: number, h: number, lineWidth = 2, lineColor = 'black') {
|
||
this._lineColor = lineColor
|
||
this._lineWidth = lineWidth
|
||
this.width = w
|
||
this.height = h
|
||
this.cx = ctx
|
||
}
|
||
down(x = 0, y = 0) {
|
||
this._isDown = true
|
||
this._x = x
|
||
this._y = y
|
||
}
|
||
move(x = 0, y = 0) {
|
||
if (!this._isDown) return
|
||
|
||
// #ifdef MP-WEIXIN || MP-ALIPAY || MP-QQ
|
||
ctx.strokeStyle = props.lineColor
|
||
ctx.lineWidth = props.lineWidth
|
||
ctx.lineCap = 'round'
|
||
// #endif
|
||
// #ifndef MP-WEIXIN || MP-ALIPAY || MP-QQ
|
||
ctx.setStrokeStyle(props.lineColor)
|
||
ctx.setLineWidth(props.lineWidth)
|
||
ctx.setLineCap('round')
|
||
// #endif
|
||
|
||
ctx.beginPath()
|
||
ctx.moveTo(this._x, this._y)
|
||
ctx.lineTo(x, y)
|
||
ctx.stroke()
|
||
ctx.closePath()
|
||
|
||
// #ifndef MP-WEIXIN || MP-ALIPAY
|
||
ctx.draw(true)
|
||
// #endif
|
||
this._x = x
|
||
this._y = y
|
||
}
|
||
up(x = 0, y = 0) {
|
||
this._isDown = false
|
||
this._x = x
|
||
this._y = y
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped></style>
|