chat-app/src/uni_modules/tmui/components/tm-sign-board/tm-sign-board.vue

418 lines
9.8 KiB
Vue
Raw Normal View History

2024-11-11 06:46:14 +00:00
<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>