小程序 uniapp 实现pdf 电子合同签名 并导出功能

需求流程:

  1. 用户只允许上传pdf
  2. 后端将上传后的pdf以base64图片的形式返回
  3. 用户设置签名的位置
  4. 位置设置完成,将电子签名放到设定的位置处
  5. 利用canvas将两张图片合成一张图片,保存至相册

实操

基本功能不再赘述,简要概括

1:首先是新建签名,出现可缩放,拖拽的框,我是借鉴uniapp插件
插件地址: https://ext.dcloud.net.cn/plugin?id=2059

2: 按照插件步骤执行完毕,下一步就是获取拖拽,缩放之后的 宽高和坐标
x, y, w, h
在下载的插件里面,有个end方法,在end方法里面,获取当前框的大小的位置

<canvascanvas-id="canvas-drag"disable-scroll="true"@touchstart="start"@touchmove="move"@touchend="end":style="'width: ' + width + 'rpx; height: ' + height + 'rpx;'"></canvas>
    end(e) {uni.setStorageSync("storage_x", parseInt(pdfX));uni.setStorageSync("storage_y", parseInt(pdfY));uni.setStorageSync("storage_w", parseInt(pdfW));uni.setStorageSync("storage_h", parseInt(pdfH));this.tempGraphArr = [];if (isMove) {isMove = false; // 重置移动标识// 用户操作结束时记录历史this.recordHistory();}},

记录之后,画框,旋转,缩放,这一快就结束了。之后就是
写签名
将签名放在指定位置
将签名导出本地

写签名直接复制过去就能用

画完之后,调用uni.canvasToTempFilePath ,会返回一个本地路径,并将本地路劲传到之前选择框的页面
<template><!-- <div v-show="showCanvas"> --><div class="contain"><canvascanvas-id="mycanvas"class="mycanvas"disable-scroll="true"@touchstart="ontouchstart"@touchmove="touchmove"@touchend="touchend"></canvas><view class="footer"><view class="footer-wrap"><pdf-buttontext="清除"mode="clear"@pdfButton="clear"class="btn"></pdf-button><pdf-buttonv-if="show"text="完成"mode="pdfGary"left="31rpx"class="btn"></pdf-button><pdf-buttonv-elsetext="完成"mode="pdfFinssh"@pdfButton="close"left="31rpx"class="btn"></pdf-button></view></view></div>
</template><script>
import pdfButton from "../../components/pdfButton";export default {components: {pdfButton,},data() {return {headerStyleHeight: 0,headerStyleTop: 0,oc: "",points: [], //路径点集合showCanvas: false, //show: true,};},mounted() {this.clear();},onShow() {this.initCanvas();// console.log('参数',+uni.getStorageSync("storage_w"));},methods: {close() {uni.canvasToTempFilePath({canvasId: "mycanvas",success: (res) => {console.log("res", res.tempFilePath);const canvas = res.tempFilePath;uni.navigateTo({url: `/pages/previewFinally/index?hasSingle=${1}&canvas=${canvas}`,});},});},initCanvas() {this.showCanvas = true;this.oc = uni.createCanvasContext("mycanvas");//设置画笔样式this.oc.lineWidth = 4;this.oc.lineCap = "round";this.oc.lineJoin = "round";this.oc.strokeStyle = "black";this.oc.fillStyle = "#fff";this.oc.lineWidth = 5;this.oc.fillRect(0, 0, 400, 700);this.oc.fill();this.oc.draw();},ontouchend(e) {console.log("结束啊啊啊啊");oc.ontouchmove = null;this.show = false;},ontouchmove(e) {const { clientX, clientY } = e.changedTouches[0];oc.lineTo(clientX - oc.offsetLeft, clientY - oc.offsetTop);oc.stroke();},//触摸开始,获取到起点ontouchstart(e) {// debugger//   ctx.beginPath();const startX = e.changedTouches[0].x;const startY = e.changedTouches[0].y;let startPoint = {X: startX,Y: startY,};this.points.push(startPoint);//每次触摸开始,开启新的路径this.oc.beginPath();},//触摸移动,获取到路径点touchmove(e) {let moveX = e.changedTouches[0].x;let moveY = e.changedTouches[0].y;let movePoint = {X: moveX,Y: moveY,};this.points.push(movePoint); //存点let len = this.points.length;if (len >= 2) {this.draw(); //绘制路径}},// 触摸结束,将未绘制的点清空防止对后续路径产生干扰touchend() {this.points = [];this.show = false;},/* ***********************************************#   绘制笔迹#   1.为保证笔迹实时显示,必须在移动的同时绘制笔迹#   2.为保证笔迹连续,每次从路径集合中区两个点作为起点(moveTo)和终点(lineTo)#   3.将上一次的终点作为下一次绘制的起点(即清除第一个点)************************************************ */draw() {let point1 = this.points[0];let point2 = this.points[1];this.points.shift();this.oc.moveTo(point1.X, point1.Y);this.oc.lineTo(point2.X, point2.Y);this.oc.stroke();this.oc.draw(true);},//清空画布clear() {this.show = true;let that = this;uni.getSystemInfo({success: (res) => {let canvasw = res.windowWidth;let canvash = res.windowHeight;that.oc.clearRect(0, 0, canvasw, canvash);that.oc.draw(true);//   that.initCanvas();},});},},
};
</script><style lang="scss">
.contain {height: 100vh;position: relative;.button {width: 100%;height: 50px;line-height: 50px;text-align: center;font-size: 20px;border: 1px solid #bbbbbb;}.header-box {width: 100%;background-color: #ffffff;padding-bottom: 20rpx;color: red;}.content-box {width: 100%;height: 500px;//   background-color: aliceblue;padding: 20rpx;box-sizing: border-box;}.signature {position: fixed;top: 10px;left: 2%;z-index: 999;width: 96%;}.mycanvas {width: 100%;height: 330rpx;}.footer {font-size: 14px;height: 100%;display: flex;justify-content: space-around;align-items: center;background: #f2f3f5;.footer-wrap {height: 100%;padding: 57rpx 37rpx 17rpx 37rpx;box-sizing: border-box;display: flex;align-items: center;}}.sty-single {position: absolute;top: 70rpx;left: 0rpx;width: 569rpx;height: 200rpx;z-index: 111;}
}
.btn {height: 100%;
}
</style>

在之前设置位置的页面,将画好的签名。放到指定位置

// 接收传来的canvasonLoad(options) {this.hasSingle = options.hasSingle;this.canvas = options.canvas;this.px();},// 渲染canvas,注意宽高,坐标。用之前保存的坐标data() {return {pdfDataList: uni.getStorageSync("filePath"),hasSingle: 0,canvas: "",rpx: "",finillyX: uni.getStorageSync("storage_x"),finillyY: uni.getStorageSync("storage_y"),finillyW: uni.getStorageSync("storage_w"),finillyH: uni.getStorageSync("storage_h"),};},//  渲染<imagev-if="pdfDataList != '' && hasSingle == 1":src="canvas"class="sty-single transfo":style="{width: finillyW + 'px',height: finillyH + 'px',top: finillyY + 'px',left: finillyX + 'px',}"></image>

合成图片,将两个图片都画成canvas,然后利用API导出

结构
<view class="footer-wrap"><pdf-buttonv-if="canvas"text="保存合同"mode="finishBig"@pdfButton="finishSave"class="btn"></pdf-button>
功能
finishSave() {// 提示用户正在合成,否则用户可能有不当操作或者以为手机卡死wx.showLoading({title: "合成中",mask: true,});// 创建画布对象const ctx = uni.createCanvasContext("myCanvas");console.log("ctx", ctx);// 获取图片信息,要按照原图来绘制,否则图片会变形uni.getImageInfo({src: this.pdfDataList, // 底图success: (res) => {// 根据 图片的大小 绘制底图 的大小console.log(" 底图", res);let imgPath = res.path;ctx.drawImage(imgPath,0,0,(750 / 2) * this.rpx,(1061 / 2) * this.rpx);wx.getImageInfo({src: this.canvas,success: (res) => {console.log(" 签名", res);ctx.drawImage(res.path,this.finillyX,this.finillyY,this.finillyW,this.finillyH);ctx.draw();setTimeout(() => {console.log("111");uni.canvasToTempFilePath({canvasId: "myCanvas",success: (res) => {uni.hideLoading();console.log("合成图片", res);const tempFilePath = res.tempFilePath;// 保存到相册wx.saveImageToPhotosAlbum({filePath: tempFilePath,success: (res) => {// 修改下载状态wx.showModal({title: "温馨提示",content: "已保存到本地相册",confirmText: "我知道了",showCancel: false,success: (res) => {console.log("res图片保存成功", res);if (res.confirm) {uni.reLaunch({url: "/pages/contract/index",});}uni.setStorageSync("sing", "");uni.setStorageSync("File", "");uni.setStorageSync("filePath", "");},});},});},});}, 2000);},});},});},

由于canvas画出来的坐标都是px,所以在保存的时候。为了不同机型,需要做适配,按比例就好

 px() {const { windowWidth } = wx.getSystemInfoSync();this.rpx = windowWidth / 375;//获取屏幕宽度,获取自适应单位console.log("rpx", this.rpx);},

以上代码仅供参考,出现v-if 判断,或者pdf-button之类的。改成自己需要即可

小程序 uniapp 实现pdf 电子合同签名 并导出功能相关推荐

  1. 【预览pdf】:uniapp在微信小程序预览pdf,兼容安卓/IOS

    前言 提示:因最近项目需求,项目需要用uniapp来开发微信小程序预览pdf的功能, 后端以链接形式返回pdf地址. 一.方法概述 开始用uniapp提供微信自带方法:wx.downloadFile( ...

  2. 微信小程序uni-app

    文章目录 一.微信小程序uni-app 二.下载微信开发者工具 三.小程序开发 一.微信小程序uni-app 小程序是一种不需要下载.安装即可使用的应用,它实现了应用触手可及的梦想,用户扫一扫或者搜一 ...

  3. 小程序uniapp实现左滑删除效果

    小程序uniapp实现左滑删除效果 实现效果 1,列表中侧滑删除 2,删除不同时存在 3,上下滑动与侧滑删除不影响 在本页面引入组件并使用 (文件在文章的最下方附上) 在需要左滑删除的地方使用 < ...

  4. 【无忧美名网-一款根据诗经、古诗词等国学给宝宝起名的小程序-uniapp端】

    无忧美名网-一款根据诗经.古诗词等国学给宝宝起名的小程序-uniapp端.使用UNIAPP开发,可以直接导出为微信小程序.抖音小程序.快手小程序和百度小程序. 前端集成了日历选择插件,支持对接微信支付 ...

  5. 小程序web-view打开PDF格式文件的安卓苹果兼容性问题

    小程序中打开pdf格式原本可以使用web-view(承载网页的容器.会自动铺满整个小程序页面,个人类型的小程序暂不支持使用) <web-view src="{{link}}" ...

  6. 微信小程序开发 - 实现pdf、word等格式文件上传到后端的方法

    写在前面 我发现,微信的wx.uploadFile接口限制好多,而且会经常性出现bug,所以今天搜了一上午的资料, 看看能不能不要通过这个接口上传multipart/form-data格式的文件. 后 ...

  7. 代驾小程序源码全套,支持二开(Thinkphp+bootstrap+小程序uniapp)

    代驾小程序源码全套,支持二开(Thinkphp+bootstrap+小程序uniapp)支持二开!包含基本的代驾功能,内部基于腾讯地图开发! 1.基于ThinkPHP+Bootstrap的极速后台代驾 ...

  8. 微信小程序uni-app前端应用框架和HBuilderX工具使用及Git管理项目

    微信小程序uni-app前端应用框架和HBuilderX工具使用及Git管理项目 官方地址:https://uniapp.dcloud.io/ 1.起步 1.1.简介 uni-app 是一个使用 Vu ...

  9. 微信小程序/uni-app 蓝牙打印开发教程和常见问题总结【文末附源码】

    微信小程序/uni-app 蓝牙打印开发教程和常见问题总结[文末附源码] 文章目录 微信小程序/uni-app 蓝牙打印开发教程和常见问题总结[文末附源码] 1️⃣ 写在前面 2️⃣ 蓝牙连接流程 3 ...

最新文章

  1. php图片加边框,php在图片上增加矩形框并加入水印
  2. LeetCode: Max Points on a Line
  3. Debug Tensorflow: TypeError: Cannot convert a symbolic Keras input/output to a numpy array.
  4. 编写你的第一个 Django 应用,第 4 部分
  5. android 屏幕旋转不重新加载,Android webview旋转屏幕导致页面重新加载问题解决办法...
  6. 秘钥协议及新兴密码学方向【7】
  7. [NOIP2018]铺设道路
  8. 总结一些开源的网站检测扫描器源码(未完)
  9. web前端面试:不做面试“海王”,一份资料就可成功上岸!
  10. 剑指offer答案python_剑指offer(python)(未完)
  11. mysql数据库获得树的节点
  12. 微信小程序弹框的使用
  13. Hi3519内核配置uart串口
  14. vue分享至qq空间,新浪微博,微信朋友圈及微信好友
  15. 关键词策略 —关键词选择
  16. ubuntu系统安装记录
  17. 微信企业号上传永久素材讲解与演示
  18. 前端项目实战145-React.Fragment
  19. 个人项目-仿微信小打卡小程序
  20. Mysql 数据库(一)

热门文章

  1. (Win8、Win7)MAK激活密钥分享【资源有限】
  2. 算法刷题【洛谷P1593】因子和(附等比数列求和公式推导)
  3. 武田就欧盟委员会对拟收购Shire plc进行中的1期评审发表置评
  4. 触摸屏基本原理介绍【转】
  5. 地理信息科学专业转计算机,2019地理信息科学专业就业前景和就业方向分析
  6. 彼岸花开,忧伤了谁的等待?诗意伤感日志
  7. 白櫵校园商品交易系统/校园二手物品交易系统/二手交易系统
  8. windows下如何删除需要***授权的文件夹、文件
  9. 计算机专业名称bios翻译,电脑BIOS中英文对照翻译有哪些?
  10. arcGIS中利用空间校正进行坐标系转换