diff options
author | elioat <elioat@tilde.institute> | 2025-03-30 09:46:28 -0400 |
---|---|---|
committer | elioat <elioat@tilde.institute> | 2025-03-30 09:46:28 -0400 |
commit | c512927296ebccb6daedc1aae2fe3245aab1bd3a (patch) | |
tree | acf06ea42415cf633dc6d7d5f62e267685f58d39 | |
parent | a564738c865b271a1d9a0161121e176627630141 (diff) | |
download | tour-c512927296ebccb6daedc1aae2fe3245aab1bd3a.tar.gz |
*
-rw-r--r-- | js/leibovitz/index.html | 27 | ||||
-rw-r--r-- | js/leibovitz/leibovitz.js | 138 |
2 files changed, 164 insertions, 1 deletions
diff --git a/js/leibovitz/index.html b/js/leibovitz/index.html index c8eb9af..997760f 100644 --- a/js/leibovitz/index.html +++ b/js/leibovitz/index.html @@ -335,6 +335,20 @@ } @media (max-width: 600px) { + #controls { + flex-direction: column; + } + + #toggle-camera, button.capture, #edit-image { + width: 100%; + border-right: none; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + } + + #toggle-camera:last-child, button.capture:last-child, #edit-image:last-child { + border-bottom: none; + } + .slide-controls { flex-direction: column; gap: 12px; @@ -359,6 +373,17 @@ text-align: left; } } + #edit-image { + display: block; + padding: 20px; + font-family: 'ChicagoFLF', sans-serif; + border-radius: 0; + text-align: center; + background-color: #f0f0f0; + } + #edit-image.hidden { + display: none; + } </style> </head> <body> @@ -412,7 +437,9 @@ <div id="controls"> <button id="toggle-camera">Camera On</button> + <button id="edit-image" class="edit-image">Edit Image</button> <button id="capture" disabled class="capture">Capture Image</button> + <input type="file" id="image-input" accept="image/*" style="display: none;"> </div> <script src="dither.js"></script> diff --git a/js/leibovitz/leibovitz.js b/js/leibovitz/leibovitz.js index 3b43d86..aa230f9 100644 --- a/js/leibovitz/leibovitz.js +++ b/js/leibovitz/leibovitz.js @@ -3,6 +3,8 @@ const ctx = canvas.getContext('2d'); const video = document.createElement('video'); const toggleCameraButton = document.getElementById('toggle-camera'); const captureButton = document.getElementById('capture'); +const editImageButton = document.getElementById('edit-image'); +const imageInput = document.getElementById('image-input'); const focusControl = document.getElementById('focus-control'); const focusSlider = document.getElementById('focus-slider'); const focusValue = document.getElementById('focus-value'); @@ -10,6 +12,8 @@ const focusValue = document.getElementById('focus-value'); let cameraOn = false; let stream = null; let track = null; +let isEditMode = false; +let originalImage = null; // Store the original image // Initialize managers ColorManager.init(); @@ -54,6 +58,19 @@ window.addEventListener('resize', updateCanvasSize); updateCanvasSize(); function startCamera() { + // If we're in edit mode, show confirmation dialog + if (isEditMode) { + if (!confirm('Switching to camera mode will discard your current image and edits. Continue?')) { + return; // User cancelled + } + + // Clear edit mode state + isEditMode = false; + originalImage = null; + canvas.style.display = 'none'; + ctx.clearRect(0, 0, canvas.width, canvas.height); + } + navigator.mediaDevices.getUserMedia({ video: { facingMode: { ideal: 'environment' } } }) .then(s => { stream = s; @@ -62,6 +79,9 @@ function startCamera() { canvas.style.display = 'block'; // Show the canvas captureButton.disabled = false; captureButton.active = true; + editImageButton.classList.add('hidden'); + isEditMode = false; + originalImage = null; // Clear the original image track = stream.getVideoTracks()[0]; const settings = track.getSettings(); @@ -115,9 +135,68 @@ function stopCamera() { focusSlider.disabled = true; focusControl.style.display = 'none'; stream = null; + editImageButton.classList.remove('hidden'); } } +function loadImage(file) { + const reader = new FileReader(); + reader.onload = function(e) { + const img = new Image(); + img.onload = function() { + // Calculate dimensions to maintain aspect ratio + const container = document.querySelector('.preview-container'); + const containerWidth = container.clientWidth; + const containerHeight = container.clientHeight; + + const imgAspect = img.width / img.height; + const containerAspect = containerWidth / containerHeight; + + if (containerAspect > imgAspect) { + canvas.height = containerHeight; + canvas.width = containerHeight * imgAspect; + } else { + canvas.width = containerWidth; + canvas.height = containerWidth / imgAspect; + } + + // Store the original image + originalImage = img; + + // Draw the image + ctx.drawImage(img, 0, 0, canvas.width, canvas.height); + canvas.style.display = 'block'; + captureButton.disabled = false; + captureButton.active = true; + + // Start the effect loop + function step() { + if (!isEditMode) return; + applyEffects(); + requestAnimationFrame(step); + } + requestAnimationFrame(step); + }; + img.src = e.target.result; + }; + reader.readAsDataURL(file); +} + +function applyEffects() { + // Clear the canvas + ctx.clearRect(0, 0, canvas.width, canvas.height); + + // Draw the original image + ctx.drawImage(originalImage, 0, 0, canvas.width, canvas.height); + + // Apply effects in sequence + applyContrast(); + applyColorTint(); + applyBlur(); + applyBalance(); + applyDither(); +} + function drawVideoProportional() { const videoAspectRatio = video.videoWidth / video.videoHeight; const canvasAspectRatio = canvas.width / canvas.height; @@ -219,6 +298,19 @@ toggleCameraButton.addEventListener('click', () => { } }); +editImageButton.addEventListener('click', () => { + if (!cameraOn) { + imageInput.click(); + } +}); + +imageInput.addEventListener('change', (e) => { + if (e.target.files && e.target.files[0]) { + isEditMode = true; + loadImage(e.target.files[0]); + } +}); + if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/service-worker.js') @@ -230,4 +322,48 @@ if ('serviceWorker' in navigator) { }); } -ColorManager._setupEventListeners(); \ No newline at end of file +ColorManager._setupEventListeners(); + +// Update the reset handlers in each manager to trigger a redraw +function resetEffects() { + if (isEditMode && originalImage) { + applyEffects(); + } +} + +// Add reset handlers to each manager +BlurManager.reset = function() { + this._currentBlur = 0; + this._slider.value = 0; + this._value.textContent = '0%'; + this._notifyObservers(); + resetEffects(); +}; + +ContrastManager.reset = function() { + this._currentContrast = 1.0; + this._slider.value = 0; + document.getElementById('contrast-value').textContent = '0'; + this._notifyObservers(); + resetEffects(); +}; + +ColorManager.reset = function() { + this._currentColor = null; + this._colorInput.value = '#ffffff'; + this._notifyObservers(); + resetEffects(); +}; + +BalanceManager.reset = function() { + this.balanceSlider.value = 6500; + this.balanceValue.textContent = '6500K'; + resetEffects(); +}; + +DitherManager.reset = function() { + this._currentMode = 'none'; + this._modeSelect.value = 'none'; + this._notifyObservers(); + resetEffects(); +}; \ No newline at end of file |