【JavaScript】实现页面中填写文档、电子签名,填写完后 转为pdf并自动下载;附带psd转图片预览效果

效果图:

需求:

 用户可以在线进行文档编辑,在线电子签名,然后点击可以另存为pdf文档

实现:

首先实现布局

让填写文档 随着页面的变化 一直保持居中

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .content {
            display: flex;
            justify-content: center;
        }

        .bgImg {
            background-image: url("./2.jpg");
            background-size: contain;
            height: 1562px;
            background-repeat: no-repeat;
            width: 1110px;
            position: relative;
        }

        input {
            display: inline-block;
            height: 35px;
            width: 135px;
            background-color: #ffffff00;
            border: none;
            outline: none;
        }

        .input1 {
            position: absolute;
            top: 162px;
            left: 191px;
            width: 580px;
            height: 28px;
        }

        .input2 {
            position: absolute;
            top: 207px;
            left: 277px;
            width: 520px;
            height: 28px;
        }

        .input3 {
            position: absolute;
            top: 258px;
            left: 235px;
            width: 545px;
            height: 28px;
        }

        .input4 {
            position: absolute;
            top: 305px;
            left: 328px;
            width: 135px;
            height: 28px;
        }
    </style>
</head>

<body>
    <div class="content" style="visibility:hidden;">
        <div id="customDiv">
            <div class="bgImg">
                <div class="text">
                    <input type="text" class="input1">
                    <input type="text" class="input2">
                    <input type="text" class="input3">
                    <input type="text" class="input4">
                </div>
            </div>
            <div class="bgImg">
                <div class="text">
                    <input type="text" class="input1">
                    <input type="text" class="input2">
                    <input type="text" class="input3">
                    <input type="text" class="input4">
                </div>
            </div>
            <div class="bgImg">
                <div class="text">
                    <input type="text" class="input1">
                    <input type="text" class="input2">
                    <input type="text" class="input3">
                    <input type="text" class="input4">
                </div>
            </div>
        </div>

        <span id="submit">提交</span>
    </div>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script>
        $(window, document).on('resize', function () {
            resizeCenterBak();
        }).on('load', function () {
            resizeCenterBak();
            $('.content').css('visibility', 'visible');
        });
        function resizeCenterBak() {
            $('body').css({
                width: "1920px",
                height: "1080px"
            });
            var ratioX = $(window).width() / $('body').width();
            var ratioY = $(window).height() / $('body').height();
            var ratio = Math.min(ratioX, ratioY);
            $('body').css({
                transform: "scale(" + ratio + ")",
                transformOrigin: "left top",
                marginLeft: ($(window).width() - $('body').width() * ratio) / 2
            });
        }
    </script>
</body>

</html>

内容转为图片

然后将填写好的内容  一切进行截图

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .content {
            display: flex;
            justify-content: center;
        }

        .bgImg {
            background-image: url("./2.jpg");
            background-size: contain;
            height: 1562px;
            background-repeat: no-repeat;
            width: 1110px;
            position: relative;
        }

        input {
            display: inline-block;
            height: 35px;
            width: 135px;
            background-color: #ffffff00;
            border: none;
            outline: none;
        }

        .input1 {
            position: absolute;
            top: 162px;
            left: 191px;
            width: 580px;
            height: 28px;
        }

        .input2 {
            position: absolute;
            top: 207px;
            left: 277px;
            width: 520px;
            height: 28px;
        }

        .input3 {
            position: absolute;
            top: 258px;
            left: 235px;
            width: 545px;
            height: 28px;
        }

        .input4 {
            position: absolute;
            top: 305px;
            left: 328px;
            width: 135px;
            height: 28px;
        }
    </style>
</head>

