Download script.js from ssboost/swq2f: direct link, hf CLI and curl.
- Browser
- Download file 52.9 kB
-
https://huggingface.co/spaces/ssboost/swq2f/resolve/main/script.js
- Command line
-
hf download hf://spaces/ssboost/swq2f/script.js
-
curl -L -o script.js https://huggingface.co/spaces/ssboost/swq2f/resolve/main/script.js
52.9 kB
| /** | |
| * ์ด๋ฏธ์ง ํฉ์ฑ ๋ฐ ํธ์ง ๊ธฐ๋ฅ์ ์ํ ์๋ฐ์คํฌ๋ฆฝํธ ํ์ผ | |
| */ | |
| // ์์ ๊ฐ์ ธ์ค๊ธฐ | |
| const backgroundInput = document.getElementById('background-input'); | |
| const overlayInput = document.getElementById('overlay-input'); | |
| const canvas = document.getElementById('canvas'); | |
| const ctx = canvas.getContext('2d'); | |
| const scaleSlider = document.getElementById('scale-slider'); | |
| const scaleValue = document.getElementById('scale-value'); | |
| const rotationSlider = document.getElementById('rotation-slider'); | |
| const rotationValue = document.getElementById('rotation-value'); | |
| // ํํฐ ๊ด๋ จ ์์ | |
| const temperatureSlider = document.getElementById('temperature-slider'); | |
| const temperatureValue = document.getElementById('temperature-value'); | |
| const brightnessSlider = document.getElementById('brightness-slider'); | |
| const brightnessValue = document.getElementById('brightness-value'); | |
| const contrastSlider = document.getElementById('contrast-slider'); | |
| const contrastValue = document.getElementById('contrast-value'); | |
| const saturationSlider = document.getElementById('saturation-slider'); | |
| const saturationValue = document.getElementById('saturation-value'); | |
| const resetFilterBtn = document.getElementById('reset-filter-btn'); | |
| const generateBtn = document.getElementById('generate-btn'); | |
| const resetAllBtn = document.getElementById('reset-all-btn'); | |
| const downloadBtn = document.getElementById('download-btn'); | |
| const deleteLayerBtn = document.getElementById('delete-layer-btn'); // ์ถ๊ฐ | |
| const previewContainer = document.getElementById('preview-container'); | |
| const previewImg = document.getElementById('preview-img'); | |
| const layersList = document.getElementById('layers-list'); | |
| // ์บ๋ฒ์ค ๊ธฐ๋ณธ ํฌ๊ธฐ | |
| const CANVAS_WIDTH = 800; | |
| const CANVAS_HEIGHT = 600; | |
| canvas.width = CANVAS_WIDTH; | |
| canvas.height = CANVAS_HEIGHT; | |
| // ์ ์ญ ๋ณ์ | |
| let backgroundImage = null; | |
| let originalFormat = 'png'; // ๊ธฐ๋ณธ ํฌ๋งท์ PNG๋ก ์ค์ | |
| let canvasAdjustedHeight = CANVAS_HEIGHT; // ์ด๋ฏธ์ง ๋น์จ์ ๋ง๊ฒ ์กฐ์ ๋ ์บ๋ฒ์ค ๋์ด | |
| // ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง ๊ทธ๋ฆฌ๊ธฐ ์์ฑ | |
| let bgDrawProps = { x: 0, y: 0, width: CANVAS_WIDTH, height: CANVAS_HEIGHT }; | |
| // ์ฌ๋ฌ ๊ฐ์ ์ค๋ฒ๋ ์ด ์ด๋ฏธ์ง์ ๊ทธ ์์ฑ์ ์ ์ฅํ ๋ฐฐ์ด | |
| let overlays = []; | |
| // ํ์ฌ ์กฐ์ ์ค์ธ ์ค๋ฒ๋ ์ด ์ด๋ฏธ์ง์ ์ธ๋ฑ์ค (-1์ด๋ฉด ์ ํ๋์ง ์์) | |
| let activeOverlayIndex = -1; | |
| // ๋ง์ฐ์ค/ํฐ์น ์กฐ์ ๊ด๋ จ ์ํ | |
| let isDragging = false; | |
| let isRotating = false; | |
| let isResizing = false; | |
| let activeHandle = null; | |
| // ์ด์ ๋ง์ฐ์ค/ํฐ์น ์ขํ | |
| let lastX = 0; | |
| let lastY = 0; | |
| // ๋ฆฌ์ฌ์ด์ฆ ์์ ์ ํธ๋ค๊น์ง์ ๊ฑฐ๋ฆฌ (๋น๋ก ์ค์ผ์ผ๋ง์ ์ฌ์ฉ) | |
| let resizeStartDistance = 0; | |
| // ํธ๋ค ์ค์ | |
| const handleSize = 15; | |
| const rotateHandleDistance = 30; | |
| // ํด๋ฆญ ๊ฐ์ง ์ ์ฌ์ ๊ณต๊ฐ | |
| const clickPadding = 5; | |
| // ์บ๋ฒ์ค ์ด๊ธฐํ (ํฐ์ ๋ฐฐ๊ฒฝ) | |
| function initCanvas() { | |
| ctx.fillStyle = '#ffffff'; | |
| ctx.fillRect(0, 0, CANVAS_WIDTH, canvasAdjustedHeight); | |
| updateLayersList(); | |
| } | |
| function cropTransparentPixels(imageElement, alphaThreshold = 0) { | |
| const tempCanvas = document.createElement('canvas'); | |
| tempCanvas.width = imageElement.naturalWidth || imageElement.width; | |
| tempCanvas.height = imageElement.naturalHeight || imageElement.height; | |
| const tempCtx = tempCanvas.getContext('2d'); | |
| tempCtx.drawImage(imageElement, 0, 0); | |
| let imageData; | |
| try { | |
| imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height); | |
| } catch (e) { | |
| console.error("Error getting ImageData (possibly due to CORS):", e); | |
| // CORS ์ค๋ฅ ๋ฐ์ ์, ๋ณด์ ์ ์ฝ ์์ด ์ด๋ฏธ์ง ์ ์ฒด๋ฅผ ์ฌ์ฉํ๋ ๋์ฒด ๋ก์ง (๋ฐฐ๊ฒฝ ์ ๊ฑฐ ํจ๊ณผ๋ ์์ ์ ์์) | |
| const fallbackCanvas = document.createElement('canvas'); | |
| fallbackCanvas.width = tempCanvas.width; | |
| fallbackCanvas.height = tempCanvas.height; | |
| const fallbackCtx = fallbackCanvas.getContext('2d'); | |
| fallbackCtx.drawImage(imageElement, 0, 0); | |
| return fallbackCanvas; | |
| } | |
| const data = imageData.data; | |
| let minX = tempCanvas.width, minY = tempCanvas.height, maxX = 0, maxY = 0; | |
| let foundContent = false; | |
| for (let y = 0; y < tempCanvas.height; y++) { | |
| for (let x = 0; x < tempCanvas.width; x++) { | |
| const alpha = data[(y * tempCanvas.width + x) * 4 + 3]; | |
| if (alpha > alphaThreshold) { | |
| minX = Math.min(minX, x); | |
| minY = Math.min(minY, y); | |
| maxX = Math.max(maxX, x); | |
| maxY = Math.max(maxY, y); | |
| foundContent = true; | |
| } | |
| } | |
| } | |
| if (!foundContent) { | |
| // ๋ด์ฉ์ด ์๋ ๊ฒฝ์ฐ (์์ ํ ํฌ๋ช ํ๊ฑฐ๋ ์ค๋ฅ) | |
| const emptyCanvas = document.createElement('canvas'); | |
| emptyCanvas.width = imageElement.naturalWidth || 1; | |
| emptyCanvas.height = imageElement.naturalHeight || 1; | |
| return emptyCanvas; | |
| } | |
| const cropWidth = maxX - minX + 1; | |
| const cropHeight = maxY - minY + 1; | |
| const croppedCanvas = document.createElement('canvas'); | |
| croppedCanvas.width = cropWidth; | |
| croppedCanvas.height = cropHeight; | |
| const croppedCtx = croppedCanvas.getContext('2d'); | |
| // ์๋ผ๋ธ ์์ญ๋ง ์ ์บ๋ฒ์ค์ ๊ทธ๋ฆฝ๋๋ค. | |
| croppedCtx.drawImage(tempCanvas, minX, minY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight); | |
| return croppedCanvas; | |
| } | |
| // ํ์ผ ๋๋ Blob ๊ฐ์ฒด๋ฅผ ๋ฐ์ ์ด๋ฏธ์ง๋ฅผ ๋ก๋ํ๊ณ ์บ๋ฒ์ค์ ์ถ๊ฐํ๋ ํจ์ | |
| function loadImage(fileOrBlob, type) { | |
| if (!fileOrBlob) return; | |
| let fileExtension = 'png'; // ๊ธฐ๋ณธ๊ฐ | |
| let isBlob = fileOrBlob instanceof Blob; | |
| let fileName = 'unknown'; | |
| if (!isBlob && fileOrBlob.name) { | |
| fileExtension = fileOrBlob.name.split('.').pop().toLowerCase(); | |
| fileName = fileOrBlob.name; | |
| } else if (isBlob && fileOrBlob.type) { | |
| fileExtension = fileOrBlob.type.split('/')[1] ? fileOrBlob.type.split('/')[1].toLowerCase() : 'png'; | |
| fileName = 'processed_image.' + fileExtension; // Blob์ ๊ฒฝ์ฐ ํ์ผ ์ด๋ฆ ์์ ์ง์ | |
| } | |
| if (type === 'background' && !isBlob && ['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(fileExtension)) { | |
| originalFormat = fileExtension; | |
| } | |
| const reader = new FileReader(); | |
| reader.onload = function(event) { | |
| const img = new Image(); | |
| img.onload = function() { | |
| if (type === 'background') { | |
| backgroundImage = img; | |
| const aspectRatio = img.width / img.height; | |
| bgDrawProps.width = CANVAS_WIDTH; | |
| bgDrawProps.height = CANVAS_WIDTH / aspectRatio; | |
| bgDrawProps.x = 0; | |
| bgDrawProps.y = 0; | |
| canvasAdjustedHeight = bgDrawProps.height; | |
| canvas.height = canvasAdjustedHeight; | |
| canvas.style.height = 'auto'; // ์บ๋ฒ์ค wrapper๊ฐ ์๋ canvas ์์ฒด์ style | |
| } else if (type === 'overlay') { | |
| let imageToUse = img; | |
| // PNG ๋๋ ํฌ๋ช ๋๊ฐ ์๋ ์ด๋ฏธ์ง ํ์์ ๊ฒฝ์ฐ์๋ง ํฌ๋ช ํฝ์ ์๋ฅด๊ธฐ ์๋ | |
| // ์์ ์ด๋ฏธ์ง๋ ๋ฐฐ๊ฒฝ ์ ๊ฑฐ๊ฐ ์๋ฃ๋ ์ด๋ฏธ์ง๋ผ๊ณ ๊ฐ์ ํ๊ฑฐ๋, | |
| // ์ฌ๊ธฐ์์ cropTransparentPixels๋ฅผ ์ ์ฉํฉ๋๋ค. | |
| // ์ค์ ๋ณต์กํ ๋ฐฐ๊ฒฝ ์ ๊ฑฐ ๋ก์ง์ ์ด ๋ถ๋ถ์ด ์๋ ๋ณ๋์ ํจ์์์ ๊ตฌํ๋์ด์ผ ํฉ๋๋ค. | |
| if (fileExtension === 'png' || fileExtension === 'webp' || fileExtension === 'gif' || (isBlob && fileOrBlob.type === 'image/png')) { | |
| try { | |
| // cropTransparentPixels๋ ํฌ๋ช ์์ญ์ ์๋ฅด๋ ๊ธฐ๋ฅ์ ๋๋ค. | |
| // ๋ง์ฝ ์์ ์ด๋ฏธ์ง๊ฐ ์ด๋ฏธ ๋ฐฐ๊ฒฝ ์ ๊ฑฐ๋ ์ด๋ฏธ์ง๋ผ๋ฉด ์ด ํจ์๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. | |
| // ์ผ๋ฐ ์ด๋ฏธ์ง์ ๋ฐฐ๊ฒฝ์ ์ ๊ฑฐํ๋ ค๋ฉด ๋ค๋ฅธ ์๊ณ ๋ฆฌ์ฆ์ด ํ์ํฉ๋๋ค. | |
| const croppedImageCanvas = cropTransparentPixels(img); | |
| if (croppedImageCanvas.width > 0 && croppedImageCanvas.height > 0) { | |
| imageToUse = croppedImageCanvas; | |
| } else { | |
| // ์๋ผ๋ผ ์์ญ์ด ์์ผ๋ฉด ์๋ณธ ์ฌ์ฉ ๋๋ ๋น ์ด๋ฏธ์ง ์ฒ๋ฆฌ | |
| console.warn("No non-transparent pixels found, using original or empty."); | |
| // imageToUse = img; // ์๋ณธ ์ด๋ฏธ์ง ์ฌ์ฉ | |
| const emptyCanvas = document.createElement('canvas'); | |
| emptyCanvas.width = img.naturalWidth || 1; | |
| emptyCanvas.height = img.naturalHeight || 1; | |
| imageToUse = emptyCanvas; // ๋๋ ๋น ์บ๋ฒ์ค ์ฌ์ฉ | |
| } | |
| } catch (e) { | |
| console.error("Error during image cropping/processing, using original:", e); | |
| imageToUse = img; // ์ค๋ฅ ๋ฐ์ ์ ์๋ณธ ์ฌ์ฉ | |
| } | |
| } | |
| const newOverlay = { | |
| image: imageToUse, | |
| // ์บ๋ฒ์ค ์ค์์ ๋ฐฐ์น | |
| x: CANVAS_WIDTH / 2, | |
| y: canvasAdjustedHeight / 2, | |
| scale: 1, | |
| rotation: 0, | |
| filters: { ...ImageFilters.DEFAULT_FILTERS } | |
| }; | |
| // ์ด๋ฏธ์ง๊ฐ ์บ๋ฒ์ค๋ณด๋ค ํฌ๋ฉด ์ด๊ธฐ ์ค์ผ์ผ ์กฐ์ | |
| const initialScaleRatio = Math.min(CANVAS_WIDTH / newOverlay.image.width, canvasAdjustedHeight / newOverlay.image.height); | |
| if (initialScaleRatio < 1) { | |
| newOverlay.scale = initialScaleRatio * 0.9; // ์ฝ๊ฐ ๋ ์๊ฒ ์์ | |
| } | |
| overlays.push(newOverlay); | |
| activeOverlayIndex = overlays.length - 1; // ์๋ก ์ถ๊ฐ๋ ๋ ์ด์ด๋ฅผ ํ์ฑํ | |
| updateControlPanel(); | |
| updateLayersList(); | |
| } | |
| drawCanvas(); | |
| }; | |
| img.onerror = function() { | |
| console.error("Error loading image from reader result for:", fileName); | |
| alert("์ด๋ฏธ์ง๋ฅผ ๋ก๋ํ๋ ๋ฐ ์คํจํ์ต๋๋ค. ํ์ผ์ด ์ ํจํ์ง ํ์ธํด์ฃผ์ธ์."); | |
| }; | |
| img.src = event.target.result; | |
| }; | |
| reader.readAsDataURL(fileOrBlob); // File ๋๋ Blob์์ Data URL ์ฝ๊ธฐ | |
| } | |
| function updateControlPanel() { | |
| const isActive = activeOverlayIndex >= 0; | |
| const activeOverlay = isActive ? overlays[activeOverlayIndex] : null; | |
| // ์ค๋ฒ๋ ์ด๊ฐ ์ ํ๋์ง ์์์ ๋ ์ปจํธ๋กค ๋นํ์ฑํ | |
| scaleSlider.disabled = !isActive; | |
| rotationSlider.disabled = !isActive; | |
| temperatureSlider.disabled = !isActive; | |
| brightnessSlider.disabled = !isActive; | |
| contrastSlider.disabled = !isActive; | |
| saturationSlider.disabled = !isActive; | |
| resetFilterBtn.disabled = !isActive; | |
| deleteLayerBtn.disabled = !isActive; // ์ญ์ ๋ฒํผ๋ ๋นํ์ฑํ | |
| if (isActive) { | |
| scaleSlider.value = Math.round(activeOverlay.scale * 100); | |
| scaleValue.textContent = scaleSlider.value + '%'; | |
| rotationSlider.value = Math.round(activeOverlay.rotation); | |
| rotationValue.textContent = rotationSlider.value + 'ยฐ'; | |
| temperatureSlider.value = activeOverlay.filters.temperature; | |
| temperatureValue.textContent = activeOverlay.filters.temperature; | |
| brightnessSlider.value = activeOverlay.filters.brightness; | |
| brightnessValue.textContent = activeOverlay.filters.brightness + '%'; | |
| contrastSlider.value = activeOverlay.filters.contrast; | |
| contrastValue.textContent = activeOverlay.filters.contrast + '%'; | |
| saturationSlider.value = activeOverlay.filters.saturation; | |
| saturationValue.textContent = activeOverlay.filters.saturation + '%'; | |
| } else { | |
| // ์ค๋ฒ๋ ์ด๊ฐ ์ ํ๋์ง ์์ผ๋ฉด ๊ธฐ๋ณธ๊ฐ์ผ๋ก ํ์ | |
| scaleSlider.value = 100; | |
| scaleValue.textContent = '100%'; | |
| rotationSlider.value = 0; | |
| rotationValue.textContent = '0ยฐ'; | |
| temperatureSlider.value = 0; | |
| temperatureValue.textContent = '0'; | |
| brightnessSlider.value = 100; | |
| brightnessValue.textContent = '100%'; | |
| contrastSlider.value = 100; | |
| contrastValue.textContent = '100%'; | |
| saturationSlider.value = 100; | |
| saturationValue.textContent = '100%'; | |
| } | |
| // ์ค๋ฒ๋ ์ด๊ฐ ํ๋๋ผ๋ ์์ ๋๋ง ์์ฑ/๋ค์ด๋ก๋ ๋ฒํผ ํ์ฑํ | |
| generateBtn.disabled = overlays.length === 0; | |
| downloadBtn.disabled = overlays.length === 0; | |
| // ์ค๋ฒ๋ ์ด๊ฐ ์์ผ๋ฉด ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์์ญ ์จ๊น | |
| if (overlays.length === 0) { | |
| previewContainer.style.display = 'none'; | |
| } | |
| } | |
| function updateLayersList() { | |
| if (!layersList) return; // layersList ์์๊ฐ ์์ผ๋ฉด ํจ์ ์ข ๋ฃ | |
| layersList.innerHTML = ''; // ๋ชฉ๋ก ์ด๊ธฐํ | |
| // ์ญ์ ๋ฒํผ ํ์ฑํ/๋นํ์ฑํ ์ค์ | |
| if (deleteLayerBtn) { | |
| deleteLayerBtn.disabled = activeOverlayIndex < 0; | |
| } | |
| if (overlays.length === 0) { | |
| const emptyMsg = document.createElement('div'); | |
| emptyMsg.style.padding = '8px'; | |
| emptyMsg.style.textAlign = 'center'; | |
| emptyMsg.style.color = '#777'; | |
| emptyMsg.textContent = '๋ ์ด์ด๊ฐ ์์ต๋๋ค'; | |
| layersList.appendChild(emptyMsg); | |
| return; | |
| } | |
| // ๋ ์ด์ด ๋ชฉ๋ก์ ์ญ์์ผ๋ก ํ์ (๊ฐ์ฅ ์์ ์๋ ๋ ์ด์ด๊ฐ ๋ชฉ๋ก์ ์๋์ ์ค๋๋ก) | |
| for (let i = overlays.length - 1; i >= 0; i--) { | |
| const layerItem = document.createElement('div'); | |
| layerItem.className = 'layer-item'; | |
| if (i === activeOverlayIndex) { | |
| layerItem.classList.add('active'); // ํ์ฑ ๋ ์ด์ด ํ์ | |
| } | |
| const layerName = document.createElement('span'); | |
| layerName.className = 'layer-name'; | |
| layerName.textContent = `๋ ์ด์ด ${i + 1}`; // ๋ ์ด์ด ๋ฒํธ ํ์ (1๋ถํฐ ์์) | |
| const layerControls = document.createElement('div'); | |
| layerControls.className = 'layer-controls'; | |
| // ์๋ก ์ด๋ ๋ฒํผ (๋งจ ์ ๋ ์ด์ด ์ ์ธ) | |
| if (i < overlays.length - 1) { | |
| const upButton = document.createElement('button'); | |
| upButton.className = 'layer-button'; | |
| upButton.textContent = 'โ'; | |
| upButton.title = '์๋ก ์ด๋'; | |
| upButton.addEventListener('click', (e) => { e.stopPropagation(); moveLayerUp(i); }); // ์ด๋ฒคํธ ๋ฒ๋ธ๋ง ๋ฐฉ์ง | |
| layerControls.appendChild(upButton); | |
| } | |
| // ์๋๋ก ์ด๋ ๋ฒํผ (๋งจ ์๋ ๋ ์ด์ด ์ ์ธ) | |
| if (i > 0) { | |
| const downButton = document.createElement('button'); | |
| downButton.className = 'layer-button'; | |
| downButton.textContent = 'โ'; | |
| downButton.title = '์๋๋ก ์ด๋'; | |
| downButton.addEventListener('click', (e) => { e.stopPropagation(); moveLayerDown(i); }); // ์ด๋ฒคํธ ๋ฒ๋ธ๋ง ๋ฐฉ์ง | |
| layerControls.appendChild(downButton); | |
| } | |
| // ๋ ์ด์ด ์ญ์ ๋ฒํผ ์ถ๊ฐ | |
| const deleteButton = document.createElement('button'); | |
| deleteButton.className = 'delete-button'; | |
| deleteButton.textContent = 'ร'; | |
| deleteButton.title = '๋ ์ด์ด ์ญ์ (๋จ์ถํค: Del)'; | |
| deleteButton.addEventListener('click', (e) => { | |
| e.stopPropagation(); // ์ด๋ฒคํธ ๋ฒ๋ธ๋ง ๋ฐฉ์ง | |
| // ํ์ฌ ๋ ์ด์ด ์ธ๋ฑ์ค๋ฅผ ์ค์ ํ๊ณ ์ญ์ | |
| activeOverlayIndex = i; | |
| deleteSelectedLayer(); | |
| }); | |
| layerControls.appendChild(deleteButton); | |
| // ๋ ์ด์ด ํญ๋ชฉ ํด๋ฆญ ์ ํด๋น ๋ ์ด์ด ํ์ฑํ | |
| layerItem.addEventListener('click', function() { | |
| activeOverlayIndex = i; | |
| updateControlPanel(); // ์ปจํธ๋กค ํจ๋ ์ ๋ฐ์ดํธ | |
| updateLayersList(); // ๋ ์ด์ด ๋ชฉ๋ก ์๊ฐ์ ์ ๋ฐ์ดํธ | |
| drawCanvas(); // ์บ๋ฒ์ค ๋ค์ ๊ทธ๋ฆฌ๊ธฐ (ํ์ฑ ๋ ์ด์ด ํธ๋ค ํ์) | |
| }); | |
| layerItem.appendChild(layerName); | |
| layerItem.appendChild(layerControls); | |
| layersList.appendChild(layerItem); | |
| } | |
| } | |
| // ๋ ์ด์ด ์๋ก ์ด๋ | |
| function moveLayerUp(index) { | |
| if (index < overlays.length - 1) { | |
| // ํ์ฌ ๋ ์ด์ด์ ๋ฐ๋ก ์ ๋ ์ด์ด์ ์์น๋ฅผ ๋ฐ๊ฟ๋๋ค. | |
| const temp = overlays[index]; | |
| overlays[index] = overlays[index + 1]; | |
| overlays[index + 1] = temp; | |
| // ํ์ฑ ๋ ์ด์ด์ ์ธ๋ฑ์ค๋ ์กฐ์ ํฉ๋๋ค. | |
| if (activeOverlayIndex === index) activeOverlayIndex = index + 1; | |
| else if (activeOverlayIndex === index + 1) activeOverlayIndex = index; | |
| updateLayersList(); // ๋ชฉ๋ก ์ ๋ฐ์ดํธ | |
| drawCanvas(); // ์บ๋ฒ์ค ๋ค์ ๊ทธ๋ฆฌ๊ธฐ | |
| } | |
| } | |
| // ๋ ์ด์ด ์๋๋ก ์ด๋ | |
| function moveLayerDown(index) { | |
| if (index > 0) { | |
| // ํ์ฌ ๋ ์ด์ด์ ๋ฐ๋ก ์๋ ๋ ์ด์ด์ ์์น๋ฅผ ๋ฐ๊ฟ๋๋ค. | |
| const temp = overlays[index]; | |
| overlays[index] = overlays[index - 1]; | |
| overlays[index - 1] = temp; | |
| // ํ์ฑ ๋ ์ด์ด์ ์ธ๋ฑ์ค๋ ์กฐ์ ํฉ๋๋ค. | |
| if (activeOverlayIndex === index) activeOverlayIndex = index - 1; | |
| else if (activeOverlayIndex === index - 1) activeOverlayIndex = index; | |
| updateLayersList(); // ๋ชฉ๋ก ์ ๋ฐ์ดํธ | |
| drawCanvas(); // ์บ๋ฒ์ค ๋ค์ ๊ทธ๋ฆฌ๊ธฐ | |
| } | |
| } | |
| function drawCanvas() { | |
| // ์บ๋ฒ์ค ์ ์ฒด ์ง์ฐ๊ธฐ | |
| ctx.clearRect(0, 0, CANVAS_WIDTH, canvasAdjustedHeight); | |
| // ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง ๊ทธ๋ฆฌ๊ธฐ (์๋ค๋ฉด) | |
| if (backgroundImage) { | |
| ctx.drawImage(backgroundImage, 0, 0, backgroundImage.width, backgroundImage.height, bgDrawProps.x, bgDrawProps.y, bgDrawProps.width, bgDrawProps.height); | |
| } else { | |
| // ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง๊ฐ ์์ผ๋ฉด ํฐ์ ๋ฐฐ๊ฒฝ ์ฑ์ฐ๊ธฐ | |
| ctx.fillStyle = '#ffffff'; | |
| ctx.fillRect(0, 0, CANVAS_WIDTH, canvasAdjustedHeight); | |
| } | |
| // ์ค๋ฒ๋ ์ด ์ด๋ฏธ์ง๋ค ๊ทธ๋ฆฌ๊ธฐ (๋ ์ด์ด ์์๋๋ก) | |
| overlays.forEach((overlay, index) => { | |
| // ์ด๋ฏธ์ง๊ฐ ์ ํจํ์ง ์์ผ๋ฉด ๊ฑด๋๋๋๋ค. | |
| if (!overlay.image || overlay.image.width === 0 || overlay.image.height === 0) return; | |
| // ํํฐ ์ ์ฉ๋ ์ด๋ฏธ์ง ์บ๋ฒ์ค ์์ฑ (filter.js์ ํจ์ ์ฌ์ฉ) | |
| const filteredCanvas = ImageFilters.createFilteredCanvas(overlay.image, overlay.filters); | |
| ctx.save(); // ํ์ฌ ์บ๋ฒ์ค ์ํ ์ ์ฅ | |
| // ์ค๋ฒ๋ ์ด ์ด๋ฏธ์ง์ ์ค์ฌ์ ๊ธฐ์ค์ผ๋ก ๋ณํ ์ ์ฉ | |
| ctx.translate(overlay.x, overlay.y); // ์์น ์ด๋ | |
| ctx.rotate(overlay.rotation * Math.PI / 180); // ํ์ (๋ผ๋์์ผ๋ก ๋ณํ) | |
| ctx.scale(overlay.scale, overlay.scale); // ํฌ๊ธฐ ์กฐ์ | |
| // ์ด๋ฏธ์ง๋ฅผ ์ค์ฌ ๊ธฐ์ค์ผ๋ก ๊ทธ๋ฆฝ๋๋ค. (translate์์ ์ด๋ํ์ผ๋ฏ๋ก -width/2, -height/2) | |
| ctx.drawImage(filteredCanvas, -overlay.image.width / 2, -overlay.image.height / 2); | |
| ctx.restore(); // ์ด์ ์บ๋ฒ์ค ์ํ ๋ณต์ (๋ณํ ์ํ ์ด๊ธฐํ) | |
| // ํ์ฌ ํ์ฑ ๋ ์ด์ด์๋ง ๋ณํ ํธ๋ค ๊ทธ๋ฆฌ๊ธฐ | |
| if (index === activeOverlayIndex) { | |
| drawTransformHandles(overlay); | |
| } | |
| }); | |
| } | |
| // ๋ณํ ํธ๋ค ๊ทธ๋ฆฌ๊ธฐ | |
| function drawTransformHandles(activeOverlay) { | |
| // ํ์ฑ ์ค๋ฒ๋ ์ด๋ ์ด๋ฏธ์ง๊ฐ ์ ํจํ์ง ์์ผ๋ฉด ๊ทธ๋ฆฌ์ง ์์ต๋๋ค. | |
| if (!activeOverlay || !activeOverlay.image || activeOverlay.image.width === 0 || activeOverlay.image.height === 0) return; | |
| const currentWidth = activeOverlay.image.width * activeOverlay.scale; | |
| const currentHeight = activeOverlay.image.height * activeOverlay.scale; | |
| const halfWidth = currentWidth / 2; | |
| const halfHeight = currentHeight / 2; | |
| const angle = activeOverlay.rotation * Math.PI / 180; // ํ์ฌ ํ์ ๊ฐ๋ (๋ผ๋์) | |
| // ์ฝ๋ ํธ๋ค ์์น (ํ์ ๋์ง ์์ ์ํ ๊ธฐ์ค) | |
| const handles = { | |
| 'tl': { x: -halfWidth, y: -halfHeight, cursor: 'nwse-resize' }, // Top-Left | |
| 'tr': { x: halfWidth, y: -halfHeight, cursor: 'nesw-resize' }, // Top-Right | |
| 'br': { x: halfWidth, y: halfHeight, cursor: 'nwse-resize' }, // Bottom-Right | |
| 'bl': { x: -halfWidth, y: halfHeight, cursor: 'nesw-resize' } // Bottom-Left | |
| }; | |
| // ํ์ ํธ๋ค ์์น (์ด๋ฏธ์ง ์๋จ ์ค์์์ ํ์ ๊ฑฐ๋ฆฌ๋งํผ ๋จ์ด์ง ์์น) | |
| const rotateHandlePos = { x: 0, y: -halfHeight - rotateHandleDistance }; | |
| ctx.save(); // ํ์ฌ ์บ๋ฒ์ค ์ํ ์ ์ฅ | |
| // ์ค๋ฒ๋ ์ด ์ด๋ฏธ์ง์ ์ค์ฌ ์์น๋ก ์ด๋ ํ ํ์ | |
| ctx.translate(activeOverlay.x, activeOverlay.y); | |
| ctx.rotate(angle); | |
| // ์ฝ๋ ํธ๋ค ๊ทธ๋ฆฌ๊ธฐ | |
| const cornerHandleColor = '#e74c3c'; // ๋นจ๊ฐ์ | |
| for (const [, handle] of Object.entries(handles)) { | |
| ctx.beginPath(); | |
| // ํธ๋ค ์์น์ ์ฌ๊ฐํ ๊ทธ๋ฆฌ๊ธฐ (ํธ๋ค ํฌ๊ธฐ์ ์ ๋ฐ๋งํผ ๋นผ์ ์ค์์ ์์น) | |
| ctx.rect(handle.x - handleSize / 2, handle.y - handleSize / 2, handleSize, handleSize); | |
| ctx.fillStyle = cornerHandleColor; | |
| ctx.fill(); | |
| } | |
| // ํ์ ํธ๋ค ์ฐ๊ฒฐ ์ ๊ทธ๋ฆฌ๊ธฐ | |
| ctx.beginPath(); | |
| ctx.moveTo(0, -halfHeight); // ์ด๋ฏธ์ง ์๋จ ์ค์ | |
| ctx.lineTo(rotateHandlePos.x, rotateHandlePos.y); // ํ์ ํธ๋ค ์์น | |
| ctx.strokeStyle = '#3498db'; // ํ๋์ | |
| ctx.lineWidth = 2; | |
| ctx.stroke(); | |
| // ํ์ ํธ๋ค ์ ๊ทธ๋ฆฌ๊ธฐ | |
| ctx.beginPath(); | |
| ctx.arc(rotateHandlePos.x, rotateHandlePos.y, handleSize / 2, 0, Math.PI * 2); // ํ์ ํธ๋ค ์์น์ ์ ๊ทธ๋ฆฌ๊ธฐ | |
| ctx.fillStyle = '#3498db'; // ํ๋์ | |
| ctx.fill(); | |
| ctx.restore(); // ์ด์ ์บ๋ฒ์ค ์ํ ๋ณต์ | |
| } | |
| // ์ฃผ์ด์ง ์บ๋ฒ์ค ์ขํ์์ ์ด๋ค ํธ๋ค์ด ์๋์ง ํ์ธ | |
| function getHandleAtPosition(canvasX, canvasY, activeOverlay) { | |
| // ํ์ฑ ์ค๋ฒ๋ ์ด๋ ์ด๋ฏธ์ง๊ฐ ์ ํจํ์ง ์์ผ๋ฉด ํธ๋ค ์์ | |
| if (!activeOverlay || !activeOverlay.image || activeOverlay.image.width === 0 || activeOverlay.image.height === 0) return null; | |
| const currentWidth = activeOverlay.image.width * activeOverlay.scale; | |
| const currentHeight = activeOverlay.image.height * activeOverlay.scale; | |
| const halfWidth = currentWidth / 2; | |
| const halfHeight = currentHeight / 2; | |
| // ๋ง์ฐ์ค/ํฐ์น ์ขํ๋ฅผ ์ค๋ฒ๋ ์ด ์ด๋ฏธ์ง์ ์ค์ฌ์ ๊ธฐ์ค์ผ๋ก ํ๊ณ ํ์ ์ ๋๋๋ฆฝ๋๋ค. | |
| const dx = canvasX - activeOverlay.x; | |
| const dy = canvasY - activeOverlay.y; | |
| const angle = -activeOverlay.rotation * Math.PI / 180; // ํ์ ์ ๋๋๋ฆฌ๋ ๊ฐ๋ | |
| const cos = Math.cos(angle); | |
| const sin = Math.sin(angle); | |
| // ํ์ ๋ ์ขํ | |
| const rotatedX = dx * cos - dy * sin; | |
| const rotatedY = dx * sin + dy * cos; | |
| // ์ฝ๋ ํธ๋ค ์์น (ํ์ ๋์ง ์์ ์ํ ๊ธฐ์ค) | |
| const handles = { | |
| 'tl': { x: -halfWidth, y: -halfHeight, cursor: 'nwse-resize' }, | |
| 'tr': { x: halfWidth, y: -halfHeight, cursor: 'nesw-resize' }, | |
| 'br': { x: halfWidth, y: halfHeight, cursor: 'nwse-resize' }, | |
| 'bl': { x: -halfWidth, y: halfHeight, cursor: 'nesw-resize' } | |
| }; | |
| // ๊ฐ ์ฝ๋ ํธ๋ค ๊ทผ์ฒ์ ํด๋ฆญ/ํฐ์นํ๋์ง ํ์ธ | |
| for (const [key, handle] of Object.entries(handles)) { | |
| const dist = Math.sqrt(Math.pow(rotatedX - handle.x, 2) + Math.pow(rotatedY - handle.y, 2)); | |
| // ํด๋ฆญ ํจ๋ฉ์ ๋ํ์ฌ ํด๋ฆญ/ํฐ์น ์์ญ์ ์ฝ๊ฐ ๋๊ฒ ์ก์ต๋๋ค. | |
| if (dist <= handleSize / 2 + clickPadding) { | |
| return { id: key, cursor: handle.cursor }; // ํด๋น ํธ๋ค์ ID์ ์ปค์ ๋ชจ์ ๋ฐํ | |
| } | |
| } | |
| // ํ์ ํธ๋ค ์์น (ํ์ ๋์ง ์์ ์ํ ๊ธฐ์ค) | |
| const rotateHandlePos = { x: 0, y: -halfHeight - rotateHandleDistance }; | |
| const rotateDist = Math.sqrt(Math.pow(rotatedX - rotateHandlePos.x, 2) + Math.pow(rotatedY - rotateHandlePos.y, 2)); | |
| // ํ์ ํธ๋ค ๊ทผ์ฒ์ ํด๋ฆญ/ํฐ์นํ๋์ง ํ์ธ | |
| if (rotateDist <= handleSize / 2 + clickPadding) { | |
| return { id: 'rotate', cursor: 'grab' }; // ํ์ ํธ๋ค ID์ ์ปค์ ๋ชจ์ ๋ฐํ | |
| } | |
| return null; // ์ด๋ค ํธ๋ค๋ ํด๋ฆญ/ํฐ์น๋์ง ์์ | |
| } | |
| // ์ฃผ์ด์ง ์บ๋ฒ์ค ์ขํ๊ฐ ํน์ ์ค๋ฒ๋ ์ด ์ด๋ฏธ์ง ์์ญ ์์ ์๋์ง ํ์ธ | |
| function isOverOverlayImage(canvasX, canvasY, overlay) { | |
| // ์ค๋ฒ๋ ์ด๋ ์ด๋ฏธ์ง๊ฐ ์ ํจํ์ง ์์ผ๋ฉด false ๋ฐํ | |
| if (!overlay || !overlay.image || overlay.image.width === 0 || overlay.image.height === 0) return false; | |
| const currentWidth = overlay.image.width * overlay.scale; | |
| const currentHeight = overlay.image.height * overlay.scale; | |
| const halfWidth = currentWidth / 2; | |
| const halfHeight = currentHeight / 2; | |
| // ๋ง์ฐ์ค/ํฐ์น ์ขํ๋ฅผ ์ค๋ฒ๋ ์ด ์ด๋ฏธ์ง์ ์ค์ฌ์ ๊ธฐ์ค์ผ๋ก ํ๊ณ ํ์ ์ ๋๋๋ฆฝ๋๋ค. | |
| const dx = canvasX - overlay.x; | |
| const dy = canvasY - overlay.y; | |
| const angle = -overlay.rotation * Math.PI / 180; // ํ์ ์ ๋๋๋ฆฌ๋ ๊ฐ๋ | |
| const cos = Math.cos(angle); | |
| const sin = Math.sin(angle); | |
| // ํ์ ๋ ์ขํ | |
| const rotatedX = dx * cos - dy * sin; | |
| const rotatedY = dx * sin + dy * cos; | |
| // ํ์ ๋ ์ขํ๊ฐ ์ด๋ฏธ์ง ๊ฒฝ๊ณ ๋ด์ ์๋์ง ํ์ธ (ํด๋ฆญ ํจ๋ฉ ์ ์ฉ) | |
| return (rotatedX >= -halfWidth - clickPadding && rotatedX <= halfWidth + clickPadding && | |
| rotatedY >= -halfHeight - clickPadding && rotatedY <= halfHeight + clickPadding); | |
| } | |
| // ๋ง์ฐ์ค ์ปค์ ๋ชจ์ ์ ๋ฐ์ดํธ | |
| function updateCursor(canvasX, canvasY) { | |
| // ๋๋๊ทธ, ํ์ , ๋ฆฌ์ฌ์ด์ฆ ์ค์๋ ์ปค์ ๋ชจ์์ ๋ณ๊ฒฝํ์ง ์์ต๋๋ค. | |
| if (isDragging || isRotating || isResizing) return; | |
| let cursor = 'default'; // ๊ธฐ๋ณธ ์ปค์ ๋ชจ์ | |
| let handled = false; // ํธ๋ค์ด๋ ์ด๋ฏธ์ง ์์ ์๋์ง ์ฌ๋ถ | |
| // ํ์ฑ ๋ ์ด์ด๊ฐ ์๋ค๋ฉด ํธ๋ค ์์ ์๋์ง ๋จผ์ ํ์ธ | |
| if (activeOverlayIndex >= 0) { | |
| const activeOverlay = overlays[activeOverlayIndex]; | |
| const handle = getHandleAtPosition(canvasX, canvasY, activeOverlay); | |
| if (handle) { | |
| cursor = handle.cursor; // ํธ๋ค์ ๋ง๋ ์ปค์ ๋ชจ์ ์ค์ | |
| handled = true; | |
| } | |
| } | |
| // ํธ๋ค ์์ ์์ง ์๋ค๋ฉด, ์ค๋ฒ๋ ์ด ์ด๋ฏธ์ง ์์ ์๋์ง ํ์ธ (๊ฐ์ฅ ์์ ์๋ ๋ ์ด์ด๋ถํฐ) | |
| if (!handled) { | |
| for (let i = overlays.length - 1; i >= 0; i--) { | |
| if (isOverOverlayImage(canvasX, canvasY, overlays[i])) { | |
| cursor = 'move'; // ์ด๋ ์ปค์ ์ค์ | |
| handled = true; | |
| break; // ํ๋๋ผ๋ ๋ฐ๊ฒฌํ๋ฉด ์ค์ง | |
| } | |
| } | |
| } | |
| canvas.style.cursor = cursor; // ์บ๋ฒ์ค ์ปค์ ๋ชจ์ ์ ์ฉ | |
| } | |
| // ํฉ์ฑ๋ ์ต์ข ์ด๋ฏธ์ง ์์ฑ | |
| function generateMergedImage() { | |
| // ์ค๋ฒ๋ ์ด ์ด๋ฏธ์ง๊ฐ ์์ผ๋ฉด ์์ฑํ ์ ์์ต๋๋ค. | |
| if (overlays.length === 0) { | |
| alert('ํฉ์ฑํ ์ค๋ฒ๋ ์ด ์ด๋ฏธ์ง๊ฐ ์์ต๋๋ค.'); | |
| return null; | |
| } | |
| // ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋ฆด ์ ์บ๋ฒ์ค ์์ฑ | |
| const resultCanvas = document.createElement('canvas'); | |
| resultCanvas.width = CANVAS_WIDTH; | |
| resultCanvas.height = canvasAdjustedHeight; | |
| const resultCtx = resultCanvas.getContext('2d'); | |
| // ์บ๋ฒ์ค ์ด๊ธฐํ (๋ฐฐ๊ฒฝ ์ฑ์ฐ๊ธฐ) | |
| resultCtx.clearRect(0, 0, resultCanvas.width, resultCanvas.height); | |
| if (backgroundImage) { | |
| resultCtx.drawImage(backgroundImage, 0, 0, backgroundImage.width, backgroundImage.height, bgDrawProps.x, bgDrawProps.y, bgDrawProps.width, bgDrawProps.height); | |
| } else { | |
| resultCtx.fillStyle = '#ffffff'; | |
| resultCtx.fillRect(0, 0, CANVAS_WIDTH, canvasAdjustedHeight); | |
| } | |
| // ์ค๋ฒ๋ ์ด ์ด๋ฏธ์ง๋ค์ ์์๋๋ก ๊ทธ๋ฆฝ๋๋ค. | |
| overlays.forEach(overlay => { | |
| if (!overlay.image || overlay.image.width === 0 || overlay.image.height === 0) return; | |
| // ํํฐ ์ ์ฉ๋ ์ด๋ฏธ์ง ์บ๋ฒ์ค ์์ฑ | |
| const filteredCanvas = ImageFilters.createFilteredCanvas(overlay.image, overlay.filters); | |
| resultCtx.save(); // ํ์ฌ ์ํ ์ ์ฅ | |
| // ์ค๋ฒ๋ ์ด ์์น, ํ์ , ์ค์ผ์ผ ์ ์ฉ | |
| resultCtx.translate(overlay.x, overlay.y); | |
| resultCtx.rotate(overlay.rotation * Math.PI / 180); | |
| resultCtx.scale(overlay.scale, overlay.scale); | |
| // ์ด๋ฏธ์ง๋ฅผ ์ค์ฌ ๊ธฐ์ค์ผ๋ก ๊ทธ๋ฆฝ๋๋ค. | |
| resultCtx.drawImage(filteredCanvas, -overlay.image.width / 2, -overlay.image.height / 2); | |
| resultCtx.restore(); // ์ํ ๋ณต์ | |
| }); | |
| // ๋ฏธ๋ฆฌ๋ณด๊ธฐ์ ๊ฒฐ๊ณผ ์ด๋ฏธ์ง ํ์ | |
| previewImg.src = resultCanvas.toDataURL(`image/${originalFormat === 'jpg' ? 'jpeg' : originalFormat}`); | |
| previewContainer.style.display = 'flex'; // ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ปจํ ์ด๋ ๋ณด์ด๊ฒ ํจ | |
| return resultCanvas; // ๊ฒฐ๊ณผ ์บ๋ฒ์ค ๋ฐํ | |
| } | |
| // ์ ํ๋ ๋ ์ด์ด ์ญ์ | |
| function deleteSelectedLayer() { | |
| if (activeOverlayIndex >= 0) { | |
| // ์ญ์ ํ์ธ (์ ํ ์ฌํญ) | |
| if (confirm(`๋ ์ด์ด ${activeOverlayIndex + 1}์(๋ฅผ) ์ญ์ ํ์๊ฒ ์ต๋๊น?`)) { | |
| // ํด๋น ์ธ๋ฑ์ค์ ๋ ์ด์ด ์ญ์ | |
| overlays.splice(activeOverlayIndex, 1); | |
| activeOverlayIndex = -1; // ํ์ฑ ๋ ์ด์ด ์์์ผ๋ก ์ค์ | |
| updateControlPanel(); // ์ปจํธ๋กค ํจ๋ ์ ๋ฐ์ดํธ | |
| updateLayersList(); // ๋ ์ด์ด ๋ชฉ๋ก ์ ๋ฐ์ดํธ | |
| drawCanvas(); // ์บ๋ฒ์ค ๋ค์ ๊ทธ๋ฆฌ๊ธฐ | |
| // ๋ชจ๋ ์ค๋ฒ๋ ์ด๊ฐ ์ญ์ ๋๋ฉด ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์จ๊น | |
| if (overlays.length === 0) { | |
| previewContainer.style.display = 'none'; | |
| } | |
| } | |
| } | |
| } | |
| // ๋ง์ฐ์ค/ํฐ์น ์ด๋ฒคํธ์ ํด๋ผ์ด์ธํธ ์ขํ๋ฅผ ์บ๋ฒ์ค ์ขํ๋ก ๋ณํ | |
| function getCanvasCoordinates(clientX, clientY) { | |
| const rect = canvas.getBoundingClientRect(); // ์บ๋ฒ์ค์ ํ๋ฉด์ ์์น์ ํฌ๊ธฐ | |
| const scaleX = canvas.width / rect.width; // ๊ฐ๋ก ์ค์ผ์ผ ๋น์จ | |
| const scaleY = canvas.height / rect.height; // ์ธ๋ก ์ค์ผ์ผ ๋น์จ | |
| // ํด๋ผ์ด์ธํธ ์ขํ๋ฅผ ์บ๋ฒ์ค ๋ด๋ถ ์ขํ๋ก ๋ณํ | |
| const x = (clientX - rect.left) * scaleX; | |
| const y = (clientY - rect.top) * scaleY; | |
| return { x, y }; | |
| } | |
| // ํ์ฌ ํํฐ ๊ฐ์ ํ์ฑ ๋ ์ด์ด์ ์ ์ฉํ๊ณ ์บ๋ฒ์ค ๋ค์ ๊ทธ๋ฆฌ๊ธฐ | |
| function applyCurrentFilters() { | |
| if (activeOverlayIndex < 0) return; // ํ์ฑ ๋ ์ด์ด๊ฐ ์์ผ๋ฉด ์ ์ฉํ์ง ์์ | |
| const filterValues = { | |
| temperature: parseInt(temperatureSlider.value), | |
| brightness: parseInt(brightnessSlider.value), | |
| contrast: parseInt(contrastSlider.value), | |
| saturation: parseInt(saturationSlider.value) | |
| }; | |
| // filter.js์ applyFilterToLayer ํจ์ ํธ์ถ | |
| ImageFilters.applyFilterToLayer(overlays, activeOverlayIndex, filterValues); | |
| drawCanvas(); // ๋ณ๊ฒฝ ์ฌํญ์ ๋ฐ์ํ์ฌ ์บ๋ฒ์ค ๋ค์ ๊ทธ๋ฆฌ๊ธฐ | |
| } | |
| // --- ์ด๋ฒคํธ ๋ฆฌ์ค๋ --- | |
| // DOMContentLoaded ์ด๋ฒคํธ: ๋ฌธ์ ๋ก๋ ํ ์ด๊ธฐํ ๋ฐ ์ด๋ฒคํธ ๋ฆฌ์ค๋ ์ค์ | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง ์ ๋ก๋ ์ด๋ฒคํธ ๋ฆฌ์ค๋ | |
| backgroundInput.addEventListener('change', function(e) { | |
| if (e.target.files.length > 0) { | |
| loadImage(e.target.files[0], 'background'); | |
| } | |
| }); | |
| // ์ค๋ฒ๋ ์ด ์ด๋ฏธ์ง ์ ๋ก๋ ์ด๋ฒคํธ ๋ฆฌ์ค๋ | |
| overlayInput.addEventListener('change', function(e) { | |
| if (e.target.files.length > 0) { | |
| loadImage(e.target.files[0], 'overlay'); | |
| this.value = ''; // ํ์ผ ์ ํ ํ input ๊ฐ ์ด๊ธฐํ (๊ฐ์ ํ์ผ ๋ค์ ์ ํ ๊ฐ๋ฅํ๊ฒ) | |
| } | |
| }); | |
| // ์์ ์ด๋ฏธ์ง ํด๋ฆญ ์ด๋ฒคํธ ๋ฆฌ์ค๋ ์ค์ | |
| const exampleImagesContainer = document.getElementById('example-images-container'); // ์์ ์ด๋ฏธ์ง ์ปจํ ์ด๋ ID | |
| if (exampleImagesContainer) { | |
| exampleImagesContainer.querySelectorAll('.example-img').forEach(img => { | |
| img.addEventListener('click', function() { | |
| const imageUrl = this.src; | |
| const fileName = this.getAttribute('data-filename') || 'example_image.png'; | |
| // ์์ ์ด๋ฏธ์ง ๋ก๋ | |
| const exampleImgElement = new Image(); | |
| exampleImgElement.crossOrigin = 'anonymous'; // CORS ๋ฌธ์ ๋ฐฉ์ง๋ฅผ ์ํด ํ์ํ ์ ์์ต๋๋ค. | |
| exampleImgElement.onload = function() { | |
| // TODO: ์ฌ๊ธฐ์์ ๋ฐฐ๊ฒฝ ์ ๊ฑฐ ๋ก์ง์ ์คํํฉ๋๋ค. | |
| // ์ ๊ณต๋ cropTransparentPixels ํจ์๋ ํฌ๋ช ์์ญ์ ์๋ฅด๋ ๋ฐ ์ฌ์ฉ๋ ์ ์์ต๋๋ค. | |
| // ๋ง์ฝ ์์ ์ด๋ฏธ์ง๊ฐ ์ด๋ฏธ ๋ฐฐ๊ฒฝ ์ ๊ฑฐ๋ ์ด๋ฏธ์ง๋ผ๋ฉด ์ด ํจ์๋ฅผ ์ฌ์ฉํด๋ ๋ฉ๋๋ค. | |
| // ์ผ๋ฐ ์ด๋ฏธ์ง์ ๋ณต์กํ ๋ฐฐ๊ฒฝ์ ์ ๊ฑฐํ๋ ค๋ฉด ๋ณ๋์ ๊ณ ๊ธ ์ด๋ฏธ์ง ์ฒ๋ฆฌ ๋ก์ง์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ํ์ํฉ๋๋ค. | |
| let processedImageSource = exampleImgElement; // ๊ธฐ๋ณธ์ ์ผ๋ก๋ ์๋ณธ ์ด๋ฏธ์ง ์ฌ์ฉ | |
| try { | |
| // ํฌ๋ช ํฝ์ ์๋ฅด๊ธฐ ์๋ (๋ฐฐ๊ฒฝ ์ ๊ฑฐ๋ ์ด๋ฏธ์ง์ ์ ์ฉ) | |
| const croppedCanvas = cropTransparentPixels(exampleImgElement); | |
| if (croppedCanvas.width > 0 && croppedCanvas.height > 0) { | |
| processedImageSource = croppedCanvas; | |
| } else { | |
| console.warn("cropTransparentPixels resulted in empty canvas, using original image."); | |
| processedImageSource = exampleImgElement; | |
| } | |
| } catch (e) { | |
| console.error("Error during cropTransparentPixels, using original image:", e); | |
| processedImageSource = exampleImgElement; | |
| } | |
| // ์ฒ๋ฆฌ๋ ์ด๋ฏธ์ง๋ฅผ Blob์ผ๋ก ๋ณํ | |
| // cropTransparentPixels๊ฐ ์บ๋ฒ์ค๋ฅผ ๋ฐํํ๋ฏ๋ก toBlob ์ฌ์ฉ | |
| const canvasToProcess = processedImageSource instanceof HTMLCanvasElement ? processedImageSource : null; | |
| if (canvasToProcess) { | |
| canvasToProcess.toBlob(function(blob) { | |
| if (blob) { | |
| // Blob์ ์ฌ์ฉํ์ฌ loadImage ํจ์ ํธ์ถ (loadImage ํจ์๊ฐ Blob์ ์ฒ๋ฆฌํ๋๋ก ์์ ๋จ) | |
| loadImage(blob, 'overlay'); | |
| } else { | |
| console.error("Failed to create blob from processed canvas."); | |
| alert("์ด๋ฏธ์ง ์ฒ๋ฆฌ์ ์คํจํ์ต๋๋ค."); | |
| } | |
| }, 'image/png'); // ๋ฐฐ๊ฒฝ ์ ๊ฑฐ ์ ํฌ๋ช ๋๋ฅผ ์ํด PNG ํฌ๋งท ๊ถ์ฅ | |
| } else if (processedImageSource instanceof HTMLImageElement) { | |
| // Canvas๊ฐ ์๋ Image ์์์ธ ๊ฒฝ์ฐ (e.g., cropTransparentPixels ์ค๋ฅ ์) | |
| // Data URL๋ก ๋ณํํ์ฌ Blob ์์ฑ ํ loadImage ํธ์ถ | |
| const tempCanvas = document.createElement('canvas'); | |
| tempCanvas.width = processedImageSource.width; | |
| tempCanvas.height = processedImageSource.height; | |
| const tempCtx = tempCanvas.getContext('2d'); | |
| tempCtx.drawImage(processedImageSource, 0, 0); | |
| tempCanvas.toBlob(function(blob) { | |
| if (blob) { | |
| loadImage(blob, 'overlay'); | |
| } else { | |
| console.error("Failed to create blob from fallback image."); | |
| alert("์ด๋ฏธ์ง ์ฒ๋ฆฌ์ ์คํจํ์ต๋๋ค."); | |
| } | |
| }, 'image/png'); | |
| } else { | |
| console.error("Processed image source is neither Canvas nor Image."); | |
| alert("์ด๋ฏธ์ง ์ฒ๋ฆฌ์ ์คํจํ์ต๋๋ค."); | |
| } | |
| }; | |
| exampleImgElement.onerror = function() { | |
| console.error("Error loading example image:", imageUrl); | |
| alert("์์ ์ด๋ฏธ์ง๋ฅผ ๋ก๋ํ๋ ๋ฐ ์คํจํ์ต๋๋ค."); | |
| }; | |
| exampleImgElement.src = imageUrl; // ์์ ์ด๋ฏธ์ง ๋ก๋ ์์ | |
| }); | |
| }); | |
| } | |
| // Delete ํค ๋๋ ์ ๋ ์ ํ๋ ๋ ์ด์ด ์ญ์ | |
| document.addEventListener('keydown', function(e) { | |
| // Ctrl ๋๋ Cmd ํค์ ํจ๊ป ๋๋ ธ์ ๋๋ง ๋์ํ๋๋ก ์กฐ๊ฑด ์ถ๊ฐ (์ค์ ๋ฐฉ์ง) | |
| if (e.key === 'Delete' && activeOverlayIndex >= 0) { | |
| e.preventDefault(); // ๊ธฐ๋ณธ ๋์(๋ค๋ก๊ฐ๊ธฐ ๋ฑ) ๋ฐฉ์ง | |
| deleteSelectedLayer(); | |
| } | |
| }); | |
| // ๋ ์ด์ด ์ญ์ ๋ฒํผ ์ด๋ฒคํธ | |
| deleteLayerBtn.addEventListener('click', function() { | |
| if (activeOverlayIndex >= 0) { | |
| deleteSelectedLayer(); | |
| } | |
| }); | |
| // ํฌ๊ธฐ ์กฐ์ ์ฌ๋ผ์ด๋ ์ด๋ฒคํธ | |
| scaleSlider.addEventListener('input', function() { | |
| if (activeOverlayIndex >= 0) { | |
| overlays[activeOverlayIndex].scale = parseInt(this.value) / 100; | |
| scaleValue.textContent = this.value + '%'; | |
| drawCanvas(); // ์บ๋ฒ์ค ๋ค์ ๊ทธ๋ฆฌ๊ธฐ | |
| } | |
| }); | |
| // ํ์ ์ฌ๋ผ์ด๋ ์ด๋ฒคํธ | |
| rotationSlider.addEventListener('input', function() { | |
| if (activeOverlayIndex >= 0) { | |
| overlays[activeOverlayIndex].rotation = parseInt(this.value); | |
| rotationValue.textContent = overlays[activeOverlayIndex].rotation + 'ยฐ'; | |
| drawCanvas(); // ์บ๋ฒ์ค ๋ค์ ๊ทธ๋ฆฌ๊ธฐ | |
| } | |
| }); | |
| // ํํฐ ์ฌ๋ผ์ด๋ ์ด๋ฒคํธ | |
| temperatureSlider.addEventListener('input', function() { temperatureValue.textContent = this.value; applyCurrentFilters(); }); | |
| brightnessSlider.addEventListener('input', function() { brightnessValue.textContent = this.value + '%'; applyCurrentFilters(); }); | |
| contrastSlider.addEventListener('input', function() { contrastValue.textContent = this.value + '%'; applyCurrentFilters(); }); | |
| saturationSlider.addEventListener('input', function() { saturationValue.textContent = this.value + '%'; applyCurrentFilters(); }); | |
| // ํํฐ ์ด๊ธฐํ ๋ฒํผ ์ด๋ฒคํธ | |
| resetFilterBtn.addEventListener('click', function() { | |
| // filter.js์ resetFilters ํจ์ ํธ์ถ | |
| const defaultFilters = ImageFilters.resetFilters(overlays, activeOverlayIndex); | |
| // ์ฌ๋ผ์ด๋ ๊ฐ๊ณผ ํ์ ์ ๋ฐ์ดํธ | |
| temperatureSlider.value = defaultFilters.temperature; temperatureValue.textContent = defaultFilters.temperature; | |
| brightnessSlider.value = defaultFilters.brightness; brightnessValue.textContent = defaultFilters.brightness + '%'; | |
| contrastSlider.value = defaultFilters.contrast; contrastValue.textContent = defaultFilters.contrast + '%'; | |
| saturationSlider.value = defaultFilters.saturation; saturationValue.textContent = defaultFilters.saturation + '%'; | |
| drawCanvas(); // ๋ณ๊ฒฝ ์ฌํญ ๋ฐ์ | |
| }); | |
| // ์บ๋ฒ์ค ๋ง์ฐ์ค/ํฐ์น ์ด๋ฒคํธ ๋ฆฌ์ค๋ | |
| canvas.addEventListener('mousedown', function(e) { | |
| // ๋ง์ฐ์ค ์ผ์ชฝ ๋ฒํผ๋ง ์ฒ๋ฆฌ | |
| if (e.button !== 0) return; | |
| const { x, y } = getCanvasCoordinates(e.clientX, e.clientY); | |
| const isAltPressed = e.altKey; // Alt ํค ๋๋ฆผ ์ฌ๋ถ ํ์ธ | |
| let clickedOverlayIndex = -1; // ํด๋ฆญ๋ ์ค๋ฒ๋ ์ด ์ธ๋ฑ์ค | |
| let handle = null; // ํด๋ฆญ๋ ํธ๋ค | |
| // ํ์ฑ ๋ ์ด์ด๊ฐ ์๋ค๋ฉด ๋จผ์ ํด๋น ๋ ์ด์ด์ ํธ๋ค ํด๋ฆญ ์ฌ๋ถ ํ์ธ | |
| if (activeOverlayIndex >= 0) { | |
| handle = getHandleAtPosition(x, y, overlays[activeOverlayIndex]); | |
| if (handle) { | |
| clickedOverlayIndex = activeOverlayIndex; // ํธ๋ค์ด ํด๋ฆญ๋๋ฉด ํด๋น ๋ ์ด์ด ์ ํ | |
| } | |
| } | |
| // ํธ๋ค์ด ํด๋ฆญ๋์ง ์์๋ค๋ฉด ์ค๋ฒ๋ ์ด ์ด๋ฏธ์ง ์์ฒด ํด๋ฆญ ์ฌ๋ถ ํ์ธ | |
| if (!handle) { | |
| let overlaysAtPosition = []; // ํด๋ฆญ๋ ์์น์ ์๋ ๋ชจ๋ ์ค๋ฒ๋ ์ด ๋ชฉ๋ก | |
| // ๋ชจ๋ ์ค๋ฒ๋ ์ด๋ฅผ ์ํํ๋ฉฐ ํด๋ฆญ๋ ์์น์ ํด๋นํ๋ ์ค๋ฒ๋ ์ด ์ฐพ๊ธฐ | |
| for (let i = 0; i < overlays.length; i++) { | |
| if (isOverOverlayImage(x, y, overlays[i])) { | |
| overlaysAtPosition.push(i); | |
| } | |
| } | |
| if (overlaysAtPosition.length > 0) { | |
| // ์ฌ๋ฌ ๊ฐ์ ์ค๋ฒ๋ ์ด๊ฐ ๊ฒน์ณ ์๋ค๋ฉด Alt ํค ๋๋ฆผ ์ฌ๋ถ์ ๋ฐ๋ผ ์ฒ๋ฆฌ | |
| if (isAltPressed && overlaysAtPosition.length > 1) { | |
| // Alt ํค๊ฐ ๋๋ ธ๊ณ ์ฌ๋ฌ ๊ฐ๊ฐ ๊ฒน์ณ ์๋ค๋ฉด, ํ์ฌ ์ ํ๋ ๋ ์ด์ด ๋ค์ ์์์ ๋ ์ด์ด๋ฅผ ์ ํ | |
| if (activeOverlayIndex >= 0) { | |
| const currentIndexInAtPosition = overlaysAtPosition.indexOf(activeOverlayIndex); | |
| if (currentIndexInAtPosition !== -1) { | |
| const nextIndexInAtPosition = (currentIndexInAtPosition + 1) % overlaysAtPosition.length; | |
| clickedOverlayIndex = overlaysAtPosition[nextIndexInAtPosition]; | |
| } else { | |
| // ํ์ฌ ์ ํ๋ ๋ ์ด์ด๊ฐ ํด๋ฆญ๋ ์์น์ ์์ผ๋ฉด ๊ฐ์ฅ ์์ ์๋ ๋ ์ด์ด ์ ํ (๊ธฐ๋ณธ ๋์) | |
| clickedOverlayIndex = overlaysAtPosition[overlaysAtPosition.length - 1]; | |
| } | |
| } else { | |
| // Alt ํค๊ฐ ๋๋ ธ์ง๋ง ํ์ฌ ์ ํ๋ ๋ ์ด์ด๊ฐ ์์ผ๋ฉด ๊ฐ์ฅ ์์ ์๋ ๋ ์ด์ด ์ ํ | |
| clickedOverlayIndex = overlaysAtPosition[overlaysAtPosition.length - 1]; | |
| } | |
| } else { | |
| // Alt ํค๊ฐ ๋๋ฆฌ์ง ์์๊ฑฐ๋ ํ๋๋ง ๊ฒน์ณ ์๋ค๋ฉด, ๊ฐ์ฅ ์์ ์๋ ๋ ์ด์ด ์ ํ | |
| clickedOverlayIndex = overlaysAtPosition[overlaysAtPosition.length - 1]; | |
| } | |
| } | |
| } | |
| // ํด๋ฆญ๋ ์ค๋ฒ๋ ์ด๊ฐ ์๋ค๋ฉด ํด๋น ์ค๋ฒ๋ ์ด๋ฅผ ํ์ฑ ๋ ์ด์ด๋ก ์ค์ ํ๊ณ ์ํ ์ ๋ฐ์ดํธ | |
| if (clickedOverlayIndex >= 0) { | |
| const activeOverlay = overlays[clickedOverlayIndex]; | |
| // ์ด๋ฏธ ํ์ฑ ๋ ์ด์ด์ธ ๊ฒฝ์ฐ๋ ์ ์ธ | |
| if (activeOverlayIndex !== clickedOverlayIndex) { | |
| activeOverlayIndex = clickedOverlayIndex; | |
| updateControlPanel(); // ์ปจํธ๋กค ํจ๋ ์ ๋ฐ์ดํธ | |
| updateLayersList(); // ๋ ์ด์ด ๋ชฉ๋ก ์ ๋ฐ์ดํธ | |
| } | |
| // ํด๋ฆญ๋ ๊ฒ์ด ํธ๋ค์ด๋ฉด ํด๋น ์์ ์์ | |
| if (handle) { | |
| if (handle.id === 'rotate') { | |
| isRotating = true; | |
| canvas.style.cursor = 'grabbing'; | |
| } else { | |
| isResizing = true; | |
| activeHandle = handle.id; // ํ์ฑ ํธ๋ค ID ์ค์ | |
| // ๋ฆฌ์ฌ์ด์ฆ ์์ ์ ๋ง์ฐ์ค ์์น์ ์ค๋ฒ๋ ์ด ์ค์ฌ ๊ฐ์ ๊ฑฐ๋ฆฌ ๊ณ์ฐ (๋น๋ก ์ค์ผ์ผ๋ง์ ์ฌ์ฉ) | |
| const dxMouse = x - activeOverlay.x; | |
| const dyMouse = y - activeOverlay.y; | |
| resizeStartDistance = Math.sqrt(dxMouse * dxMouse + dyMouse * dyMouse); | |
| if (resizeStartDistance < 10) resizeStartDistance = 10; // ๋๋ฌด ๊ฐ๊น์ฐ๋ฉด ์ต์๊ฐ ์ค์ | |
| } | |
| } else { | |
| // ํธ๋ค์ด ์๋ ์ค๋ฒ๋ ์ด ์ด๋ฏธ์ง ์์ฒด๋ฅผ ํด๋ฆญํ ๊ฒฝ์ฐ ๋๋๊ทธ ์์ | |
| isDragging = true; | |
| canvas.style.cursor = 'grabbing'; // ๋๋๊ทธ ์ปค์ ๋ชจ์ | |
| } | |
| } else { | |
| // ์ค๋ฒ๋ ์ด ์ด๋ฏธ์ง๊ฐ ์๋ ๋น ๊ณต๊ฐ์ ํด๋ฆญํ ๊ฒฝ์ฐ ํ์ฑ ๋ ์ด์ด ํด์ | |
| activeOverlayIndex = -1; | |
| updateControlPanel(); // ์ปจํธ๋กค ํจ๋ ์ด๊ธฐํ | |
| updateLayersList(); // ๋ ์ด์ด ๋ชฉ๋ก ์ ๋ฐ์ดํธ (ํ์ฑ ํ์ ์ ๊ฑฐ) | |
| } | |
| // ๋ง์ง๋ง ๋ง์ฐ์ค/ํฐ์น ์ขํ ์ ์ฅ | |
| lastX = x; | |
| lastY = y; | |
| drawCanvas(); // ์บ๋ฒ์ค ๋ค์ ๊ทธ๋ฆฌ๊ธฐ (ํ์ฑ ๋ ์ด์ด ํธ๋ค ํ์ ์ ๋ฐ์ดํธ) | |
| }); | |
| canvas.addEventListener('mousemove', function(e) { | |
| const { x, y } = getCanvasCoordinates(e.clientX, e.clientY); | |
| // ๋๋๊ทธ, ํ์ , ๋ฆฌ์ฌ์ด์ฆ ์ค์ด ์๋๋ฉด ์ปค์ ๋ชจ์ ์ ๋ฐ์ดํธ | |
| if (!isDragging && !isRotating && !isResizing) { | |
| updateCursor(x, y); | |
| } else if (activeOverlayIndex >= 0) { // ๋๋๊ทธ, ํ์ , ๋ฆฌ์ฌ์ด์ฆ ์ค์ด๊ณ ํ์ฑ ๋ ์ด์ด๊ฐ ์๋ค๋ฉด | |
| const activeOverlay = overlays[activeOverlayIndex]; | |
| canvas.style.cursor = 'grabbing'; // ์์ ์ค์๋ ์ก๋ ์ปค์ ์ ์ง | |
| if (isRotating) { | |
| // ๋ง์ฐ์ค ์์น๋ฅผ ๊ธฐ์ค์ผ๋ก ํ์ ๊ฐ๋ ๊ณ์ฐ | |
| const dx = x - activeOverlay.x; | |
| const dy = y - activeOverlay.y; | |
| let angle = Math.atan2(dy, dx) * 180 / Math.PI + 90; // atan2 ๊ฒฐ๊ณผ๋ -180~180, +90ํ์ฌ 0~360์ผ๋ก ๋ณํ | |
| angle = (angle % 360 + 360) % 360; // ๊ฐ๋๋ฅผ 0~360 ๋ฒ์๋ก ์ ์ง | |
| activeOverlay.rotation = angle; | |
| // ํ์ ์ฌ๋ผ์ด๋ ๋ฐ ๊ฐ ์ ๋ฐ์ดํธ | |
| rotationSlider.value = Math.round(activeOverlay.rotation); | |
| rotationValue.textContent = Math.round(activeOverlay.rotation) + 'ยฐ'; | |
| } else if (isResizing && activeOverlay.image) { | |
| // ๋ฆฌ์ฌ์ด์ฆ ์ค์ด๊ณ ์ด๋ฏธ์ง๊ฐ ์๋ค๋ฉด | |
| const dxMouse = x - activeOverlay.x; | |
| const dyMouse = y - activeOverlay.y; | |
| const currentDistance = Math.sqrt(dxMouse * dxMouse + dyMouse * dyMouse); | |
| if (resizeStartDistance > 0 && currentDistance > 0) { | |
| // ๋ง์ฐ์ค ์ด๋ ๊ฑฐ๋ฆฌ์ ๋น๋กํ์ฌ ์ค์ผ์ผ ์กฐ์ | |
| let newScale = activeOverlay.scale * (currentDistance / resizeStartDistance); | |
| activeOverlay.scale = Math.max(0.01, newScale); // ์ค์ผ์ผ ์ต์๊ฐ ์ ํ | |
| resizeStartDistance = currentDistance; // ๋ค์ ์ด๋ ๊ณ์ฐ์ ์ํด ๊ฑฐ๋ฆฌ ์ ๋ฐ์ดํธ | |
| } | |
| // ์ค์ผ์ผ ์ฌ๋ผ์ด๋ ๋ฐ ๊ฐ ์ ๋ฐ์ดํธ | |
| const scalePercent = Math.round(activeOverlay.scale * 100); | |
| scaleSlider.value = scalePercent; | |
| scaleValue.textContent = scalePercent + '%'; | |
| } else if (isDragging && activeOverlay.image) { | |
| // ๋๋๊ทธ ์ค์ด๊ณ ์ด๋ฏธ์ง๊ฐ ์๋ค๋ฉด | |
| const dx = x - lastX; // x์ถ ์ด๋ ๊ฑฐ๋ฆฌ | |
| const dy = y - lastY; // y์ถ ์ด๋ ๊ฑฐ๋ฆฌ | |
| activeOverlay.x += dx; // ์ค๋ฒ๋ ์ด ์์น ์ ๋ฐ์ดํธ | |
| activeOverlay.y += dy; | |
| } | |
| // ์์ ์ค์๋ ์บ๋ฒ์ค ๊ณ์ ๋ค์ ๊ทธ๋ฆฌ๊ธฐ | |
| if (isRotating || isResizing || isDragging) { | |
| drawCanvas(); | |
| } | |
| } | |
| // ๋ง์ง๋ง ๋ง์ฐ์ค/ํฐ์น ์ขํ ์ ๋ฐ์ดํธ | |
| lastX = x; | |
| lastY = y; | |
| }); | |
| // ๋ง์ฐ์ค ๋ฒํผ ๋ผ๊ฑฐ๋ ์บ๋ฒ์ค ๋ฐ์ผ๋ก ๋ฒ์ด๋ฌ์ ๋ ์์ ์ข ๋ฃ | |
| window.addEventListener('mouseup', function(e) { | |
| isDragging = false; | |
| isRotating = false; | |
| isResizing = false; | |
| activeHandle = null; // ํ์ฑ ํธ๋ค ์์ | |
| // ๋ง์ฐ์ค ์ขํ๊ฐ ์ ํจํ๋ฉด ์ปค์ ๋ชจ์ ์ ๋ฐ์ดํธ | |
| if (e.clientX !== undefined && e.clientY !== undefined) { | |
| const { x, y } = getCanvasCoordinates(e.clientX, e.clientY); | |
| updateCursor(x, y); | |
| } else { | |
| // ์ขํ๊ฐ ์ ํจํ์ง ์์ผ๋ฉด ๊ธฐ๋ณธ ์ปค์๋ก ์ค์ | |
| canvas.style.cursor = 'default'; | |
| } | |
| }); | |
| // ํฐ์น ์ด๋ฒคํธ (๋ง์ฐ์ค ์ด๋ฒคํธ๋ฅผ ์๋ฎฌ๋ ์ด์ ํ์ฌ ์ฒ๋ฆฌ) | |
| canvas.addEventListener('touchstart', function(e) { | |
| e.preventDefault(); // ๊ธฐ๋ณธ ํฐ์น ๋์ ๋ฐฉ์ง (์คํฌ๋กค ๋ฑ) | |
| if (e.touches.length > 0) { | |
| const touch = e.touches[0]; | |
| // ์ฒซ ๋ฒ์งธ ํฐ์น ์ ๋ณด๋ฅผ ์ฌ์ฉํ์ฌ mousedown ์ด๋ฒคํธ ์์ฑ ๋ฐ ๋ฐ์ | |
| const mouseEvent = new MouseEvent('mousedown', { clientX: touch.clientX, clientY: touch.clientY, buttons: 1 }); | |
| canvas.dispatchEvent(mouseEvent); | |
| } | |
| }, { passive: false }); // passive: false๋ก ์ค์ ํ์ฌ preventDefault()๊ฐ ๋์ํ๋๋ก ํจ | |
| canvas.addEventListener('touchmove', function(e) { | |
| e.preventDefault(); // ๊ธฐ๋ณธ ํฐ์น ๋์ ๋ฐฉ์ง | |
| if (e.touches.length > 0) { | |
| const touch = e.touches[0]; | |
| // ์ฒซ ๋ฒ์งธ ํฐ์น ์ ๋ณด๋ฅผ ์ฌ์ฉํ์ฌ mousemove ์ด๋ฒคํธ ์์ฑ ๋ฐ ๋ฐ์ | |
| const mouseEvent = new MouseEvent('mousemove', { clientX: touch.clientX, clientY: touch.clientY, buttons: 1 }); | |
| canvas.dispatchEvent(mouseEvent); | |
| } | |
| }, { passive: false }); | |
| canvas.addEventListener('touchend', function(e) { | |
| e.preventDefault(); // ๊ธฐ๋ณธ ํฐ์น ๋์ ๋ฐฉ์ง | |
| // mouseup ์ด๋ฒคํธ ์์ฑ ๋ฐ ๋ฐ์ (์ขํ ์ ๋ณด๋ ํ์ ์์) | |
| const mouseEvent = new MouseEvent('mouseup', {}); | |
| window.dispatchEvent(mouseEvent); // window์ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์์ผ mouseup ๋ฆฌ์ค๋๊ฐ ๋์ํ๋๋ก ํจ | |
| }, { passive: false }); | |
| // ํฉ์ฑ ๋ฒํผ ์ด๋ฒคํธ | |
| generateBtn.addEventListener('click', function() { | |
| generateMergedImage(); // ํฉ์ฑ ์ด๋ฏธ์ง ์์ฑ ๋ฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์ ํ์ | |
| // ์ด ๋ฒํผ ํด๋ฆญ ์ UI ๋ ์ด์์์ ๋ณ๊ฒฝํ๋ ์ฝ๋๋ ์ฌ๊ธฐ์ ์์ต๋๋ค. | |
| // ์ค์ง generateMergedImage() ํจ์๋ง ํธ์ถํฉ๋๋ค. | |
| }); | |
| // ์ ์ฒด ์ด๊ธฐํ ๋ฒํผ ์ด๋ฒคํธ | |
| resetAllBtn.addEventListener('click', function() { | |
| backgroundImage = null; // ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง ์ด๊ธฐํ | |
| overlays = []; // ์ค๋ฒ๋ ์ด ๋ฐฐ์ด ์ด๊ธฐํ | |
| activeOverlayIndex = -1; // ํ์ฑ ๋ ์ด์ด ํด์ | |
| backgroundInput.value = ''; // ํ์ผ input ๊ฐ ์ด๊ธฐํ | |
| overlayInput.value = ''; | |
| updateControlPanel(); // ์ปจํธ๋กค ํจ๋ ์ด๊ธฐํ | |
| updateLayersList(); // ๋ ์ด์ด ๋ชฉ๋ก ์ด๊ธฐํ | |
| // ํํฐ ์ปจํธ๋กค ์ด๊ธฐ๊ฐ์ผ๋ก ์ค์ | |
| const defaultFilters = ImageFilters.resetFilters(overlays, activeOverlayIndex); // (์ฌ์ค์ overlays๊ฐ ๋น์ด ์์ผ๋ฏ๋ก ๊ธฐ๋ณธ๊ฐ ๋ฐํ) | |
| temperatureSlider.value = defaultFilters.temperature; temperatureValue.textContent = defaultFilters.temperature; | |
| brightnessSlider.value = defaultFilters.brightness; brightnessValue.textContent = defaultFilters.brightness + '%'; | |
| contrastSlider.value = defaultFilters.contrast; contrastValue.textContent = defaultFilters.contrast + '%'; | |
| saturationSlider.value = defaultFilters.saturation; saturationValue.textContent = defaultFilters.saturation + '%'; | |
| // ์บ๋ฒ์ค ํฌ๊ธฐ ๋ฐ ์ด๊ธฐ ์ํ๋ก ์ฌ์ค์ | |
| canvasAdjustedHeight = CANVAS_HEIGHT; | |
| canvas.height = canvasAdjustedHeight; | |
| initCanvas(); // ์บ๋ฒ์ค ํฐ์ ๋ฐฐ๊ฒฝ์ผ๋ก ์ด๊ธฐํ | |
| previewContainer.style.display = 'none'; // ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์์ญ ์จ๊น | |
| }); | |
| // ๋ค์ด๋ก๋ ๋ฒํผ ์ด๋ฒคํธ | |
| downloadBtn.addEventListener('click', function() { | |
| const resultCanvas = generateMergedImage(); // ํฉ์ฑ ์ด๋ฏธ์ง ์์ฑ | |
| if (!resultCanvas) return; // ์ด๋ฏธ์ง๊ฐ ์์ผ๋ฉด ๋ค์ด๋ก๋ํ์ง ์์ | |
| const link = document.createElement('a'); // ๋ค์ด๋ก๋๋ฅผ ์ํ ์์ ๋งํฌ ์์ ์์ฑ | |
| // ํ์ผ ์ด๋ฆ ์์ฑ (ํ์ฌ ์๊ฐ ๊ธฐ๋ฐ) | |
| const timestamp = new Date().toISOString().replace(/[-:.]/g, '').substring(0, 14); | |
| // ์ด๋ฏธ์ง ํฌ๋งท ๋ฐ ํ์ฅ์ ๊ฒฐ์ (์๋ณธ ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง ํฌ๋งท ๋ฐ๋ฆ) | |
| const mimeType = originalFormat === 'jpg' ? 'image/jpeg' : `image/${originalFormat}`; | |
| const fileExt = originalFormat; | |
| link.download = `merged_image_${timestamp}.${fileExt}`; // ๋ค์ด๋ก๋๋ ํ์ผ ์ด๋ฆ ์ค์ | |
| // ์บ๋ฒ์ค ์ด๋ฏธ์ง๋ฅผ Data URL๋ก ๋ณํํ์ฌ ๋งํฌ์ href๋ก ์ค์ | |
| link.href = resultCanvas.toDataURL(mimeType, 1.0); // 1.0์ ํ์ง (PNG๋ ์ํฅ ์์, JPG๋ 0~1) | |
| link.click(); // ๋งํฌ ํด๋ฆญ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์์ผ ๋ค์ด๋ก๋ ์คํ | |
| }); | |
| // ํ์ด์ง ๋ก๋ ์ ์ด๊ธฐํ ํจ์ ํธ์ถ | |
| initCanvas(); // ์บ๋ฒ์ค ์ด๊ธฐ ์ํ๋ก ์ค์ | |
| updateControlPanel(); // ์ปจํธ๋กค ํจ๋ ์ด๊ธฐ ์ํ๋ก ์ค์ (๋ฒํผ ๋นํ์ฑํ ๋ฑ) | |
| }); // DOMContentLoaded ๋ |