在components文件夹下新建目录sign-view,再在该目录下新建文件sign-view.vue
<template>
<view class="main-content" v-if="isShow">
<!-- 签字canvas -->
<block v-if="showCanvas">
<canvas
class="mycanvas"
id="mycanvas"
canvas-id="mycanvas"
@touchstart="touchstart"
@touchmove="touchmove"
@touchend="touchend"
disable-scroll="true"
></canvas>
<!-- 旋转canvas -->
</block>
<block class="" v-else>
<image :src="imgurl" mode="" class="sign-img"></image>
</block>
<canvas
class="rotatCanvas"
id="rotatCanvas"
:style="{ 'z-index': -1, width: `${screenWidth}px`, height: `${(screenWidth * screenWidth) / screenHeight}px` }"
canvas-id="rotatCanvas"
></canvas>
<view class="button-line">
<view class="save-button" @tap.stop="finish">保存</view>
<view class="clear-button" @tap.stop="clear">清除</view>
<view class="cancel-button" @tap.stop="hide">关闭</view>
</view>
<view class="mask" v-if="showModal" @tap.stop>
<view class="kw-modal flex align-items-center justify-content-center">
<view class="kw-modal-content">
<view class="flex justify-content-end font-size-34 lightgray">
<text class="iconfont icon-guanbi" @tap="close"></text>
</view>
<view class="flex justify-content-center font-size-38 font-600 kw-modal-title">
{{title}}
</view>
<view class="kw-modal-btnbox flex justify-content-between">
<view @tap="cancel" class="kw-modal-btn">{{cancelText}}</view>
<view @tap="confirm" class="kw-modal-btn">{{confirmText}}</view>
</view>
</view>
</view>
</view>
<view class="mask" v-if="showToast">
<view class="sign-toast flex align-items-center justify-content-center">
<view class="toast-box">请先签名</view>
</view>
</view>
</view>
</template>
<script setup>
import { watch, ref, onMounted } from 'vue'
const props = defineProps({
isShow:{
type:Boolean,
default:true
},
})
const emits = defineEmits(['close','savesign'])
const ctx = ref('') //绘图图像
const points = ref([]) //路径点集合
const screenWidth = ref('')
const screenHeight = ref('')
const isRotatShow = ref(false)
const showModal = ref(false)
const showToast = ref(false)
const startX = ref('')
const startY = ref('')
const moveX = ref('')
const moveY = ref('')
const imgurl = ref('')
const showCanvas = ref(true)
const title = ref('提示框')
const cancelText = ref('取消')
const confirmText = ref('确认')
onMounted(()=> {
createCanvas();
uni.getSystemInfo({
success: e => {
screenWidth.value = e.screenWidth;
screenHeight.value = e.screenHeight - 110;
console.log(e,'e')
}
});
})
function show() {
clear();
props.isShow = true;
}
function hide() {
emits("close",false)
showCanvas.value = true
}
function close(){
showCanvas.value = true
showModal.value = false
}
//取消保存
function cancel(){
close()
//清除画布
clear()
}
function confirm(){
rotat(imgurl.value);
}
function save(){
showCanvas.value = false
console.log(moveX.value,'moveX')
console.log(moveY.value,'moveY')
if(!moveX.value&&!moveY.value){
showToast.value = true
setTimeout(()=>{
showToast.value = false
showCanvas.value = true
},1500)
return false
}
showModal.value = true
title.value = '确认签字?一旦确认,不能修改'
}
//创建并显示画布
function createCanvas() {
showCanvas.value = true;
ctx.value = uni.createCanvasContext('mycanvas', this); //创建绘图对象
//设置画笔样式
ctx.value.lineWidth = 2;
ctx.value.lineCap = 'round';
ctx.value.lineJoin = 'round';
}
//触摸开始,获取到起点
function touchstart(e) {
let startX = e.changedTouches[0].x;
let startY = e.changedTouches[0].y;
let startPoint = {
X: startX,
Y: startY
};
points.value.push(startPoint);
//每次触摸开始,开启新的路径
ctx.value.beginPath();
}
//触摸移动,获取到路径点
function touchmove(e) {
moveX.value = e.changedTouches[0].x;
moveX.value = e.changedTouches[0].y;
let move_x = e.changedTouches[0].x;
let move_y = e.changedTouches[0].y;
let movePoint = {
X: move_x,
Y: move_y
};
points.value.push(movePoint); //存点
let len = points.value.length;
if (len >= 2) {
draw(); //绘制路径
}
}
// 触摸结束,将未绘制的点清空防止对后续路径产生干扰
function touchend() {
points.value = [];
}
function draw() {
let point1 = points.value[0];
let point2 = points.value[1];
points.value.shift();
ctx.value.moveTo(point1.X, point1.Y);
ctx.value.lineTo(point2.X, point2.Y);
ctx.value.stroke();
ctx.value.draw(true);
}
//清空画布
function clear() {
ctx.value.clearRect(0, 0, this.screenWidth, this.screenHeight);
ctx.value.draw(true);
moveX.value = '';
moveY.value = '';
showCanvas.value = true
}
//完成绘画并保存到本地
function finish() {
uni.canvasToTempFilePath(
{
canvasId: 'mycanvas',
success: res => {
imgurl.value = res.tempFilePath
save()
},
complete: com => {}
},
this
);
}
// 将图片选装
function rotat(e) {
// this.isRotatShow = true
// this.showCanvas = true
let rotatCtx = uni.createCanvasContext('rotatCanvas', this); //创建绘图对象
// 重新定位中心点
rotatCtx.translate(0, (screenWidth.value * screenWidth.value) / screenHeight.value);
// rotatCtx.translate(0, 0);
// 将画布逆时针旋转90度
rotatCtx.rotate((270 * Math.PI) / 180);
// 将签字图片绘制进入Canvas
rotatCtx.drawImage(e, 0, 0, (screenWidth.value * screenWidth.value) / screenHeight.value, screenWidth.value);
// 保存后旋转后的结果
rotatCtx.draw(true);
setTimeout(() => {
// 生成图片并回调
uni.canvasToTempFilePath(
{
canvasId: 'rotatCanvas',
success: val => {
console.log('tempFilePath', val.tempFilePath)
showCanvas.value = false
emits('savesign', {tempFilePath: val.tempFilePath ,flag: true})
setTimeout(() => {
hide();
}, 500);
},
complete: com => {
// console.log(com);
}
},
this
);
}, 500);
}
</script>
<style lang="scss" scoped>
.main-content {
width: 100vw;
height: 100vh;
background-color: #ffffff;
z-index: 9999;
overflow: hidden;
max-height:100vh;
}
.mycanvas {
width: 100vw;
height: calc(100vh - 120px);
background-color: #efefef;
position: fixed;
left: 0rpx;
top: 0rpx;
z-index: 2;
}
.sign-img{
width: 100vw;
height: calc(100vh - 110px);
}
.rotatCanvas{
background-color: red;
}
.button-line {
transform: rotate(90deg);
position: fixed;
bottom: -130rpx;
left: 260rpx;
align-items: center;
justify-content: space-between;
z-index: 999;
}
.button-style {
color: #ffffff;
width: 100px;
height: 120rpx;
text-align: center;
line-height: 120rpx;
border-radius: 10rpx;
margin-bottom: 40rpx;
font-size: 34rpx
}
.save-button {
@extend .button-style;
background-color: #02b340;
height: 140rpx;
line-height: 140rpx;
}
.clear-button {
@extend .button-style;
background-color: #ffa500;
height: 140rpx;
line-height: 140rpx;
}
.cancel-button {
@extend .button-style;
background-color: #e10b2b;
}
.mask{
width: 100vw;
height: 100vh;
background: rgba(0,0,0,0.8) ;
position: absolute;
top: 0;
left: 0;
z-index: 999;
}
.kw-modal,.sign-toast{
width: 50vw;
height: calc(100vh - 210px);
transform: rotate(90deg);
position: absolute;
top: 0;
left: 0;
text-align: center;
z-index: 999;
}
.kw-modal-content{
width: 690rpx;
// height: 400rpx;
background: #efefef;
border-radius: 30rpx;
padding: 38rpx 30rpx 70rpx;
.kw-modal-title{
margin-top: 22rpx;
}
.kw-modal-tips{
margin-top: 20rpx;
}
.kw-modal-inp{
margin-top: 50rpx;
.kw-inp{
border: 1rpx solid #D9D9D9;
border-radius: 4rpx;
height: 70rpx;
line-height: 70rpx;
padding: 0 30rpx;
}
.inp-title{
margin-right: 20rpx;
}
}
.kw-modal-btnbox{
margin-top: 100rpx;
display: flex;
justify-content: space-around;
.kw-modal-btn{
width: 300rpx;
height: 84rpx;
background: #D9D9D9;
border-radius: 60rpx;
line-height: 84rpx;
text-align: center;
font-size: 30rpx;
&:last-child{
color: #FFFFFF;
background: #59A3FF;
}
}
}
}
.toast-box{
padding: 15rpx 30rpx;
// background-color: rgba(0, 0, 0, .7);
// color: #fff;
background-color: #ffffff;
color: #ffa500;
border-radius: 8rpx;
font-size: 30rpx;
}
</style>
在页面中使用该插件
<template>
<view class="c-form-item">
<text class="c-form-labels">
<text class="cuIcon-peoplefill c-m-r-10"></text>
<text style="color: red;vertical-align: sub;margin-right: 10rpx;">*</text><text>技术工程师签字</text>
</text>
</view>
<view class="tech-sign" @click="changeIsShow" v-if="!imgFlag">
<van-icon name="orders-o" size="20" color="#ccc"/>
<view class="down-sign">
点击横屏进行签字
</view>
</view>
<view class="canvasbox" v-else>
<image class="canvas-img" :src="imgUrl" mode=""></image>
</view>
<sign-view v-if="isShow" @close="close" @savesign="savesign"></sign-view>
</template>
<script setup>
import { watch, ref } from 'vue'
const isShow = ref(false)
const imgFlag = ref(false)
const imgUrl = ref('')
function changeIsShow(){
isShow.value = true
}
function close(value){
isShow.value = value
}
function savesign(data){
imgUrl.value = data.tempFilePath
imgFlag.value = data.flag
}
</script>
<style>
</style>
由于我项目是自动识别插件,所有不需要手动引入
时候uniapp,vue3,setup语法