<body>
    <div class="content" style="visibility:hidden;">
        <div id="customDiv">
            <div class="bgImg">
                <div class="text">
                    <input type="text" class="input1">
                    <input type="text" class="input2">
                    <input type="text" class="input3">
                    <input type="text" class="input4">
                </div>
            </div>
            <div class="bgImg">
                <div class="text">
                    <input type="text" class="input1">
                    <input type="text" class="input2">
                    <input type="text" class="input3">
                    <input type="text" class="input4">
                </div>
            </div>
            <div class="bgImg">
                <div class="text">
                    <input type="text" class="input1">
                    <input type="text" class="input2">
                    <input type="text" class="input3">
                    <input type="text" class="input4">
                </div>
            </div>
        </div>

        <span id="submit">提交</span>
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.0/html2canvas.min.js"></script>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script>
        $(window, document).on('resize', function () {
            resizeCenterBak();
        }).on('load', function () {
            resizeCenterBak();
            $('.content').css('visibility', 'visible');
        });
        function resizeCenterBak() {
            $('body').css({
                width: "1920px",
                height: "1080px"
            });
            var ratioX = $(window).width() / $('body').width();
            var ratioY = $(window).height() / $('body').height();
            var ratio = Math.min(ratioX, ratioY);
            $('body').css({
                transform: "scale(" + ratio + ")",
                transformOrigin: "left top",
                marginLeft: ($(window).width() - $('body').width() * ratio) / 2
            });
        }
        $("#submit").click(function () {
            var targetDiv = document.getElementById("customDiv");
            var divRect = targetDiv.getBoundingClientRect();

            // 获取缩放倍数
            var Scale = 760 / divRect.width;
            targetDiv.style.transform = "scale(" + Scale + ")";

            var contentWidth = Scale * divRect.width
            var contentHeight = Scale * divRect.height
            var c = document.createElement('canvas');
            c.width = contentWidth * 1;
            c.height = contentHeight * 1;

            var opts = {
                scale: 1,
                canvas: c,
                useCORS: true,
                logging: true
            };

            var printWindow = window.open("", "_blank");
            
            html2canvas(targetDiv, opts).then(function (canvas) {
                var imageData = canvas.toDataURL(); // 获取截图数据
                var imageElement = new Image();
                imageElement.src = imageData;

                printWindow.document.write('<html><head>');
                printWindow.document.write('<title>打印</title>');
                printWindow.document.write('</head><body>');
                printWindow.document.write(imageElement.outerHTML); // 将截图添加到新窗口
                printWindow.document.write('<//body><//html>');
                printWindow.document.close();
            });
        });
    </script>
</body>

</html>
补充说明:
html2canvas是什么

html2canvas 是一个 JavaScript 库,用于将当前页面或指定的 HTML 元素转换为 <canvas> 元素

主要作用是实现将 HTML 元素转换为图像的功能,这样你就可以在网页上进行截图、生成缩略图、保存网页内容为图像等操作

电子签名

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        body {
            display: flex;
            flex-direction: column;
            align-items: center;
        }
    </style>
</head>

<body>
    <canvas></canvas>
    <div class="content">
        <button onclick="cancel()">取消</button>
        <button onclick="save()">保存</button>
    </div>
</body>
<script src="https://code.jquery.com/jquery-3.6.0.js" type="text/javascript"></script>
<script>
    const config = {
        width: 600, // 宽度
        height: 400, // 高度
        lineWidth: 6, // 线宽
        strokeStyle: 'red', // 线条颜色
        lineCap: 'round', // 设置线条两端圆角
        lineJoin: 'round', // 线条交汇处圆角
    }

    const canvas = document.querySelector('canvas')
    canvas.width = config.width
    canvas.height = config.height
    canvas.style.border = '1px solid #000'
    const ctx = canvas.getContext('2d')
    var element;

    // 设置填充背景色
    ctx.fillStyle = '#fff'
    // 绘制填充矩形
    ctx.fillRect(
        0, // x 轴起始绘制位置
        0, // y 轴起始绘制位置
        config.width, // 宽度
        config.height // 高度
    );

    // 保存上次绘制的 坐标及偏移量
    const client = {
        offsetX: 0, // 偏移量
        offsetY: 0,
        endX: 0, // 坐标
        endY: 0
    }

    // 判断是否为移动端
    const mobileStatus = (/Mobile|Android|iPhone/i.test(navigator.userAgent))

    // 初始化
    const init = event => {
        // 获取偏移量及坐标
        const { offsetX, offsetY, pageX, pageY } = mobileStatus ? event.changedTouches[0] : event

        // 修改上次的偏移量及坐标
        client.offsetX = offsetX
        client.offsetY = offsetY
        client.endX = pageX
        client.endY = pageY

        // 清除以上一次 beginPath 之后的所有路径,进行绘制
        ctx.beginPath()
        // 根据配置文件设置相应配置
        ctx.lineWidth = config.lineWidth
        ctx.strokeStyle = config.strokeStyle
        ctx.lineCap = config.lineCap
        ctx.lineJoin = config.lineJoin
        // 设置画线起始点位
        // client.endX - canvas.offsetLeft, client.endY - canvas.offsetTop
        ctx.moveTo(client.endX - canvas.offsetLeft, client.endY - canvas.offsetTop)
        // 监听 鼠标移动或手势移动
        window.addEventListener(mobileStatus ? "touchmove" : "mousemove", draw)
    }
    // 绘制
    const draw = event => {
        // 获取当前坐标点位
        const { pageX, pageY } = mobileStatus ? event.changedTouches[0] : event
        const offsetX = pageX - canvas.offsetLeft
        const offsetY = pageY - canvas.offsetTop
        // 修改最后一次绘制的坐标点
        client.endX = offsetX
        client.endY = offsetY

        // 根据坐标点位移动添加线条
        ctx.lineTo(offsetX, offsetY)

        // 绘制
        ctx.stroke()
    }
    // 结束绘制
    const cloaseDraw = () => {
        // 结束绘制
        ctx.closePath()
        // 移除鼠标移动或手势移动监听器
        window.removeEventListener("mousemove", draw)
    }
    // 创建鼠标/手势按下监听器
    window.addEventListener(mobileStatus ? "touchstart" : "mousedown", init)
    // 创建鼠标/手势 弹起/离开 监听器
    window.addEventListener(mobileStatus ? "touchend" : "mouseup", cloaseDraw)

    // 取消-清空画布
    const cancel = () => {
        // 保存当前的填充样式
        const currentFillStyle = ctx.fillStyle;

        // 设置填充样式为背景色
        ctx.fillStyle = '#fff';

        // 填充整个画布以覆盖绘制内容
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        // 恢复之前的填充样式
        ctx.fillStyle = currentFillStyle;
    }
    // 保存-将画布内容保存为图片
    const save = () => {
        // 将canvas上的内容转成blob流
        canvas.toBlob(blob => {
            const url = URL.createObjectURL(blob);
            var imageElement = new Image();
            imageElement.src = url;

            // 打开一个文档流,将处理好的截图在新窗口展示,看看效果
            var printWindow = window.open("", "_blank");
            printWindow.document.write('<html><head>');
            printWindow.document.write('<title>打印</title>');
            printWindow.document.write('</head><body>');
            printWindow.document.write(imageElement.outerHTML); // 将截图添加到新窗口
            printWindow.document.write('<//body><//html>');
            printWindow.document.close();
            $('.sig_big').css('visibility', 'hidden');
        })
    }
