-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
787 lines (661 loc) · 29.4 KB
/
Copy pathscript.js
File metadata and controls
787 lines (661 loc) · 29.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
document.addEventListener('DOMContentLoaded', () => {
// DOM Elements
const pixelCanvas = document.getElementById('pixelCanvas');
const colorPicker = document.getElementById('colorPicker');
const eraserBtn = document.getElementById('eraser');
const clearBtn = document.getElementById('clear');
const gridSizeSelect = document.getElementById('gridSize');
const saveBtn = document.getElementById('save');
const loadBtn = document.getElementById('load');
const savedArtworks = document.getElementById('savedArtworks');
const exportImageBtn = document.getElementById('exportImage');
const exportJSONBtn = document.getElementById('exportJSON');
const importFileInput = document.getElementById('importFile');
const importButton = document.getElementById('importButton');
const zoomInBtn = document.getElementById('zoomIn');
const zoomOutBtn = document.getElementById('zoomOut');
const zoomLevelEl = document.getElementById('zoomLevel');
const canvasContainer = document.querySelector('.canvas-container');
// New Modal DOM Elements for Pixelate Feature
const pixelateModal = document.getElementById('pixelateModal');
const mainPixelateButton = document.getElementById('pixelateButton'); // The button that opens the modal
const closePixelateModalBtn = document.getElementById('closePixelateModalBtn');
const uploadImageInModalBtn = document.getElementById('uploadImageInModalBtn');
const pixelateFileModalInput = document.getElementById('pixelateFileModal');
const previewContainerModal = document.getElementById('previewContainerModal');
const previewOriginalModalImg = document.getElementById('previewOriginalModal');
const previewPixelatedModalCanvas = document.getElementById('previewPixelatedModal');
const pixelateOptionsModalDiv = document.getElementById('pixelateOptionsModal');
const pixelateSizeModalSelect = document.getElementById('pixelateSizeModal');
const pixelateConfirmModalBtn = document.getElementById('pixelateConfirmModal');
const pixelateCancelModalBtn = document.getElementById('pixelateCancelModal');
// Variables
let currentColor = colorPicker.value;
let isDrawing = false;
let isEraser = false;
let currentGridSize = parseInt(gridSizeSelect.value);
let artworks = JSON.parse(localStorage.getItem('pixelArtworks')) || [];
let zoomLevel = 1;
const zoomStep = 0.25;
const maxZoom = 5;
const minZoom = 0.5;
let lastDrawPosition = null; // 记录上一次绘制的位置
// Initialize the grid
createGrid(currentGridSize);
displaySavedArtworks();
// Event Listeners
colorPicker.addEventListener('input', () => {
currentColor = colorPicker.value;
isEraser = false;
eraserBtn.classList.remove('active');
});
eraserBtn.addEventListener('click', () => {
isEraser = !isEraser;
eraserBtn.classList.toggle('active');
});
clearBtn.addEventListener('click', clearGrid);
gridSizeSelect.addEventListener('change', () => {
if (confirm('更改网格大小将清除当前画布。确定要继续吗?')) {
currentGridSize = parseInt(gridSizeSelect.value);
createGrid(currentGridSize);
} else {
gridSizeSelect.value = currentGridSize;
}
});
saveBtn.addEventListener('click', saveArtwork);
loadBtn.addEventListener('click', loadSavedArtwork);
exportImageBtn.addEventListener('click', exportAsImage);
exportJSONBtn.addEventListener('click', exportAsJSON);
importButton.addEventListener('click', () => importFileInput.click());
importFileInput.addEventListener('change', handleImport);
// 修改鼠标事件处理
pixelCanvas.addEventListener('mousedown', (e) => {
e.preventDefault();
e.stopPropagation();
startDrawing(e);
});
pixelCanvas.addEventListener('mouseup', (e) => {
e.preventDefault();
e.stopPropagation();
stopDrawing();
});
pixelCanvas.addEventListener('mouseleave', (e) => {
e.preventDefault();
e.stopPropagation();
stopDrawing();
});
pixelCanvas.addEventListener('mouseover', (e) => {
e.preventDefault();
e.stopPropagation();
draw(e);
});
// Touch events for mobile
pixelCanvas.addEventListener('touchstart', (e) => {
e.preventDefault();
e.stopPropagation();
handleTouch(e);
});
pixelCanvas.addEventListener('touchmove', (e) => {
e.preventDefault();
e.stopPropagation();
handleTouchMove(e);
});
pixelCanvas.addEventListener('touchend', (e) => {
e.preventDefault();
e.stopPropagation();
stopDrawing();
});
// Zoom functionality
zoomInBtn.addEventListener('click', () => {
if (zoomLevel < maxZoom) {
zoomLevel += zoomStep;
updateZoom();
}
});
zoomOutBtn.addEventListener('click', () => {
if (zoomLevel > minZoom) {
zoomLevel -= zoomStep;
updateZoom();
}
});
// --- MODAL PIXELATE FEATURE ---
function openPixelateModal() {
// Reset modal state
previewOriginalModalImg.src = '';
const previewCtx = previewPixelatedModalCanvas.getContext('2d', { willReadFrequently: true });
previewCtx.clearRect(0, 0, previewPixelatedModalCanvas.width, previewPixelatedModalCanvas.height);
pixelateFileModalInput.value = ''; // Clear file input
previewContainerModal.style.display = 'none';
pixelateOptionsModalDiv.style.display = 'none';
pixelateConfirmModalBtn.disabled = true;
pixelateSizeModalSelect.value = '32'; // Reset to default
pixelateModal.style.display = 'block';
}
function closePixelateModal() {
pixelateModal.style.display = 'none';
// Optional: further cleanup if needed when modal is simply closed without confirm
previewOriginalModalImg.src = '';
const previewCtx = previewPixelatedModalCanvas.getContext('2d', { willReadFrequently: true });
previewCtx.clearRect(0, 0, previewPixelatedModalCanvas.width, previewPixelatedModalCanvas.height);
pixelateFileModalInput.value = '';
}
// Event listener for the main button that opens the modal
mainPixelateButton.addEventListener('click', openPixelateModal);
// Event listener for the close button (X) on the modal
closePixelateModalBtn.addEventListener('click', closePixelateModal);
// Event listener for the "Cancel" button inside the modal
pixelateCancelModalBtn.addEventListener('click', closePixelateModal);
// Event listener for the "Upload Image" button inside the modal
uploadImageInModalBtn.addEventListener('click', () => {
pixelateFileModalInput.click();
});
// Event listener for file selection in the modal
pixelateFileModalInput.addEventListener('change', handlePixelateFileSelectModal);
// Event listener for pixel size change in the modal
pixelateSizeModalSelect.addEventListener('change', generatePixelatedPreviewModal);
// Event listener for the "Confirm Conversion" button in the modal
pixelateConfirmModalBtn.addEventListener('click', () => {
if (!previewOriginalModalImg.complete || !previewOriginalModalImg.src || previewOriginalModalImg.naturalWidth === 0) {
alert('请先选择一张图片并生成预览。');
return;
}
const targetGridSize = parseInt(pixelateSizeModalSelect.value);
if (confirm(`确定要将图片转换为 ${targetGridSize}×${targetGridSize} 像素画并应用到当前画布吗?此操作会覆盖现有内容。`)) {
if (currentGridSize !== targetGridSize) {
gridSizeSelect.value = targetGridSize;
currentGridSize = targetGridSize;
createGrid(currentGridSize);
}
const previewCtx = previewPixelatedModalCanvas.getContext('2d', { willReadFrequently: true });
const mainCanvasPixels = document.querySelectorAll('#pixelCanvas .pixel');
if (mainCanvasPixels.length !== targetGridSize * targetGridSize) {
console.error("Mismatch between main canvas pixels and target grid size after recreating grid.");
alert("转换时发生错误,画布大小不匹配。");
return; // Keep modal open if error
}
for (let y = 0; y < targetGridSize; y++) {
for (let x = 0; x < targetGridSize; x++) {
const imageData = previewCtx.getImageData(x, y, 1, 1).data;
const r = imageData[0];
const g = imageData[1];
const b = imageData[2];
const a = imageData[3];
const color = (a < 128) ? 'white' : `rgb(${r},${g},${b})`;
const pixelIndex = y * targetGridSize + x;
if (mainCanvasPixels[pixelIndex]) {
mainCanvasPixels[pixelIndex].style.backgroundColor = color;
}
}
}
closePixelateModal();
}
});
// --- Helper functions for Modal Pixelate ---
function handlePixelateFileSelectModal(event) {
const file = event.target.files[0];
if (!file || !file.type.startsWith('image/')) {
alert('请选择一个图像文件。');
pixelateFileModalInput.value = ''; // Clear input
pixelateConfirmModalBtn.disabled = true;
previewContainerModal.style.display = 'none';
pixelateOptionsModalDiv.style.display = 'none';
return;
}
const reader = new FileReader();
reader.onload = (e) => {
previewOriginalModalImg.src = e.target.result;
previewOriginalModalImg.onload = () => {
if (previewOriginalModalImg.naturalWidth === 0) {
alert('无法加载图片,请尝试其他文件。');
previewOriginalModalImg.src = '';
pixelateFileModalInput.value = '';
pixelateConfirmModalBtn.disabled = true;
previewContainerModal.style.display = 'none';
pixelateOptionsModalDiv.style.display = 'none';
return;
}
previewContainerModal.style.display = 'block';
pixelateOptionsModalDiv.style.display = 'flex'; // Show options
pixelateConfirmModalBtn.disabled = false; // Enable confirm button
generatePixelatedPreviewModal();
};
previewOriginalModalImg.onerror = () => {
// alert('加载图片预览时出错。');
previewOriginalModalImg.src = '';
pixelateFileModalInput.value = '';
pixelateConfirmModalBtn.disabled = true;
previewContainerModal.style.display = 'none';
pixelateOptionsModalDiv.style.display = 'none';
};
};
reader.readAsDataURL(file);
// Don't clear pixelateFileModalInput.value here, allow browser to handle it.
// If cleared too early, might cause issues with some browsers or repeated uploads of same file.
}
function generatePixelatedPreviewModal() {
if (!previewOriginalModalImg.complete || !previewOriginalModalImg.src || previewOriginalModalImg.naturalWidth === 0) {
const ctx = previewPixelatedModalCanvas.getContext('2d', { willReadFrequently: true });
ctx.clearRect(0, 0, previewPixelatedModalCanvas.width, previewPixelatedModalCanvas.height);
pixelateConfirmModalBtn.disabled = true;
return;
}
const targetGridSize = parseInt(pixelateSizeModalSelect.value);
const image = previewOriginalModalImg;
const canvas = previewPixelatedModalCanvas;
const ctx = canvas.getContext('2d', { willReadFrequently: true });
canvas.width = targetGridSize;
canvas.height = targetGridSize;
ctx.imageSmoothingEnabled = false;
ctx.drawImage(image, 0, 0, targetGridSize, targetGridSize);
pixelateConfirmModalBtn.disabled = false; // Ensure confirm is enabled if preview generates
}
// Functions
function createGrid(size) {
pixelCanvas.innerHTML = '';
pixelCanvas.style.gridTemplateColumns = `repeat(${size}, 1fr)`;
for (let i = 0; i < size * size; i++) {
const pixel = document.createElement('div');
pixel.classList.add('pixel');
pixel.dataset.index = i;
// 为每个像素添加防止拖拽的属性
pixel.setAttribute('draggable', 'false');
pixel.addEventListener('dragstart', (e) => {
e.preventDefault();
});
pixelCanvas.appendChild(pixel);
}
updateZoom();
}
function updateZoom() {
pixelCanvas.style.transform = `scale(${zoomLevel})`;
zoomLevelEl.textContent = `${Math.round(zoomLevel * 100)}%`;
// 调整容器大小适应缩放
const containerSize = Math.min(640, window.innerWidth - 40);
canvasContainer.style.width = `${containerSize}px`;
canvasContainer.style.height = `${containerSize}px`;
}
function startDrawing(e) {
isDrawing = true;
// 记录上一个点的位置
if (e.target.classList.contains('pixel')) {
const pixelIndex = parseInt(e.target.dataset.index);
lastDrawPosition = { index: pixelIndex };
}
draw(e);
}
function stopDrawing() {
isDrawing = false;
lastDrawPosition = null; // 停止绘制时重置位置记录
}
function draw(e) {
if (!isDrawing) return;
if (e.target.classList.contains('pixel')) {
const currentIndex = parseInt(e.target.dataset.index);
// 设置当前像素颜色
e.target.style.backgroundColor = isEraser ? 'white' : currentColor;
// 如果存在上一个绘制位置,则进行插值填充
if (lastDrawPosition && lastDrawPosition.index !== currentIndex) {
fillPixelsBetween(lastDrawPosition.index, currentIndex, isEraser ? 'white' : currentColor);
}
// 更新上一个绘制位置
lastDrawPosition = { index: currentIndex };
}
}
function handleTouch(e) {
e.preventDefault();
const touch = e.touches[0];
const pixel = document.elementFromPoint(touch.clientX, touch.clientY);
if (pixel && pixel.classList.contains('pixel')) {
isDrawing = true;
const pixelIndex = parseInt(pixel.dataset.index);
// 设置当前像素颜色
pixel.style.backgroundColor = isEraser ? 'white' : currentColor;
// 记录上一个触摸位置
lastDrawPosition = { index: pixelIndex };
}
}
function handleTouchMove(e) {
e.preventDefault();
if (!isDrawing) return;
const touch = e.touches[0];
const pixel = document.elementFromPoint(touch.clientX, touch.clientY);
if (pixel && pixel.classList.contains('pixel')) {
const currentIndex = parseInt(pixel.dataset.index);
// 设置当前像素颜色
pixel.style.backgroundColor = isEraser ? 'white' : currentColor;
// 如果存在上一个触摸位置,进行插值填充
if (lastDrawPosition && lastDrawPosition.index !== currentIndex) {
fillPixelsBetween(lastDrawPosition.index, currentIndex, isEraser ? 'white' : currentColor);
}
// 更新上一个触摸位置
lastDrawPosition = { index: currentIndex };
}
}
function clearGrid() {
if (confirm('确定要清空画布吗?')) {
const pixels = document.querySelectorAll('.pixel');
pixels.forEach(pixel => {
pixel.style.backgroundColor = 'white';
});
}
}
function saveArtwork() {
// 创建用于生成图像的画布
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const size = currentGridSize;
const pixelSize = 10; // 保存图像中每个像素的大小(像素)
canvas.width = size * pixelSize;
canvas.height = size * pixelSize;
// 使用当前像素艺术填充画布
const pixels = document.querySelectorAll('.pixel');
pixels.forEach((pixel, index) => {
const row = Math.floor(index / size);
const col = index % size;
ctx.fillStyle = pixel.style.backgroundColor || 'white';
ctx.fillRect(col * pixelSize, row * pixelSize, pixelSize, pixelSize);
});
// 转换为数据URL
const dataURL = canvas.toDataURL('image/png');
// 创建像素数据数组
const pixelData = Array.from(pixels).map(pixel => pixel.style.backgroundColor || 'white');
// 保存到本地存储
const timestamp = new Date().toISOString();
const artwork = {
id: Date.now(),
dataURL,
timestamp,
gridSize: size,
pixelData
};
artworks.push(artwork);
localStorage.setItem('pixelArtworks', JSON.stringify(artworks));
// 刷新画廊
displaySavedArtworks();
}
function displaySavedArtworks() {
savedArtworks.innerHTML = '';
if (artworks.length === 0) {
savedArtworks.innerHTML = '<p class="no-artworks">还没有保存的作品</p>';
return;
}
artworks.forEach(art => {
const artworkDiv = document.createElement('div');
artworkDiv.classList.add('artwork');
artworkDiv.dataset.id = art.id;
const img = document.createElement('img');
img.src = art.dataURL;
img.alt = `像素艺术 ${new Date(art.timestamp).toLocaleString()}`;
const deleteBtn = document.createElement('div');
deleteBtn.classList.add('delete');
deleteBtn.textContent = '×';
deleteBtn.addEventListener('click', (e) => {
e.stopPropagation();
if (confirm('确定要删除这个作品吗?')) {
deleteArtwork(art.id);
}
});
artworkDiv.appendChild(img);
artworkDiv.appendChild(deleteBtn);
artworkDiv.addEventListener('click', () => loadArtwork(art));
savedArtworks.appendChild(artworkDiv);
});
}
function deleteArtwork(id) {
artworks = artworks.filter(art => art.id !== id);
localStorage.setItem('pixelArtworks', JSON.stringify(artworks));
displaySavedArtworks();
}
function loadArtwork(artwork) {
if (artwork.gridSize !== currentGridSize) {
if (confirm(`此作品的网格大小为 ${artwork.gridSize}×${artwork.gridSize},需要调整当前画布大小。继续吗?`)) {
gridSizeSelect.value = artwork.gridSize;
currentGridSize = artwork.gridSize;
createGrid(currentGridSize);
} else {
return;
}
}
if (artwork.pixelData) {
// 使用像素数据直接加载
const pixels = document.querySelectorAll('.pixel');
pixels.forEach((pixel, index) => {
if (index < artwork.pixelData.length) {
pixel.style.backgroundColor = artwork.pixelData[index];
}
});
} else {
// 旧版本兼容:使用图像数据加载
loadArtworkFromImage(artwork);
}
}
function loadArtworkFromImage(artwork) {
// 创建临时图像以加载像素颜色
const img = new Image();
img.src = artwork.dataURL;
img.onload = function() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
const pixels = document.querySelectorAll('.pixel');
const pixelSize = img.width / artwork.gridSize;
pixels.forEach((pixel, index) => {
const row = Math.floor(index / artwork.gridSize);
const col = index % artwork.gridSize;
// 从每个像素的中心获取颜色
const x = Math.floor(col * pixelSize + pixelSize / 2);
const y = Math.floor(row * pixelSize + pixelSize / 2);
const pixelData = ctx.getImageData(x, y, 1, 1).data;
const r = pixelData[0];
const g = pixelData[1];
const b = pixelData[2];
const a = pixelData[3] / 255;
if (a === 0) {
pixel.style.backgroundColor = 'white';
} else {
pixel.style.backgroundColor = `rgba(${r}, ${g}, ${b}, ${a})`;
}
});
};
}
function loadSavedArtwork() {
if (artworks.length > 0) {
// 加载最近的作品
loadArtwork(artworks[artworks.length - 1]);
} else {
alert('没有可加载的作品');
}
}
function exportAsImage() {
// 创建高分辨率图像
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const size = currentGridSize;
// 每个像素使用更大的尺寸以获得更高分辨率
const pixelSize = Math.max(5, Math.floor(1024 / size));
canvas.width = size * pixelSize;
canvas.height = size * pixelSize;
// 绘制像素
const pixels = document.querySelectorAll('.pixel');
pixels.forEach((pixel, index) => {
const row = Math.floor(index / size);
const col = index % size;
ctx.fillStyle = pixel.style.backgroundColor || 'white';
ctx.fillRect(col * pixelSize, row * pixelSize, pixelSize, pixelSize);
});
// 导出为PNG
const link = document.createElement('a');
link.download = `pixel-art-${Date.now()}.png`;
link.href = canvas.toDataURL('image/png');
link.click();
}
function exportAsJSON() {
// 收集像素数据
const pixels = document.querySelectorAll('.pixel');
const pixelData = Array.from(pixels).map(pixel => pixel.style.backgroundColor || 'white');
const artData = {
gridSize: currentGridSize,
pixelData: pixelData,
timestamp: new Date().toISOString(),
version: '1.0'
};
// 创建下载链接
const dataStr = JSON.stringify(artData);
const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
const link = document.createElement('a');
link.download = `pixel-art-${Date.now()}.json`;
link.href = dataUri;
link.click();
}
function handleImport(e) {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
if (file.name.endsWith('.json')) {
// 导入JSON数据
reader.onload = function(event) {
try {
const jsonData = JSON.parse(event.target.result);
if (jsonData.gridSize && jsonData.pixelData) {
importJSON(jsonData);
} else {
alert('无效的JSON数据格式');
}
} catch (err) {
alert('无法解析JSON文件');
console.error(err);
}
};
reader.readAsText(file);
} else {
// 导入图像
reader.onload = function(event) {
importImage(event.target.result);
};
reader.readAsDataURL(file);
}
// 清除input以便于重复导入同一文件
e.target.value = '';
}
function importJSON(data) {
if (data.gridSize !== currentGridSize) {
if (confirm(`此设计的网格大小为 ${data.gridSize}×${data.gridSize},需要调整当前画布大小。继续吗?`)) {
gridSizeSelect.value = data.gridSize;
currentGridSize = data.gridSize;
createGrid(currentGridSize);
} else {
return;
}
}
const pixels = document.querySelectorAll('.pixel');
pixels.forEach((pixel, index) => {
if (index < data.pixelData.length) {
pixel.style.backgroundColor = data.pixelData[index];
}
});
}
function importImage(dataURL) {
const img = new Image();
img.src = dataURL;
img.onload = function() {
// 确定合适的网格大小
let gridSize = 32; // 默认大小
// 尝试自动检测最佳网格大小
if (img.width > 128 || img.height > 128) {
if (confirm('导入的图像较大。是否要使用128×128网格?')) {
gridSize = 128;
} else if (confirm('是否要使用64×64网格?')) {
gridSize = 64;
} else {
gridSize = 32;
}
} else if (img.width > 64 || img.height > 64) {
if (confirm('是否要使用64×64网格?')) {
gridSize = 64;
} else {
gridSize = 32;
}
}
// 更新网格大小
gridSizeSelect.value = gridSize;
currentGridSize = gridSize;
createGrid(gridSize);
// 创建画布并绘制图像
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 计算保持纵横比的尺寸
let drawWidth, drawHeight;
if (img.width > img.height) {
drawWidth = gridSize;
drawHeight = (img.height / img.width) * gridSize;
} else {
drawHeight = gridSize;
drawWidth = (img.width / img.height) * gridSize;
}
// 居中图像
const offsetX = (gridSize - drawWidth) / 2;
const offsetY = (gridSize - drawHeight) / 2;
canvas.width = gridSize;
canvas.height = gridSize;
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
// 将图像数据映射到像素网格
const pixels = document.querySelectorAll('.pixel');
pixels.forEach((pixel, index) => {
const row = Math.floor(index / gridSize);
const col = index % gridSize;
const pixelData = ctx.getImageData(col, row, 1, 1).data;
const r = pixelData[0];
const g = pixelData[1];
const b = pixelData[2];
const a = pixelData[3] / 255;
if (a < 0.1) {
pixel.style.backgroundColor = 'white';
} else {
pixel.style.backgroundColor = `rgba(${r}, ${g}, ${b}, ${a})`;
}
});
};
}
// 添加插值填充函数
function fillPixelsBetween(fromIndex, toIndex, color) {
// 获取网格中的所有像素
const pixels = document.querySelectorAll('.pixel');
// 计算网格中的行和列
const row1 = Math.floor(fromIndex / currentGridSize);
const col1 = fromIndex % currentGridSize;
const row2 = Math.floor(toIndex / currentGridSize);
const col2 = toIndex % currentGridSize;
// 使用Bresenham算法进行线段插值
const dx = Math.abs(col2 - col1);
const dy = Math.abs(row2 - row1);
const sx = col1 < col2 ? 1 : -1;
const sy = row1 < row2 ? 1 : -1;
let err = dx - dy;
let x = col1;
let y = row1;
// 最大插值数量限制,防止过度计算
const maxInterpolations = Math.max(dx, dy) * 2;
let count = 0;
while ((x !== col2 || y !== row2) && count < maxInterpolations) {
const currentIndex = y * currentGridSize + x;
// 确保索引在有效范围内
if (currentIndex >= 0 && currentIndex < pixels.length) {
pixels[currentIndex].style.backgroundColor = color;
}
const e2 = 2 * err;
if (e2 > -dy) {
err -= dy;
x += sx;
}
if (e2 < dx) {
err += dx;
y += sy;
}
count++;
}
}
});