湖北网站建设搭建,厦门高端网站建设公司,免费网站建设 百度收录,怎么做免费网页(给前端大全加星标#xff0c;提升前端技能)转自#xff1a;coyota666https://juejin.cn/post/6901273585428463624前言电子签名通俗来说就是通过技术手段实现在电子文档上加载电子形式的签名#xff0c;其作用类似于纸质合同上的手写签名或加盖的公章。虽然电子签名多年来合… (给前端大全加星标提升前端技能)转自coyota666https://juejin.cn/post/6901273585428463624前言电子签名通俗来说就是通过技术手段实现在电子文档上加载电子形式的签名其作用类似于纸质合同上的手写签名或加盖的公章。虽然电子签名多年来合法性一直遭到质疑但其在企业工作流审批、请柬、单据保全等场景应用广泛最近的项目中就有这样一个手写签名并生成PDF文件的需求。实现思路使用canvas来实现手写签名的功能然后将canvas转化为图片贴在签名的位置将整个需要生成文档的dom区域使用html2canvas插件转成一张大图使用JsPDF插件将上述图片生成PDF文档对于文件内容较多的情况需要合理选择分页位置生成签名1. 在tsx中定义canvas画布 350 height150 /注意Canvas的宽高必须要使用内联样式定义这是因为Canvas标签有自己的默认宽高300px×150px。它内联样式定义的width和height是绘画区域(画布)实际宽度和高度绘制的图形都是在这个上面。如果在style外链文件中定义其width和height那么这个width和height是Canvas在浏览器中被渲染的高度和宽度。如果Canvas中没有直接定义width和height没或值不正确就会被设置成默认值{width:300pxheight:150px}。所以如果你在style中外链文件中设置了canvas {width: 200px; height: 200px;}却没有直接在canvas上定义画布宽高那么此时你输出canvas.height 值依旧为150canvas.width值依旧为300。也就是一块150×300的画布在200×200的区域渲染因而图片会出现拉伸、变形等现象。2. 定义签名函数 const writing ( beginX: number, beginY: number, stopX: number, stopY: number, ctx: any, ) { ctx.beginPath(); // 开启一条新路径 ctx.globalAlpha 1; // 设置图片的透明度 ctx.lineWidth 3; // 设置线宽 ctx.strokeStyle red; // 设置路径颜色 ctx.moveTo(beginX, beginY); // 从(beginX, beginY)这个坐标点开始画图 ctx.lineTo(stopX, stopY); // 定义从(beginX, beginY)到(stopX, stopY)的线条(该方法不会创建线条) ctx.closePath(); // 创建该条路径 ctx.stroke(); // 实际地绘制出通过 moveTo() 和 lineTo() 方法定义的路径。默认颜色是黑色。 };3. 注册监听事件 let beginX: number, beginY: number; const canvas: HTMLCanvasElement canvasDom.current; const ctx canvas.getContext(2d); ctx.fillStyle #fff; ctx.fillRect(0, 0, canvas.width, canvas.height); canvas.addEventListener(touchstart, function(event: any) { event.preventDefault(); // 阻止在canvas画布上签名的时候页面跟着滚动 beginX event.touches[0].clientX - this.offsetLeft; beginY event.touches[0].pageY - this.offsetTop; }); canvas.addEventListener(touchmove, (event: any) { event.preventDefault(); // 阻止在canvas画布上签名的时候页面跟着滚动 event event.touches[0];let stopX event.clientX - canvas.offsetLeft;let stopY event.pageY - canvas.offsetTop; writing(beginX, beginY, stopX, stopY, ctx); beginX stopX; // 这一步很关键需要不断更新起点否则画出来的是射线簇 beginY stopY; });注意在注册“touchstart”和“touchmove”事件时需要阻止默认事件否则页面会跟着手势上下滑动。移动端的每个触摸事件对象中都包括了touches这个属性它用于描述位于屏幕上的所有手指的一个列表获取当前事件对象我们习惯性的使用event event.touches[0],而在PC端则不需要这么操作。offsetLeft值跟offsetTop值跟父级元素没关系而是跟其上一级的定位元素(除position:static外的所有定位如fixed,relative,absolute元素)有关系。若上一级定位元素都没有除position:staice外的定位则这个偏移量是相对于body而言的。需要理清移动端事件对象的几个属性⏬clientX/clientY: 触摸位置距离当前body可视区域的x,y坐标;pageX/pageY: 对于整个页面来说触摸位置距离body左上角的x,y坐标包括被scrollTop和scrollLeft的值screenX/screenY: 触摸位置距离显示器左边和顶部的x,y距离。所以在获取结束点坐标的时候如果当前页面没有出现滚动条使用clientY和pageY计算差别不大如果页面比较长出现了滚动条那么就必须要使用pageY来计算。clientX同理但是移动端通常横向滚动的场景不多所以用clientX来计算即可。在签名(touchmove)这个动作过程中我们需要不断的更新起点位置否则画出来是这样?其实这个原理和微积分很相似线段本质上就是由无穷多个小线段组成宏观一点来看可以把线段当成一个个长度很小的小线段首尾相连构成。所以我一直觉得编程编到最后就是考验一个人的数学能力交并集、逻辑思维、算法等都能看到数学的身影。最后生成签名如下生成PDF文档html2canvas是一款将HTML代码转换成Canvas的插件因此需要用一个div包裹住需要打印的内容区域获得这个dom节点。html2Canvas(dom, { allowTaint: true, width: dom.offsetWidth, //设置获取到的canvas宽度 height: dom.offsetHeight, //设置获取到的canvas高度 x: 0, //页面在水平方向滚动的距离 y: 0, //页面在垂直方向滚动的距离 })注意此处需要设置width和height及x,y否则当页面内容只有一页的时候没有问题但是若页面内容有很多页的时候就会出现生成的图片只有一小部分有内容的现象。问题就出现在这个配置参数上若没有设置宽高则默认只取当前视口的内容丢弃掉其他超出当前视口的内容。设置打印参数const print () {let dom: HTMLElement pdfDom.current; html2Canvas(dom, { allowTaint: true, width: dom.offsetWidth, //设置获取到的canvas宽度 height: dom.offsetHeight, //设置获取到的canvas高度 x: 0, //页面在水平方向滚动的距离 y: 0, //页面在垂直方向滚动的距离 }).then((canvas: HTMLCanvasElement) {let canvasWidth canvas.width;let canvasHeight canvas.height;let pageHeight (canvasWidth / 592.28) * 841.89; // 一页A4 pdf能显示的canvas高度let imgWidth 595.28; // 设置图片宽度和A4纸宽度相等let imgHeight (592.28 / canvasWidth) * canvasHeight;//等比例换算成A4纸的高度let totalHeight imgHeight; // 需要打印的图片总高度初始状态和图片高度相等let pageData canvas.toDataURL(image/png, 1.0);let PDF new JsPDF(p, pt, a4, true);if (totalHeight PDF.addImage(pageData, JPEG, 0, 0, imgWidth, imgHeight); // 从顶部开始打印 } else {let top 0; // 打印初始区域while (totalHeight 0) { PDF.addImage(pageData, JPEG, 0, top, imgWidth, imgHeight); // 从图片顶部往下top位置开始打印 totalHeight - pageHeight; top - 841.89;if (totalHeight 0) { PDF.addPage(); } } } PDF.save(test.pdf); }); };选择分页位置按照上述步骤生成了一份PDF文档但是当PDF页数有很多的时候会有这样的问题⏬可以看到分页的时候从这段文字这里懒腰截断了。这显然不是我们想要看到的效果如何解决这个问题呢?PDF文档页数较少的情况可以在开发测试的时候预先在将要分页的地方插入一个padding就是提前预留分页位置PDF文档页数较多对于这种情况笔者尝试遍历要打印的dom节点的子节点将每一页所能打印的dom节点高度累加若超过了页面所能承载的最大高度则将最后一个节点增加padding打印完毕将样式还原。这种方法因为要计算每个dom节点的高度非常耗性能也要求页面dom元素的颗粒度较细否则会出现一个页面有大块空白完全无法模拟出word生成pdf的那种效果所以就不展开讨论了。如若有读者有比较好的解放方案欢迎不吝赐教感谢~❤️推荐阅读 点击标题可跳转1、烧脑JSCanvas 带你体验「偶消奇不消」的智商挑战2、canvas 中普通动效与粒子动效的实现3、基于 HTML5 Canvas 的交互式地铁线路图觉得本文对你有帮助请分享给更多人推荐关注「前端大全」提升前端技能点赞和在看就是最大的支持❤️