</script>

</html>
补充说明:

const url = URL.createObjectURL(blob); 生成的url是Blob URL

Blob URL与base64吗,有什么区别?

Blob URL 是一种用于引用二进制数据的特殊 URL 格式。它指向浏览器中的二进制数据块(Blob 对象),可以是图像、音频、视频或其他文件类型。Blob URL 的格式是 blob:<origin>/<unique-id>,其中 <origin> 表示 URL 的来源,<unique-id> 是一个唯一的标识符。例如:blob:http://127.0.0.1:5500/d96d1b24-0a77-4010-b6c0-4d44ef95e485

Base64 是一种编码方式,用于将二进制数据转换为纯文本字符串。Base64 编码后的数据可以方便地传输和存储,但需要进行编码和解码才能使用

如何将 Blob 对象转换为 File 对象,将其作为文件上传到后台?
canvas.toBlob(blob => {
                    const url = URL.createObjectURL(blob);
                    const reader = new FileReader();
                    reader.readAsDataURL(blob);
                    reader.onload = function () {
                        const file = new File([blob], 'fileName.jpg', { type: blob.type });
                        const formData = new FormData();
                        formData.append('file', file);
                    }
                })

创建一个 FileReader 对象,使用 readAsDataURL(blob) 方法读取指定的blob,将其转换为 base64 编码的字符串。这个过程会触发 FileReader 的 onload 事件 

new File([blob], 'fileName.jpg', { type: blob.type }) 中的三个参数分别为

[blob]:要包含在 File 对象中的二进制数据,通常是一个 Blob 对象,用数组的形式传递。

'fileName.jpg':File 对象的文件名,通常需要与上传的文件名保持一致。

{ type: blob.type }:File 对象的 MIME 类型(或内容类型),通常是上传文件的实际类型。

将图片转为pdf并下载

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .content {
            display: flex;
            justify-content: center;
        }

        .bgImg {
            background-image: url("./2.jpg");
            background-size: contain;
            height: 1562px;
            background-repeat: no-repeat;
            width: 1110px;
            position: relative;
        }

        input {
            display: inline-block;
            height: 35px;
            width: 135px;
            background-color: #ffffff00;
            border: none;
            outline: none;
        }

        .input1 {
            position: absolute;
            top: 162px;
            left: 191px;
            width: 580px;
            height: 28px;
        }

        .input2 {
            position: absolute;
            top: 207px;
            left: 277px;
            width: 520px;
            height: 28px;
        }

        .input3 {
            position: absolute;
            top: 258px;
            left: 235px;
            width: 545px;
            height: 28px;
        }

        .input4 {
            position: absolute;
            top: 305px;
            left: 328px;
            width: 135px;
            height: 28px;
        }
    </style>
