212 lines
6.9 KiB
HTML
212 lines
6.9 KiB
HTML
|
<!DOCTYPE html>
|
|||
|
<html lang="zh-CN">
|
|||
|
<head>
|
|||
|
<meta charset="UTF-8">
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
<title>图片转PDF</title>
|
|||
|
<style>
|
|||
|
body {
|
|||
|
font-family: Arial, sans-serif;
|
|||
|
display: flex;
|
|||
|
flex-direction: column;
|
|||
|
align-items: center;
|
|||
|
padding: 20px;
|
|||
|
margin: 0;
|
|||
|
background-color: #f4f4f4;
|
|||
|
}
|
|||
|
h1 {
|
|||
|
font-size: 2.5vw;
|
|||
|
margin-bottom: 20px;
|
|||
|
color: #333;
|
|||
|
}
|
|||
|
#image-container {
|
|||
|
display: flex;
|
|||
|
flex-wrap: wrap;
|
|||
|
gap: 15px;
|
|||
|
justify-content: center;
|
|||
|
margin: 20px 0;
|
|||
|
padding: 10px;
|
|||
|
border: 2px dashed #ccc;
|
|||
|
background-color: #fff;
|
|||
|
min-height: 100px;
|
|||
|
width: 100%;
|
|||
|
max-width: 1200px;
|
|||
|
}
|
|||
|
.image-item {
|
|||
|
position: relative;
|
|||
|
border: 1px solid #ccc;
|
|||
|
overflow: hidden;
|
|||
|
transition: transform 0.2s;
|
|||
|
cursor: move; /* 提示用户可以拖拽 */
|
|||
|
}
|
|||
|
.image-item:hover {
|
|||
|
transform: scale(1.05);
|
|||
|
}
|
|||
|
.image-item img {
|
|||
|
width: 100%;
|
|||
|
height: 100%;
|
|||
|
object-fit: cover;
|
|||
|
}
|
|||
|
.delete-btn {
|
|||
|
position: absolute;
|
|||
|
top: 5px;
|
|||
|
right: 5px;
|
|||
|
background-color: rgb(0, 0, 0);
|
|||
|
opacity: 0.5;
|
|||
|
color: white;
|
|||
|
margin: 0;
|
|||
|
border: none;
|
|||
|
border-radius: 50%;
|
|||
|
width: 20px;
|
|||
|
height: 20px;
|
|||
|
padding: 0;
|
|||
|
font-size: 12px;
|
|||
|
cursor: pointer;
|
|||
|
display: flex;
|
|||
|
align-items: center;
|
|||
|
justify-content: center;
|
|||
|
}
|
|||
|
button {
|
|||
|
padding: 10px 20px;
|
|||
|
font-size: 16px;
|
|||
|
background-color: #007BFF;
|
|||
|
color: white;
|
|||
|
border: none;
|
|||
|
border-radius: 5px;
|
|||
|
cursor: pointer;
|
|||
|
margin-top: 20px;
|
|||
|
}
|
|||
|
button:hover {
|
|||
|
background-color: #0056b3;
|
|||
|
}
|
|||
|
@media (max-width: 600px) {
|
|||
|
h1 {
|
|||
|
font-size: 5vw;
|
|||
|
}
|
|||
|
button {
|
|||
|
font-size: 14px;
|
|||
|
padding: 8px 16px;
|
|||
|
}
|
|||
|
}
|
|||
|
</style>
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<h1>图片转PDF</h1>
|
|||
|
<input type="file" id="file-input" multiple accept="image/*">
|
|||
|
<div id="image-container"></div>
|
|||
|
<button id="generate-pdf">生成PDF</button>
|
|||
|
|
|||
|
<!-- 引入外部库 -->
|
|||
|
<script src="../../../libs/Sortable.min.js"></script>
|
|||
|
<script src="../../../libs/pdf-lib.min.js"></script>
|
|||
|
<script>
|
|||
|
const fileInput = document.getElementById('file-input');
|
|||
|
const imageContainer = document.getElementById('image-container');
|
|||
|
const generateButton = document.getElementById('generate-pdf');
|
|||
|
let images = [];
|
|||
|
|
|||
|
// 文件选择事件
|
|||
|
fileInput.addEventListener('change', (e) => {
|
|||
|
handleFiles(e.target.files);
|
|||
|
});
|
|||
|
|
|||
|
// 生成PDF
|
|||
|
generateButton.addEventListener('click', async () => {
|
|||
|
if (images.length === 0) {
|
|||
|
alert('请先添加至少一张图片!');
|
|||
|
return;
|
|||
|
}
|
|||
|
const pdfDoc = await PDFLib.PDFDocument.create();
|
|||
|
for (const image of images) {
|
|||
|
const imgBytes = await fetch(image.src).then(res => res.arrayBuffer());
|
|||
|
let img = image.file.type === 'image/png' ?
|
|||
|
await pdfDoc.embedPng(imgBytes) :
|
|||
|
await pdfDoc.embedJpg(imgBytes);
|
|||
|
const page = pdfDoc.addPage([img.width, img.height]);
|
|||
|
page.drawImage(img, { x: 0, y: 0, width: img.width, height: img.height });
|
|||
|
}
|
|||
|
const pdfBytes = await pdfDoc.save();
|
|||
|
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
|
|||
|
const url = URL.createObjectURL(blob);
|
|||
|
const a = document.createElement('a');
|
|||
|
a.href = url;
|
|||
|
a.download = '图片合集.pdf';
|
|||
|
a.click();
|
|||
|
URL.revokeObjectURL(url);
|
|||
|
});
|
|||
|
|
|||
|
// 处理文件
|
|||
|
function handleFiles(files) {
|
|||
|
Array.from(files).forEach(file => {
|
|||
|
if (file.type.startsWith('image/')) {
|
|||
|
const reader = new FileReader();
|
|||
|
reader.onload = function(e) {
|
|||
|
const imgWrapper = document.createElement('div');
|
|||
|
imgWrapper.classList.add('image-item');
|
|||
|
|
|||
|
const img = document.createElement('img');
|
|||
|
img.src = e.target.result;
|
|||
|
|
|||
|
const deleteBtn = document.createElement('button');
|
|||
|
deleteBtn.classList.add('delete-btn');
|
|||
|
deleteBtn.innerHTML = '×';
|
|||
|
deleteBtn.addEventListener('click', () => {
|
|||
|
imgWrapper.remove();
|
|||
|
images = images.filter(item => item.src !== img.src);
|
|||
|
});
|
|||
|
|
|||
|
imgWrapper.appendChild(img);
|
|||
|
imgWrapper.appendChild(deleteBtn);
|
|||
|
imageContainer.appendChild(imgWrapper);
|
|||
|
images.push({ src: e.target.result, file });
|
|||
|
|
|||
|
setImageSize();
|
|||
|
};
|
|||
|
reader.readAsDataURL(file);
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
// 获取视口宽度
|
|||
|
function getViewportWidth() {
|
|||
|
return window.innerWidth || document.documentElement.clientWidth;
|
|||
|
}
|
|||
|
|
|||
|
// 设置图片尺寸
|
|||
|
function setImageSize() {
|
|||
|
const viewportWidth = getViewportWidth();
|
|||
|
const imageItems = document.querySelectorAll('.image-item');
|
|||
|
let size = viewportWidth >= 1200 ? 150 : viewportWidth >= 600 ? 100 : 80;
|
|||
|
imageItems.forEach(item => {
|
|||
|
item.style.width = `${size}px`;
|
|||
|
item.style.height = `${size}px`;
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
// 监听窗口resize事件
|
|||
|
window.addEventListener('resize', setImageSize);
|
|||
|
setImageSize();
|
|||
|
|
|||
|
// 拖拽导入支持
|
|||
|
imageContainer.addEventListener('dragover', (e) => {
|
|||
|
e.preventDefault();
|
|||
|
});
|
|||
|
imageContainer.addEventListener('drop', (e) => {
|
|||
|
e.preventDefault();
|
|||
|
if (e.dataTransfer.files.length > 0) {
|
|||
|
handleFiles(e.dataTransfer.files);
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
// 初始化 Sortable.js 进行拖拽排序
|
|||
|
new Sortable(imageContainer, {
|
|||
|
animation: 150,
|
|||
|
onEnd: function(evt) {
|
|||
|
// 更新 images 数组顺序
|
|||
|
const movedItem = images.splice(evt.oldIndex, 1)[0];
|
|||
|
images.splice(evt.newIndex, 0, movedItem);
|
|||
|
}
|
|||
|
});
|
|||
|
</script>
|
|||
|
</body>
|
|||
|
</html>
|