效果图:
需求:
用户可以在线进行文档编辑,在线电子签名,然后点击可以另存为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文档的功能