</head>

<body>
    <div class="content" style="visibility:hidden;">
        <div id="customDiv">
            <div class="bgImg">
                <div class="text">
                    <input type="text" class="input1">
                    <input type="text" class="input2">
                    <input type="text" class="input3">
                    <input type="text" class="input4">
                </div>
            </div>
            <div class="bgImg">
                <div class="text">
                    <input type="text" class="input1">
                    <input type="text" class="input2">
                    <input type="text" class="input3">
                    <input type="text" class="input4">
                </div>
            </div>
            <div class="bgImg">
                <div class="text">
                    <input type="text" class="input1">
                    <input type="text" class="input2">
                    <input type="text" class="input3">
                    <input type="text" class="input4">
                </div>
            </div>
        </div>

        <span id="submit">提交</span>
    </div>
    <script src="./html2canvas.min.js"></script>
    <script src="./jspdf.umd.min.js"></script>
    <script src="./jquery-3.6.0.min.js"></script>
    <script>
        $(window, document).on('resize', function () {
            resizeCenterBak();
        }).on('load', function () {
            resizeCenterBak();
            $('.content').css('visibility', 'visible');
        });
        function resizeCenterBak() {
            $('body').css({
                width: "1920px",
                height: "1080px"
            });
            var ratioX = $(window).width() / $('body').width();
            var ratioY = $(window).height() / $('body').height();
            var ratio = Math.min(ratioX, ratioY);
            $('body').css({
                transform: "scale(" + ratio + ")",
                transformOrigin: "left top",
                marginLeft: ($(window).width() - $('body').width() * ratio) / 2
            });
        }
        $("#submit").click(function () {
            var targetDiv = document.getElementById("customDiv");
            var divRect = targetDiv.getBoundingClientRect();

            var Scale = 760 / divRect.width;
            targetDiv.style.transform = "scale(" + Scale + ")";

            var contentWidth = Scale * divRect.width
            var contentHeight = Scale * divRect.height

            var c = document.createElement('canvas');
            c.width = contentWidth * 1;
            c.height = contentHeight * 1;
            var opts = {
                scale: 1,
                canvas: c,
                useCORS: true,
                logging: true
            };
            var printWindow = window.open("", "_blank");
            html2canvas(targetDiv, opts).then(function (canvas) {
                var imageData = canvas.toDataURL(); // 获取截图数据
                var imageElement = new Image();
                imageElement.src = imageData;

                printWindow.document.write('<html><head>');
                printWindow.document.write('<title>打印</title>');
                printWindow.document.write('</head><body>');
                printWindow.document.write(imageElement.outerHTML); // 将截图添加到新窗口
                printWindow.document.write('<//body><//html>');
                printWindow.document.close();

                var pageHeight = canvas.width / 592.28 * 841.89;

                var pdf = new jspdf.jsPDF('', 'px', 'a4');
                var position = 0;

                while (position < canvas.height) {
                    var leftHeight = canvas.height - position;
                    var maxHeight = Math.min(pageHeight, leftHeight);
                    pdf.addImage(canvas, 'JPEG', 0, 0, null, null, null, 'FAST');
                    position += maxHeight;

                    if (position < canvas.height) {
                        pdf.addPage();
                    }
                }

                pdf.save('我导出的PDF.pdf');
            });
        });
    </script>
</body>

</html>
补充说明:
jspdf.umd.min.js 是什么

jsPDF(JavaScript PDF)库的压缩版本。jsPDF 是一个用于在客户端生成 PDF 文件的 JavaScript 库

主要作用是通过 JavaScript 生成 PDF 文件,使你能够以编程方式创建、编辑和下载 PDF 文档。

扩展:pdf如何转成图片

转成一张图

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>PDF to Merged Image Converter</title>
    <style>
        #merged-image {
            border: 1px solid black;
        }
    </style>
</head>

