Update index.html
Browse files- index.html +81 -13
index.html
CHANGED
|
@@ -9,6 +9,7 @@
|
|
| 9 |
font-family: Arial, sans-serif;
|
| 10 |
margin: 20px;
|
| 11 |
text-align: center;
|
|
|
|
| 12 |
}
|
| 13 |
#imageContainer {
|
| 14 |
display: flex;
|
|
@@ -21,8 +22,9 @@
|
|
| 21 |
}
|
| 22 |
.imageWrapper {
|
| 23 |
max-width: 200px;
|
| 24 |
-
cursor: move; /*
|
| 25 |
position: relative;
|
|
|
|
| 26 |
}
|
| 27 |
.imageWrapper img {
|
| 28 |
max-width: 100%;
|
|
@@ -31,7 +33,10 @@
|
|
| 31 |
border-radius: 5px;
|
| 32 |
}
|
| 33 |
.imageWrapper.dragging {
|
| 34 |
-
opacity: 0.5; /* Visual feedback during drag */
|
|
|
|
|
|
|
|
|
|
| 35 |
}
|
| 36 |
button {
|
| 37 |
padding: 10px 20px;
|
|
@@ -50,7 +55,7 @@
|
|
| 50 |
<body>
|
| 51 |
<h1>Images to PDF Converter</h1>
|
| 52 |
<input type="file" id="imageInput" accept="image/*" multiple>
|
| 53 |
-
<p>Drag and drop images to reorder them for the PDF.</p>
|
| 54 |
<div id="imageContainer"></div>
|
| 55 |
<button onclick="generatePDF()">Download as PDF</button>
|
| 56 |
|
|
@@ -81,7 +86,7 @@
|
|
| 81 |
});
|
| 82 |
});
|
| 83 |
|
| 84 |
-
// Render images with drag-and-drop functionality
|
| 85 |
function renderImages() {
|
| 86 |
imageContainer.innerHTML = '';
|
| 87 |
images.forEach((image, index) => {
|
|
@@ -94,40 +99,103 @@
|
|
| 94 |
img.src = image.src;
|
| 95 |
wrapper.appendChild(img);
|
| 96 |
|
| 97 |
-
//
|
| 98 |
wrapper.addEventListener('dragstart', (e) => {
|
| 99 |
wrapper.classList.add('dragging');
|
| 100 |
e.dataTransfer.setData('text/plain', index);
|
| 101 |
});
|
| 102 |
|
| 103 |
-
// Drag end
|
| 104 |
wrapper.addEventListener('dragend', () => {
|
| 105 |
wrapper.classList.remove('dragging');
|
|
|
|
| 106 |
});
|
| 107 |
|
| 108 |
-
// Drag over
|
| 109 |
wrapper.addEventListener('dragover', (e) => {
|
| 110 |
e.preventDefault();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
});
|
| 112 |
|
| 113 |
-
// Drop
|
| 114 |
wrapper.addEventListener('drop', (e) => {
|
| 115 |
e.preventDefault();
|
| 116 |
const draggedIndex = parseInt(e.dataTransfer.getData('text/plain'));
|
| 117 |
const dropIndex = parseInt(wrapper.dataset.index);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
|
|
|
| 122 |
|
| 123 |
-
|
| 124 |
-
|
|
|
|
|
|
|
|
|
|
| 125 |
});
|
| 126 |
|
| 127 |
imageContainer.appendChild(wrapper);
|
| 128 |
});
|
| 129 |
}
|
| 130 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
// Generate PDF from images
|
| 132 |
function generatePDF() {
|
| 133 |
if (images.length === 0) {
|
|
|
|
| 9 |
font-family: Arial, sans-serif;
|
| 10 |
margin: 20px;
|
| 11 |
text-align: center;
|
| 12 |
+
touch-action: none; /* Prevent default touch behaviors like scrolling */
|
| 13 |
}
|
| 14 |
#imageContainer {
|
| 15 |
display: flex;
|
|
|
|
| 22 |
}
|
| 23 |
.imageWrapper {
|
| 24 |
max-width: 200px;
|
| 25 |
+
cursor: move; /* For desktop */
|
| 26 |
position: relative;
|
| 27 |
+
user-select: none; /* Prevent text selection during drag/touch */
|
| 28 |
}
|
| 29 |
.imageWrapper img {
|
| 30 |
max-width: 100%;
|
|
|
|
| 33 |
border-radius: 5px;
|
| 34 |
}
|
| 35 |
.imageWrapper.dragging {
|
| 36 |
+
opacity: 0.5; /* Visual feedback during drag/touch */
|
| 37 |
+
}
|
| 38 |
+
.imageWrapper.over {
|
| 39 |
+
border: 2px dashed #4CAF50; /* Visual feedback for drop target */
|
| 40 |
}
|
| 41 |
button {
|
| 42 |
padding: 10px 20px;
|
|
|
|
| 55 |
<body>
|
| 56 |
<h1>Images to PDF Converter</h1>
|
| 57 |
<input type="file" id="imageInput" accept="image/*" multiple>
|
| 58 |
+
<p>Drag and drop (desktop) or tap and move (mobile) images to reorder them for the PDF.</p>
|
| 59 |
<div id="imageContainer"></div>
|
| 60 |
<button onclick="generatePDF()">Download as PDF</button>
|
| 61 |
|
|
|
|
| 86 |
});
|
| 87 |
});
|
| 88 |
|
| 89 |
+
// Render images with drag-and-drop and touch functionality
|
| 90 |
function renderImages() {
|
| 91 |
imageContainer.innerHTML = '';
|
| 92 |
images.forEach((image, index) => {
|
|
|
|
| 99 |
img.src = image.src;
|
| 100 |
wrapper.appendChild(img);
|
| 101 |
|
| 102 |
+
// Desktop drag-and-drop
|
| 103 |
wrapper.addEventListener('dragstart', (e) => {
|
| 104 |
wrapper.classList.add('dragging');
|
| 105 |
e.dataTransfer.setData('text/plain', index);
|
| 106 |
});
|
| 107 |
|
|
|
|
| 108 |
wrapper.addEventListener('dragend', () => {
|
| 109 |
wrapper.classList.remove('dragging');
|
| 110 |
+
clearOverStyles();
|
| 111 |
});
|
| 112 |
|
|
|
|
| 113 |
wrapper.addEventListener('dragover', (e) => {
|
| 114 |
e.preventDefault();
|
| 115 |
+
wrapper.classList.add('over');
|
| 116 |
+
});
|
| 117 |
+
|
| 118 |
+
wrapper.addEventListener('dragleave', () => {
|
| 119 |
+
wrapper.classList.remove('over');
|
| 120 |
});
|
| 121 |
|
|
|
|
| 122 |
wrapper.addEventListener('drop', (e) => {
|
| 123 |
e.preventDefault();
|
| 124 |
const draggedIndex = parseInt(e.dataTransfer.getData('text/plain'));
|
| 125 |
const dropIndex = parseInt(wrapper.dataset.index);
|
| 126 |
+
reorderImages(draggedIndex, dropIndex);
|
| 127 |
+
renderImages();
|
| 128 |
+
});
|
| 129 |
+
|
| 130 |
+
// Touch events for mobile
|
| 131 |
+
wrapper.addEventListener('touchstart', (e) => {
|
| 132 |
+
e.preventDefault();
|
| 133 |
+
wrapper.classList.add('dragging');
|
| 134 |
+
startTouchReorder(e, wrapper, index);
|
| 135 |
+
});
|
| 136 |
|
| 137 |
+
wrapper.addEventListener('touchmove', (e) => {
|
| 138 |
+
e.preventDefault();
|
| 139 |
+
handleTouchMove(e, wrapper);
|
| 140 |
+
});
|
| 141 |
|
| 142 |
+
wrapper.addEventListener('touchend', (e) => {
|
| 143 |
+
e.preventDefault();
|
| 144 |
+
wrapper.classList.remove('dragging');
|
| 145 |
+
endTouchReorder(wrapper, index);
|
| 146 |
+
clearOverStyles();
|
| 147 |
});
|
| 148 |
|
| 149 |
imageContainer.appendChild(wrapper);
|
| 150 |
});
|
| 151 |
}
|
| 152 |
|
| 153 |
+
// Touch reordering logic
|
| 154 |
+
let touchStartY = 0;
|
| 155 |
+
let targetIndex = null;
|
| 156 |
+
|
| 157 |
+
function startTouchReorder(event, wrapper, index) {
|
| 158 |
+
const touch = event.touches[0];
|
| 159 |
+
touchStartY = touch.clientY;
|
| 160 |
+
targetIndex = index;
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
function handleTouchMove(event, wrapper) {
|
| 164 |
+
const touch = event.touches[0];
|
| 165 |
+
const touchY = touch.clientY;
|
| 166 |
+
|
| 167 |
+
// Find the element under the touch point
|
| 168 |
+
const elements = document.elementsFromPoint(touch.clientX, touchY);
|
| 169 |
+
const targetWrapper = elements.find(el => el.classList.contains('imageWrapper') && el !== wrapper);
|
| 170 |
+
|
| 171 |
+
// Clear previous 'over' styles
|
| 172 |
+
clearOverStyles();
|
| 173 |
+
|
| 174 |
+
if (targetWrapper) {
|
| 175 |
+
targetWrapper.classList.add('over');
|
| 176 |
+
}
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
function endTouchReorder(wrapper, startIndex) {
|
| 180 |
+
const overElement = document.querySelector('.imageWrapper.over');
|
| 181 |
+
if (overElement) {
|
| 182 |
+
const dropIndex = parseInt(overElement.dataset.index);
|
| 183 |
+
reorderImages(startIndex, dropIndex);
|
| 184 |
+
renderImages();
|
| 185 |
+
}
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
function clearOverStyles() {
|
| 189 |
+
document.querySelectorAll('.imageWrapper.over').forEach(el => el.classList.remove('over'));
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
function reorderImages(draggedIndex, dropIndex) {
|
| 193 |
+
if (draggedIndex !== dropIndex) {
|
| 194 |
+
const [draggedImage] = images.splice(draggedIndex, 1);
|
| 195 |
+
images.splice(dropIndex, 0, draggedImage);
|
| 196 |
+
}
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
// Generate PDF from images
|
| 200 |
function generatePDF() {
|
| 201 |
if (images.length === 0) {
|