要实现的内容:手写签名,协议内容。点击提交后:生成1张图片,有协议内容和签署日期和签署人。
实现的效果图如下:
1、签名页面
<template>
<view class="index">
<u-navbar title="电子协议" :is-back="false" :border-bottom="false" title-color="#333" :background="{background:''}">
<view class="page_navbar_warp">
<image src="../../static/icon/0.png" mode="" class="page_navbar_commonImg" @click="$go(1,1)"></image>
</view>
</u-navbar>
<image src="https://www.*****/xieyi.png" mode="" class="banner"></image>
<view class="signBox">
<view class="title">签名区</view>
<view style="width: 700rpx;height: 450rpx;">
<l-signature disableScroll backgroundColor="rgba(255, 249, 238, .0)" ref="signatureRef" penColor="#333" :penSize="5" :openSmooth="true" ></l-signature>
</view>
</view>
<view class="footer">
<view class="btn1 t-c" @click="onClick('undo')">撤消</view>
<u-button class="btn2 t-c" @click="onClick('save')" :loading="loading">提交</u-button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
loading:false,
}
},
methods:{
onClick(type) {
if(type == 'openSmooth') {
this.openSmooth = !this.openSmooth
return
}
if (type == 'save') {
this.$refs.signatureRef.canvasToTempFilePath({
success: (res) => {
// 是否为空画板 无签名
// 生成图片的临时路径
// H5 生成的是base64
let url = res.tempFilePath;
console.log(res);
if(res.isEmpty){
this.$toast('请签名')
}else{
this.loading = true;
this.$uploadImage('common/upload', url).then(res => {
this.loading = false;
if(res.code == 1){
this.$go(2,'/pages/mine/canvas?signImg='+res.data.fullurl)
}
})
}
}
})
return
}
if (this.$refs.signatureRef) this.$refs.signatureRef[type]()
}
}
}
</script>
<style scoped lang="scss">
.index{
min-height: 100vh;
position: relative;
.banner{
display: block;
width: 585rpx;
height: 416rpx;
margin: auto;
}
.signBox{
border: 1rpx dashed #BF9350;
width: 700rpx;
height: 500rpx;
margin: 32rpx auto;
.title{
padding-top: 32rpx;
font-size: 40rpx;
color: #BF9350;
padding-left: 32rpx;
}
}
.footer{
position: fixed;
left: 0;
bottom: 0;
width: 750rpx;
height: 98rpx;
background: #fff;
box-shadow: 0rpx 3rpx 6rpx 1rpx rgba(0,0,0,0.32);
padding: 0 50rpx;
display: flex;
align-items: center;
justify-content: space-between;
.btn1{
width: 300rpx;
height: 81rpx;
border-radius: 41rpx 41rpx 41rpx 41rpx;
border: 1rpx solid #BF9350;
font-size: 32rpx;
color: #BF9350;
}
.btn2{
width: 300rpx;
height: 81rpx;
background: #BF9350;
border-radius: 41rpx 41rpx 41rpx 41rpx;
font-size: 32rpx;
color: #fff;
}
}
}
</style>
2、canvas页面,用来合成1张图
<template>
<view class="demo">
<u-navbar title="电子协议" :is-back="false" :border-bottom="false" title-color="#333" :background="{background:'#FFFAF3'}">
<view class="page_navbar_warp">
<image src="../../static/icon/0.png" mode="" class="page_navbar_commonImg" @click="$go(1,1)"></image>
</view>
</u-navbar>
<canvas :style="{ width: canvasW + 'px', height: canvasH + 'px' }" canvas-id="myCanvas" id="myCanvas"></canvas>
<view class="footer">
<view class="btn1 t-c" @click="$go(1,1)">取消</view>
<u-button class="btn2 t-c" @click="submit" :loading="loading" shape="circle" :ripple="true">提交</u-button>
</view>
</view>
</template>
<script>
export default {
components: {},
data() {
return {
loading:false,
canvasW:0, // 画布宽
canvasH:0, // 画布高
SystemInfo:{}, // 设备信息
goodsImg: {}, // 协议图片
signImg:{}, // 签名图片
signW:120, // 签名图片大小
bgImg:{},
year:'',
mon:'',
date:'',
tempFilePath:'',
}
},
async onLoad(option) {
var start = new Date();
this.year = start.getFullYear();
this.mon = start.getMonth() + 1;
this.date = start.getDate();
// 获取设备信息,主要获取宽度,赋值给canvasW 也就是宽度:100%
this.SystemInfo = await this.getSystemInfo();
// 获取协议图片,签名二维码图片信息,APP端会返回图片的本地路径(H5端只能返回原路径)
this.bgImg = await this.getImageInfo('https://www.*******/xieyi.png');
this.goodsImg = await this.getImageInfo('https://www.*******/bg.png');
this.signImg = await this.getImageInfo(option.signImg);
this.canvasW = this.SystemInfo.windowWidth; // 画布宽度
// #ifdef APP-PLUS
this.canvasH = this.SystemInfo.windowHeight-94-uni.getSystemInfoSync().statusBarHeight; // 画布高度 = 页面高度-(导航栏固定44px+footer的50px+APP内手机双跳栏的高度)
// #endif
// #ifdef H5
this.canvasH = this.SystemInfo.windowHeight-94;
// #endif
// 如果主图,二维码图片,设备信息都获取成功,开始绘制海报,这里需要用setTimeout延时绘制,否则可能会出现图片不显示。
if(this.goodsImg.errMsg == 'getImageInfo:ok' && this.signImg.errMsg == 'getImageInfo:ok' && this.SystemInfo.errMsg == 'getSystemInfo:ok'){
uni.showToast({
icon:'loading',
mask:true,
duration:10000,
title: '加载中,请稍后',
});
setTimeout(()=>{
var ctx = uni.createCanvasContext('myCanvas', this);
// 填充背景
ctx.drawImage(this.bgImg.path, 0, 0, this.canvasW, this.canvasH) // drawImage(图片路径,x,y,绘制图像的宽度,绘制图像的高度)
// 绘制协议主图
ctx.drawImage(this.goodsImg.path, 50, 60, this.canvasW-100, this.canvasW-180) // drawImage(图片路径,x,y,绘制图像的宽度,绘制图像的高度)
// 签署日期
ctx.setFontSize(16)
ctx.setFillStyle('#333')
ctx.fillText(`签署日期:${this.year}年${this.mon}月${this.date}日`, 50, this.canvasH -this.signW-80);
// 签署人
ctx.setFontSize(14)
ctx.setFillStyle('#333')
ctx.fillText('签署人:', 50, this.canvasH -this.signW-40);
// 签署人
ctx.drawImage(this.signImg.path, 90, this.canvasH-this.signW-80, this.signW, this.signW) // drawImage(图片路径,x,y,绘制图像的宽度,绘制图像的高度,二维码的宽,高)
ctx.draw(true,(ret)=>{ // draw方法 把以上内容画到 canvas 中。
console.log(ret)
uni.showToast({
icon:'success',
mask:true,
title: '绘制完成',
});
uni.canvasToTempFilePath({ // 保存canvas为图片
canvasId: 'myCanvas',
quality: 1,
complete: (res)=> {
console.log(res)
// 在H5平台下,tempFilePath 为 base64, // 图片提示跨域 H5保存base64失败,APP端正常输出临时路径
if(res.tempFilePath){
this.tempFilePath = res.tempFilePath;
}
},
})
});
},1500)
}else{
console.log('err')
}
},
methods: {
submit(){
this.loading = true;
console.log('需要提交给后台的图片',this.tempFilePath)
},
// 获取图片信息
getImageInfo(image) {
return new Promise((req, rej) => {
uni.getImageInfo({
src: image,
success: function(res) {
req(res)
},
});
})
},
// 获取设备信息
getSystemInfo(){
return new Promise((req, rej) => {
uni.getSystemInfo({
success: function (res) {
req(res)
}
});
})
},
},
}
</script>
<style scoped lang="scss">
.footer{
position: fixed;
left: 0;
bottom: 0;
width: 750rpx;
height: 50px;
box-shadow: 0rpx 3rpx 6rpx 1rpx rgba(0,0,0,0.32);
padding: 0 50rpx;
display: flex;
align-items: center;
justify-content: space-between;
background: #fff;
.btn1{
width: 300rpx;
height: 40px;
border-radius: 41rpx 41rpx 41rpx 41rpx;
border: 1rpx solid #BF9350;
font-size: 32rpx;
color: #BF9350;
}
.btn2{
width: 300rpx;
height: 40px;
background: #BF9350;
border-radius: 41rpx 41rpx 41rpx 41rpx;
font-size: 32rpx;
color: #fff;
}
}
</style>
备注:
1、协议页面内用的l-signature
来自于uniapp插件市场
2、canvas页面灵感来自于之前写过的一篇绘制海报文章
3、页面中用到的 xieyi.png(协议内容)、bg.png(底图)、以及签名后的option.signImg(签名图),都需要后台设置允许跨域。否则H5就会报错画布污染无法生成base64。
这个问题在APP内不存在,只有H5会出现。