<body>
    <input type="file" id="pdf-file">
    <canvas id="merged-image"></canvas>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.10.377/pdf.min.js"></script>
    <script>
        const pdfFile = document.getElementById('pdf-file');
        const mergedImageCanvas = document.getElementById('merged-image');
        const mergedImageContext = mergedImageCanvas.getContext('2d');

        pdfFile.addEventListener('change', async (event) => {
            const file = event.target.files[0];

            // 判断文件格式是否为 PDF
            if (file.type !== 'application/pdf') {
                alert('只能上传 PDF 格式文件!');
                return;
            }

            // 创建一个 FileReader 对象,用于读取 PDF 文件数据
            const reader = new FileReader();
            reader.readAsArrayBuffer(file);

            reader.onload = async function () {
                // 将 PDF 文件数据解析成 PDF.js 对象
                const pdf = await pdfjsLib.getDocument(reader.result).promise;

                let mergedWidth = 0;
                let mergedHeight = 0;

                // 计算合并后图像的总宽度和高度
                for (let i = 1; i <= pdf.numPages; i++) {
                    const page = await pdf.getPage(i);
                    const viewport = page.getViewport({ scale: 2 });
                    mergedWidth += viewport.width;
                    mergedHeight = Math.max(mergedHeight, viewport.height);
                }

                // 设置合并后图像的 Canvas 尺寸
                mergedImageCanvas.width = mergedWidth;
                mergedImageCanvas.height = mergedHeight;

                let currentX = 0;

                // 遍历 PDF 文件的每一页,并将其绘制到合并后的 Canvas 上
                for (let i = 1; i <= pdf.numPages; i++) {
                    const page = await pdf.getPage(i);
                    const viewport = page.getViewport({ scale: 2 });

                    // 创建一个临时的 Canvas 元素,用于绘制当前页面
                    const tempCanvas = document.createElement('canvas');
                    tempCanvas.width = viewport.width;
                    tempCanvas.height = viewport.height;
                    const context = tempCanvas.getContext('2d');

                    // 将 PDF 页面内容绘制到临时的 Canvas 上
                    const renderContext = { canvasContext: context, viewport };
                    await page.render(renderContext).promise;

                    // 将临时的 Canvas 绘制到合并后的 Canvas 上
                    mergedImageContext.drawImage(tempCanvas, currentX, 0);

                    currentX += viewport.width;
                }
            };
        });
    </script>
</body>

</html>

根据pdf页数转成多张图

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>PDF to Image Converter</title>
    <style>
      #pdf-container {
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
      }

      img {
        margin: 10px;
        border: 1px solid black;
      }
    </style>
  </head>
  <body>
    <input type="file" id="pdf-file">
    <div id="pdf-container"></div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.10.377/pdf.min.js"></script>
    <script>
      const pdfFile = document.getElementById('pdf-file');
      const pdfContainer = document.getElementById('pdf-container');

      pdfFile.addEventListener('change', async (event) => {
        const file = event.target.files[0];

        // 判断文件格式是否为 PDF
        if (file.type !== 'application/pdf') {
          alert('只能上传 PDF 格式文件!');
          return;
        }

        // 创建一个 FileReader 对象,用于读取 PDF 文件数据
        const reader = new FileReader();
        reader.readAsArrayBuffer(file);

        reader.onload = async function () {
          // 将 PDF 文件数据解析成 PDF.js 对象
          const pdf = await pdfjsLib.getDocument(reader.result).promise;

          // 遍历 PDF 文件的每一页,并将其转换为图片显示在页面上
          for (let i = 1; i <= pdf.numPages; i++) {
            const page = await pdf.getPage(i);
            const viewport = page.getViewport({ scale: 1 });

            // 创建一个 Canvas 元素,用于绘制 PDF 页面内容
            const canvas = document.createElement('canvas');
            canvas.width = viewport.width;
            canvas.height = viewport.height;
            const context = canvas.getContext('2d');

            // 将 PDF 页面内容绘制到 Canvas 上
            const renderContext = { canvasContext: context, viewport };
            await page.render(renderContext).promise;

            // 将 Canvas 转换为图片并显示在页面上
            const imageUrl = canvas.toDataURL();
            const img = document.createElement('img');
            img.src = imageUrl;
            pdfContainer.appendChild(img);
          }
        };
      });
    </script>
  </body>
</html>

pdf.min.js是做什么的,有什么作用

pdf.min.js是一个JavaScript库,用于在Web页面中显示和操作PDF文档。它提供了一系列的API和功能,使得在浏览器中嵌入和处理PDF文件变得更加容易

jspdf.umd.min.js与pdf.min.js 有什么区别

pdf.min.js是一个用于在Web页面中显示和操作PDF文档的库,它提供了显示、缩放、滚动、导航、搜索、标注和打印等功能。而jspdf.umd.min.js是一个用于生成PDF文档的库,它提供了创建、编辑和导出PDF文档的功能

完结撒花,有问题欢迎留言

猜你喜欢

转载自blog.csdn.net/weixin_52479803/article/details/134042922