这里是通过父子传值,用弹窗的形式写的
父组件
<script>
// 引用手写板组件
import htzSign from "@/subPackage/homeBed/pages/project/service/service/htz-sign.vue";
export default {
name: "serviceIn",
components: {
htzSign,
},
data() {
return {
// 是否滚动 防止在手写板输入内容的时候,手写板滚动
popupShow:false,
staffImg:'',
}
},
methods: {
// 写字板弹窗
goSignstaff(type){
this.$refs.popupstaff.open(type)
this.popupShow = true;
},
//提交签字
async sumbit(res) {
try {
// 自己的上传接口
... .then(r=> {
this.staffImg=r.Model
this.$refs.popupstaff.close()
this.popupShow = false;
})
} catch (e) {
uni.hideLoading()
}
},
// 签字失败
fail(err) {
console.log('fail', err)
},
// 勿删 手写板禁止晃动的事件 可以给空事件
stopRoll() {}
}
}
</script>
<template>
<view class="mainView" :class="{popupShow:popupShow}" >
<uni-card title="签字" v-if="AutoSyncQn()" >
<image class="imgSize" v-if="staffImg!==''" :src="staffImg" @click="goSignstaff( 'bottom')"></image>
<image class="imgSize" v-else :src="userInfo.StaffSignImg " @click="goSignstaff( 'bottom')"></image>
<uni-popup :animation="false" :is-mask-click="false" ref="popupstaff" background-color="#fff" class="pop">
<view class="popup-content" @touchmove.stop.prevent="stopRoll">
<!-- 签字板组件-->
<htz-sign @sumbit="sumbit" @fail="fail" cid="ceshi1" ></htz-sign>
</view>
</uni-popup>
</uni-card>
</view>
</template>
<style scoped lang="scss">
.popupShow {
overflow: hidden;
position: fixed;
}
.imgSize{
width: 100%;
height: 150px;
}
//模糊处理
image {
will-change: transform;//解决加载时瞬间拉伸问题
width: auto;//解决加载时瞬间拉伸问题
height: auto;//解决加载时瞬间拉伸问题
image-rendering:-moz-crisp-edges;
image-rendering:-o-crisp-edges;
image-rendering:-webkit-optimize-contrast;
image-rendering: crisp-edges;
-ms-interpolation-mode:nearest-neighbor;
}
.mainView {
padding: 8px 4px;
height: 100%;
background-color: #F2F6FC;
}
.popup-content {
height: 100vh;
width: 100%;
background-color: #fff;
}
.pop{
width: 100%;
height:100%;
}
</style>
手写板组件
<template>
<view class="htz-signature-body">
<canvas class="canvas" :canvas-id="cid" :id="cid" @touchstart="touchstart" @touchmove="touchmove"
@touchend="touchend"></canvas>
<!--用于旋转图片的canvas容器-->
<canvas style="position: fixed;z-index: 0;top:-500%;" canvas-id="handWriting2"></canvas>
<view class="htz-signature-fixed-bottom">
<view class="htz-signature-fixed-bottom-item htz-signature-tools">
<view class="htz-signature-tools-item" @click="lineWidth">
<image src="https://cdn.cos.adl66.com/Applets/ShiLaoHuaGaiZao/bicuxi.png"></image>
<view>线条</view>
</view>
<view class="htz-signature-tools-item" @click="color">
<image src="https://cdn.cos.adl66.com/Applets/ShiLaoHuaGaiZao/color-plate-fill.png"></image>
<view>颜色</view>
</view>
<view class="htz-signature-tools-item" @click="revoke">
<image src="https://cdn.cos.adl66.com/Applets/ShiLaoHuaGaiZao/chehuinormal.png"></image>
<view>撤回</view>
</view>
<view class="htz-signature-tools-item" @click="clear">
<image src="https://cdn.cos.adl66.com/Applets/ShiLaoHuaGaiZao/qingkong_1.png"></image>
<view>清空</view>
</view>
</view>
<view class="htz-signature-fixed-bottom-item sumbit" @click="sumbit">提交</view>
</view>
<!-- #ifdef APP-PLUS -->
<view class="htz-signature-color-main" v-if="colorShow">
<view @click="selColor(index)"
:class="index==colorIndex?'htz-signature-color-item on ':'htz-signature-color-item '"
:style="'background-color:'+item.value" v-for="(item,index) in colorData" :key="index">
<image class="htz-signature-color-item-icon" src="https://cdn.cos.adl66.com/Applets/ShiLaoHuaGaiZao/on.png"></image>
</view>
</view>
<view class="htz-signature-color-main" v-if="lineWidthShow">
<view @click="selLineWidth(index)"
:class="index==lineWidthIndex?'htz-signature-lineWidth-item on':'htz-signature-lineWidth-item'"
v-for="(item,index) in lineWidthData" :key="index">
<view
:style="'width:60%;height:'+item+'px;background-color:#000000;position: absolute;top: 50%;left: 20%;margin-top:-'+item/2+'px'">
</view>
</view>
</view>
<!-- #endif -->
<!-- #ifndef APP-PLUS -->
<cover-view class="htz-signature-color-main" v-if="colorShow">
<cover-view @click="selColor(index)"
:class="index==colorIndex?'htz-signature-color-item on ':'htz-signature-color-item '"
:style="'background-color:'+item.value" v-for="(item,index) in colorData" :key="index">
<cover-image class="htz-signature-color-item-icon" src="https://cdn.cos.adl66.com/Applets/ShiLaoHuaGaiZao/on.png">
</cover-image>
</cover-view>
</cover-view>
<cover-view class="htz-signature-color-main" v-if="lineWidthShow">
<cover-view @click="selLineWidth(index)"
:class="index==lineWidthIndex?'htz-signature-lineWidth-item on':'htz-signature-lineWidth-item'"
v-for="(item,index) in lineWidthData" :key="index">
<cover-view
:style="'width:60%;height:'+item+'px;background-color:#000000;position: absolute;top: 50%;left: 20%;margin-top:-'+item/2+'px'">
</cover-view>
</cover-view>
</cover-view>
<!-- #endif -->
</view>
</template>
<script>
export default {
name: 'htz-signature',
props: {
cid: {
type: String,
default: '',
},
},
data() {
return {
id: '',
Strokes: [],
dom: null,
width: 0,
height: 0,
colorShow: false,
colorIndex: 0,
colorData: [{
name: 'black',
value: '#000000'
}, {
name: 'red',
value: '#f34336',
}, {
name: 'blue',
value: '#0238d0',
}, {
name: 'green',
value: '#8bc24b',
}, {
name: 'yellow',
value: '#ffeb3c',
}, {
name: 'purple',
value: '#a603d0',
}, {
name: 'grey',
value: '#a5a5a5',
}],
lineWidthShow: false,
lineWidthIndex: 0,
lineWidthData: ['3', '6', '9', '12', '15', '18']
}
},
mounted: function() {
this.$nextTick(function() {
// #ifdef H5
document.body.addEventListener('touchmove', this.touchmoveEnd, {
passive: false
})
// #endif
uni.getSystemInfo({
success: (res) => {
this.width = res.windowWidth;
this.height = res.windowHeight;
}
});
this.dom = uni.createCanvasContext(this.cid, this);
});
},
beforeDestroy: function() {
// #ifdef H5
document.body.removeEventListener('touchmove', this.touchmoveEnd, {
passive: false
})
// #endif
},
methods: {
touchmoveEnd(e) {
e.preventDefault();
e.stopPropagation();
},
sumbit() {
var t=this;
uni.canvasToTempFilePath({
canvasId: this.cid,
fileType:"jpg",
quality: 1, //图片质量
success:function(res) {
//获取到原签名照片
t.rotate(res.tempFilePath);
},
fail(e){
console.log(e)
}
}, this)
},
//旋转图片,生成新canvas实例
rotate(tempFilePaths){
var _this = this;
uni.getImageInfo({
// 获取图片的信息
src: tempFilePaths,
success: (res1) => {
// 将canvas1的内容复制到canvas2中
let canvasContext = uni.createCanvasContext('handWriting2',this)
let width = _this.height
let height = _this.width
canvasContext.translate(height / 2, width / 2)
canvasContext.rotate((270 * Math.PI) / 180)
canvasContext.drawImage(tempFilePaths, -width / 2, -height / 2, width, height)
canvasContext.draw(false,(data)=>{
// 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中
uni.canvasToTempFilePath({
// 把当前画布指定区域的内容导出生成指定大小的图片。在 draw() 回调里调用该方法才能保证图片导出成功。
canvasId: 'handWriting2',
fileType: 'jpg',
quality: 1, //图片质量
success(res2) {
// 调用uni.uploadFile上传图片即可
_this.$emit('sumbit',res2);
}
},_this)
});
}
})
},
clear() { //清空
this.Strokes = [];
this.dom.clearRect(0, 0, this.width, this.height)
this.dom.draw();
},
lineWidth() {
this.lineWidthShow = !this.lineWidthShow;
this.colorShow = false;
},
selLineWidth(index) {
this.lineWidthIndex = index;
this.lineWidthShow = false;
},
color() {
this.colorShow = !this.colorShow;
this.lineWidthShow = false;
},
selColor(index) {
this.colorIndex = index;
this.colorShow = false;
},
revoke() { //撤销上一步
var delItem = this.Strokes.pop();
this.drawCanves();
},
drawCanves() {
//this.dom.draw();
this.Strokes.forEach((item, index) => {
let StrokesItem = item;
if (StrokesItem.points.length > 1) {
this.dom.beginPath();
this.dom.setLineCap('round');
this.dom.setStrokeStyle(item.style.color);
this.dom.setLineWidth(item.style.lineWidth);
StrokesItem.points.forEach((sitem, sindex) => {
if (sitem.type == "touchstart") {
this.dom.moveTo(sitem.x, sitem.y)
} else {
this.dom.lineTo(sitem.x, sitem.y)
}
})
this.dom.stroke();
}
})
this.dom.draw();
},
createId() {
var d = new Date();
return 'can' + d.getTime();
},
touchstart(e) {
this.Strokes.push({
imageData: null,
style: {
color: this.colorData[this.colorIndex].value,
lineWidth: this.lineWidthData[this.lineWidthIndex],
},
points: [{
x: e.touches[0].x,
y: e.touches[0].y,
type: e.type,
}]
})
this.drawLine(this.Strokes[this.Strokes.length - 1], e.type);
},
touchmove(e) {
this.Strokes[this.Strokes.length - 1].points.push({
x: e.touches[0].x,
y: e.touches[0].y,
type: e.type,
})
this.drawLine(this.Strokes[this.Strokes.length - 1], e.type);
},
touchend(e) {
if (this.Strokes[this.Strokes.length - 1].points.length < 2) { //当此路径只有一个点的时候
this.Strokes.pop();
}
},
drawLine(StrokesItem, type) {
if (StrokesItem.points.length > 1) {
this.dom.beginPath();
this.dom.setLineCap('round')
this.dom.setStrokeStyle(StrokesItem.style.color);
this.dom.setLineWidth(StrokesItem.style.lineWidth);
this.dom.moveTo(StrokesItem.points[StrokesItem.points.length - 2].x, StrokesItem.points[StrokesItem
.points.length -
2].y);
this.dom.lineTo(StrokesItem.points[StrokesItem.points.length - 1].x, StrokesItem.points[StrokesItem
.points.length -
1].y);
this.dom.stroke();
this.dom.draw(true);
}
}
}
}
</script>
<style>
.canvas {
position: relative;
z-index: 5;
width: 100%;
height: 100%;
}
.htz-signature-body {
position: fixed;
top: 0;
bottom: 0rpx;
left: 0;
width: 100%;
}
.htz-signature-body canvas {
width: 100%;
height: 100%;
}
.htz-signature-fixed-bottom {
transform:rotate(90deg) translate(0%,270%);
position: fixed;
left: 0%;
top: 40%;
width: 100%;
height: 120rpx;
line-height: 120rpx;
text-align: center;
color: #000;
z-index: 11;
display: -webkit-box;
display: -webkit-flex;
display: flex;
background-color: #fff;
}
.htz-signature-fixed-bottom .htz-signature-fixed-bottom-item {
-webkit-box-flex: 3;
-webkit-flex-grow: 3;
flex-grow: 3;
border-top: 1px solid #d9d9d9;
color: #1890ff;
}
.htz-signature-fixed-bottom view.sumbit {
-webkit-box-flex: 1;
-webkit-flex-grow: 1;
flex-grow: 1;
background-color: #1890ff;
color: #fff;
}
.htz-signature-tools {
display: -webkit-box;
display: -webkit-flex;
display: flex;
}
.htz-signature-tools-item {
text-align: center;
-webkit-box-flex: 1;
-webkit-flex-grow: 1;
flex-grow: 1;
line-height: 35rpx;
}
.htz-signature-fixed-bottom-item view image {
width: 50rpx;
height: 50rpx;
padding-top: 10rpx;
}
.htz-signature-tools-item view {
font-size: 22rpx;
}
.htz-signature-color-main {
position: fixed;
bottom: 120rpx;
left: 0;
width: 710rpx;
/* height: 75rpx; */
z-index: 11;
padding: 25rpx 20rpx;
display: -webkit-box;
display: -webkit-flex;
display: flex;
flex-wrap: wrap;
background-color: #fff;
border-top: 1px dashed #d9d9d9;
transition: display 2s;
-moz-transition: display 2s;
/* Firefox 4 */
-webkit-transition: display 2s;
/* Safari 和 Chrome */
-o-transition: display 2s;
}
.htz-signature-color-item {
width: 80rpx;
height: 80rpx;
background-color: #000000;
border-radius: 100px;
margin: 5px;
position: relative;
}
.htz-signature-lineWidth-item {
width: 80rpx;
height: 80rpx;
background-color: #fff;
border-radius: 100px;
margin: 5px;
position: relative;
}
.htz-signature-lineWidth-item.on {
border: 1px solid #d4a39e;
}
.htz-signature-color-item .htz-signature-color-item-icon {
display: none;
}
.htz-signature-color-item.on .htz-signature-color-item-icon {
display: block;
position: absolute;
top: 50%;
left: 50%;
width: 50rpx;
height: 50rpx;
margin-top: -25rpx;
margin-left: -25rpx;
}
.black {
background-color: #000000 !important;
}
</style>
效果 手写板:
图片效果:
附加:手写板如果还晃动