Spaces:
Running
Running
Update script.js
Browse files
script.js
CHANGED
@@ -7,9 +7,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
7 |
const saturationSlider = document.getElementById('saturation');
|
8 |
const shadowsSlider = document.getElementById('shadows');
|
9 |
const highlightsSlider = document.getElementById('highlights');
|
10 |
-
const hueRedSlider = document.getElementById('hue-red');
|
11 |
-
const hueGreenSlider = document.getElementById('hue-green');
|
12 |
-
const hueBlueSlider = document.getElementById('hue-blue');
|
13 |
const brightnessValue = document.getElementById('brightness-value');
|
14 |
const contrastValue = document.getElementById('contrast-value');
|
15 |
const saturationValue = document.getElementById('saturation-value');
|
@@ -20,19 +17,20 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
20 |
const downloadBtn = document.getElementById('download-btn');
|
21 |
const loadingOverlay = document.getElementById('loading-overlay');
|
22 |
|
23 |
-
//
|
24 |
const redCurveCanvas = document.getElementById('red-curve');
|
25 |
const greenCurveCanvas = document.getElementById('green-curve');
|
26 |
const blueCurveCanvas = document.getElementById('blue-curve');
|
|
|
27 |
|
28 |
// カーブ制御用の変数
|
29 |
let redCurvePoints = [{x: 0, y: 0}, {x: 255, y: 255}];
|
30 |
let greenCurvePoints = [{x: 0, y: 0}, {x: 255, y: 255}];
|
31 |
let blueCurvePoints = [{x: 0, y: 0}, {x: 255, y: 255}];
|
|
|
32 |
|
33 |
let camanInstance = null;
|
34 |
let originalImageData = null;
|
35 |
-
let currentFilters = {};
|
36 |
|
37 |
// 画像アップロードの処理
|
38 |
imageUpload.addEventListener('change', function(e) {
|
@@ -71,7 +69,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
71 |
camanInstance = this;
|
72 |
originalImageData = this.canvas.toDataURL();
|
73 |
|
74 |
-
//
|
75 |
initCurves();
|
76 |
hideLoading();
|
77 |
});
|
@@ -109,69 +107,22 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
109 |
}, 100);
|
110 |
});
|
111 |
|
112 |
-
//
|
113 |
function applyFilters() {
|
114 |
if (!camanInstance) return;
|
115 |
|
116 |
camanInstance.revert(false);
|
117 |
|
118 |
-
//
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
saturation: parseInt(saturationSlider.value),
|
123 |
-
shadows: parseInt(shadowsSlider.value),
|
124 |
-
highlights: parseInt(highlightsSlider.value),
|
125 |
-
hueRed: parseInt(hueRedSlider.value),
|
126 |
-
hueGreen: parseInt(hueGreenSlider.value),
|
127 |
-
hueBlue: parseInt(hueBlueSlider.value)
|
128 |
-
};
|
129 |
-
|
130 |
-
// 明るさ(範囲拡大: -100 to 100 → -150 to 150)
|
131 |
-
camanInstance.brightness(currentFilters.brightness * 1.5);
|
132 |
-
|
133 |
-
// コントラスト(効果を強化)
|
134 |
-
camanInstance.contrast(currentFilters.contrast * 1.5);
|
135 |
-
|
136 |
-
// 彩度
|
137 |
-
camanInstance.saturation(currentFilters.saturation);
|
138 |
-
|
139 |
-
// シャドウ(効果を強化、範囲拡大)
|
140 |
-
if (currentFilters.shadows > 0) {
|
141 |
-
camanInstance.shadows(currentFilters.shadows * 2);
|
142 |
-
} else {
|
143 |
-
camanInstance.exposure(currentFilters.shadows / 5);
|
144 |
-
}
|
145 |
-
|
146 |
-
// ハイライト(効果を強化、範囲拡大)
|
147 |
-
if (currentFilters.highlights > 0) {
|
148 |
-
camanInstance.highlights(currentFilters.highlights * 2);
|
149 |
-
} else {
|
150 |
-
camanInstance.exposure(currentFilters.highlights / 5);
|
151 |
-
}
|
152 |
-
|
153 |
-
// HSL調整
|
154 |
-
camanInstance.hue(currentFilters.hueRed / 2);
|
155 |
-
camanInstance.hue(currentFilters.hueGreen / 2);
|
156 |
-
camanInstance.hue(currentFilters.hueBlue / 2);
|
157 |
|
158 |
// RGBカーブを適用
|
159 |
applyRgbCurves();
|
160 |
|
161 |
-
//
|
162 |
-
|
163 |
-
camanInstance.sharpen(currentFilters.sharpness);
|
164 |
-
}
|
165 |
-
|
166 |
-
// 追加フィルター: ノイズ
|
167 |
-
if (currentFilters.noise) {
|
168 |
-
camanInstance.noise(currentFilters.noise);
|
169 |
-
}
|
170 |
-
|
171 |
-
// 追加フィルター: ビネット
|
172 |
-
if (currentFilters.vignette) {
|
173 |
-
camanInstance.vignette("10%", currentFilters.vignette * 2);
|
174 |
-
}
|
175 |
|
176 |
camanInstance.render();
|
177 |
}
|
@@ -181,10 +132,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
181 |
drawCurve(redCurveCanvas, redCurvePoints, 'red');
|
182 |
drawCurve(greenCurveCanvas, greenCurvePoints, 'green');
|
183 |
drawCurve(blueCurveCanvas, blueCurvePoints, 'blue');
|
|
|
184 |
|
185 |
setupCurveInteraction(redCurveCanvas, redCurvePoints, 'red');
|
186 |
setupCurveInteraction(greenCurveCanvas, greenCurvePoints, 'green');
|
187 |
setupCurveInteraction(blueCurveCanvas, blueCurvePoints, 'blue');
|
|
|
188 |
}
|
189 |
|
190 |
// カーブを描画
|
@@ -285,6 +238,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
285 |
}
|
286 |
|
287 |
drawCurve(canvas, points, color);
|
|
|
|
|
|
|
288 |
});
|
289 |
|
290 |
canvas.addEventListener('mousemove', function(e) {
|
@@ -309,16 +265,25 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
309 |
draggedPoint.y = y;
|
310 |
|
311 |
drawCurve(canvas, points, color);
|
|
|
|
|
|
|
312 |
});
|
313 |
|
314 |
canvas.addEventListener('mouseup', function() {
|
315 |
isDragging = false;
|
316 |
draggedPoint = null;
|
|
|
|
|
|
|
317 |
});
|
318 |
|
319 |
canvas.addEventListener('mouseleave', function() {
|
320 |
isDragging = false;
|
321 |
draggedPoint = null;
|
|
|
|
|
|
|
322 |
});
|
323 |
|
324 |
// ポイントを削除するダブルクリック
|
@@ -336,6 +301,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
336 |
if (distance < 15) {
|
337 |
points.splice(i, 1);
|
338 |
drawCurve(canvas, points, color);
|
|
|
|
|
|
|
339 |
return;
|
340 |
}
|
341 |
}
|
@@ -360,6 +328,48 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
360 |
});
|
361 |
}
|
362 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
363 |
// カーブデータを作成
|
364 |
function createCurveData(points) {
|
365 |
points.sort((a, b) => a.x - b.x);
|
@@ -396,9 +406,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
396 |
saturationSlider.value = 0;
|
397 |
shadowsSlider.value = 0;
|
398 |
highlightsSlider.value = 0;
|
399 |
-
hueRedSlider.value = 0;
|
400 |
-
hueGreenSlider.value = 0;
|
401 |
-
hueBlueSlider.value = 0;
|
402 |
|
403 |
brightnessValue.textContent = '0';
|
404 |
contrastValue.textContent = '0';
|
@@ -410,10 +417,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
410 |
redCurvePoints = [{x: 0, y: 0}, {x: 255, y: 255}];
|
411 |
greenCurvePoints = [{x: 0, y: 0}, {x: 255, y: 255}];
|
412 |
blueCurvePoints = [{x: 0, y: 0}, {x: 255, y: 255}];
|
|
|
413 |
|
414 |
drawCurve(redCurveCanvas, redCurvePoints, 'red');
|
415 |
drawCurve(greenCurveCanvas, greenCurvePoints, 'green');
|
416 |
drawCurve(blueCurveCanvas, blueCurvePoints, 'blue');
|
|
|
417 |
|
418 |
// 画像をリセット
|
419 |
const img = new Image();
|
@@ -429,9 +438,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
429 |
});
|
430 |
};
|
431 |
img.src = originalImageData;
|
432 |
-
|
433 |
-
// フィルター設定をリセット
|
434 |
-
currentFilters = {};
|
435 |
});
|
436 |
|
437 |
// ダウンロードボタン
|
@@ -452,53 +458,4 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
452 |
function hideLoading() {
|
453 |
loadingOverlay.style.display = 'none';
|
454 |
}
|
455 |
-
|
456 |
-
// 追加機能: キーボードショートカット
|
457 |
-
document.addEventListener('keydown', function(e) {
|
458 |
-
if (e.ctrlKey && e.key === 'z') {
|
459 |
-
resetBtn.click();
|
460 |
-
} else if (e.ctrlKey && e.key === 's') {
|
461 |
-
downloadBtn.click();
|
462 |
-
e.preventDefault();
|
463 |
-
} else if (e.key === 'Enter') {
|
464 |
-
applyBtn.click();
|
465 |
-
}
|
466 |
-
});
|
467 |
-
|
468 |
-
// 追加機能: プリセットフィルター
|
469 |
-
function applyPreset(presetName) {
|
470 |
-
switch(presetName) {
|
471 |
-
case 'vintage':
|
472 |
-
brightnessSlider.value = 10;
|
473 |
-
contrastSlider.value = 20;
|
474 |
-
saturationSlider.value = -30;
|
475 |
-
shadowsSlider.value = 15;
|
476 |
-
highlightsSlider.value = -10;
|
477 |
-
hueRedSlider.value = 10;
|
478 |
-
break;
|
479 |
-
case 'dramatic':
|
480 |
-
brightnessSlider.value = -10;
|
481 |
-
contrastSlider.value = 50;
|
482 |
-
saturationSlider.value = -20;
|
483 |
-
shadowsSlider.value = 30;
|
484 |
-
highlightsSlider.value = -20;
|
485 |
-
break;
|
486 |
-
case 'bright':
|
487 |
-
brightnessSlider.value = 30;
|
488 |
-
contrastSlider.value = 20;
|
489 |
-
saturationSlider.value = 40;
|
490 |
-
shadowsSlider.value = 20;
|
491 |
-
highlightsSlider.value = 10;
|
492 |
-
break;
|
493 |
-
}
|
494 |
-
|
495 |
-
// スライダー値を更新
|
496 |
-
brightnessValue.textContent = brightnessSlider.value;
|
497 |
-
contrastValue.textContent = contrastSlider.value;
|
498 |
-
saturationValue.textContent = saturationSlider.value;
|
499 |
-
shadowsValue.textContent = shadowsSlider.value;
|
500 |
-
highlightsValue.textContent = highlightsSlider.value;
|
501 |
-
|
502 |
-
applyBtn.click();
|
503 |
-
}
|
504 |
});
|
|
|
7 |
const saturationSlider = document.getElementById('saturation');
|
8 |
const shadowsSlider = document.getElementById('shadows');
|
9 |
const highlightsSlider = document.getElementById('highlights');
|
|
|
|
|
|
|
10 |
const brightnessValue = document.getElementById('brightness-value');
|
11 |
const contrastValue = document.getElementById('contrast-value');
|
12 |
const saturationValue = document.getElementById('saturation-value');
|
|
|
17 |
const downloadBtn = document.getElementById('download-btn');
|
18 |
const loadingOverlay = document.getElementById('loading-overlay');
|
19 |
|
20 |
+
// カーブ用のキャンバス
|
21 |
const redCurveCanvas = document.getElementById('red-curve');
|
22 |
const greenCurveCanvas = document.getElementById('green-curve');
|
23 |
const blueCurveCanvas = document.getElementById('blue-curve');
|
24 |
+
const luminanceCurveCanvas = document.getElementById('luminance-curve');
|
25 |
|
26 |
// カーブ制御用の変数
|
27 |
let redCurvePoints = [{x: 0, y: 0}, {x: 255, y: 255}];
|
28 |
let greenCurvePoints = [{x: 0, y: 0}, {x: 255, y: 255}];
|
29 |
let blueCurvePoints = [{x: 0, y: 0}, {x: 255, y: 255}];
|
30 |
+
let luminanceCurvePoints = [{x: 0, y: 0}, {x: 255, y: 255}];
|
31 |
|
32 |
let camanInstance = null;
|
33 |
let originalImageData = null;
|
|
|
34 |
|
35 |
// 画像アップロードの処理
|
36 |
imageUpload.addEventListener('change', function(e) {
|
|
|
69 |
camanInstance = this;
|
70 |
originalImageData = this.canvas.toDataURL();
|
71 |
|
72 |
+
// カーブを初期化
|
73 |
initCurves();
|
74 |
hideLoading();
|
75 |
});
|
|
|
107 |
}, 100);
|
108 |
});
|
109 |
|
110 |
+
// フィルターを適用する関数
|
111 |
function applyFilters() {
|
112 |
if (!camanInstance) return;
|
113 |
|
114 |
camanInstance.revert(false);
|
115 |
|
116 |
+
// 基本調整
|
117 |
+
camanInstance.brightness(parseInt(brightnessSlider.value));
|
118 |
+
camanInstance.contrast(parseInt(contrastSlider.value) * 1.5); // コントラスト強化
|
119 |
+
camanInstance.saturation(parseInt(saturationSlider.value));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
|
121 |
// RGBカーブを適用
|
122 |
applyRgbCurves();
|
123 |
|
124 |
+
// 輝度カーブを適用(シャドウ/ハイライト調整)
|
125 |
+
applyLuminanceCurve();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
126 |
|
127 |
camanInstance.render();
|
128 |
}
|
|
|
132 |
drawCurve(redCurveCanvas, redCurvePoints, 'red');
|
133 |
drawCurve(greenCurveCanvas, greenCurvePoints, 'green');
|
134 |
drawCurve(blueCurveCanvas, blueCurvePoints, 'blue');
|
135 |
+
drawCurve(luminanceCurveCanvas, luminanceCurvePoints, '#888');
|
136 |
|
137 |
setupCurveInteraction(redCurveCanvas, redCurvePoints, 'red');
|
138 |
setupCurveInteraction(greenCurveCanvas, greenCurvePoints, 'green');
|
139 |
setupCurveInteraction(blueCurveCanvas, blueCurvePoints, 'blue');
|
140 |
+
setupCurveInteraction(luminanceCurveCanvas, luminanceCurvePoints, '#888');
|
141 |
}
|
142 |
|
143 |
// カーブを描画
|
|
|
238 |
}
|
239 |
|
240 |
drawCurve(canvas, points, color);
|
241 |
+
if (canvas !== luminanceCurveCanvas) {
|
242 |
+
applyBtn.click();
|
243 |
+
}
|
244 |
});
|
245 |
|
246 |
canvas.addEventListener('mousemove', function(e) {
|
|
|
265 |
draggedPoint.y = y;
|
266 |
|
267 |
drawCurve(canvas, points, color);
|
268 |
+
if (canvas !== luminanceCurveCanvas) {
|
269 |
+
applyBtn.click();
|
270 |
+
}
|
271 |
});
|
272 |
|
273 |
canvas.addEventListener('mouseup', function() {
|
274 |
isDragging = false;
|
275 |
draggedPoint = null;
|
276 |
+
if (canvas === luminanceCurveCanvas) {
|
277 |
+
applyBtn.click();
|
278 |
+
}
|
279 |
});
|
280 |
|
281 |
canvas.addEventListener('mouseleave', function() {
|
282 |
isDragging = false;
|
283 |
draggedPoint = null;
|
284 |
+
if (canvas === luminanceCurveCanvas && isDragging) {
|
285 |
+
applyBtn.click();
|
286 |
+
}
|
287 |
});
|
288 |
|
289 |
// ポイントを削除するダブルクリック
|
|
|
301 |
if (distance < 15) {
|
302 |
points.splice(i, 1);
|
303 |
drawCurve(canvas, points, color);
|
304 |
+
if (canvas !== luminanceCurveCanvas) {
|
305 |
+
applyBtn.click();
|
306 |
+
}
|
307 |
return;
|
308 |
}
|
309 |
}
|
|
|
328 |
});
|
329 |
}
|
330 |
|
331 |
+
// 輝度カーブを適用(連続的なシャドウ/ハイライト調整)
|
332 |
+
function applyLuminanceCurve() {
|
333 |
+
if (!camanInstance) return;
|
334 |
+
|
335 |
+
const luminanceCurve = createCurveData(luminanceCurvePoints);
|
336 |
+
const shadowAmount = parseInt(shadowsSlider.value) / 100 * 50; // -50から50の範囲に調整
|
337 |
+
const highlightAmount = parseInt(highlightsSlider.value) / 100 * 50;
|
338 |
+
|
339 |
+
camanInstance.process("luminanceAdjustment", function(rgba) {
|
340 |
+
// ピクセルの輝度を計算
|
341 |
+
const luminance = 0.299 * rgba.r + 0.587 * rgba.g + 0.114 * rgba.b;
|
342 |
+
|
343 |
+
// 輝度カーブから調整値を取得
|
344 |
+
const curveAdjustment = (luminanceCurve[luminance] - luminance) / 255 * 100;
|
345 |
+
|
346 |
+
// シャドウ/ハイライト調整を適用
|
347 |
+
let adjustment = 0;
|
348 |
+
|
349 |
+
// シャドウ調整(低輝度ほど強く適用)
|
350 |
+
if (shadowAmount !== 0) {
|
351 |
+
const shadowFactor = 1 - (luminance / 255); // 0(明るい)~1(暗い)
|
352 |
+
adjustment += shadowAmount * shadowFactor;
|
353 |
+
}
|
354 |
+
|
355 |
+
// ハイライト調整(高輝度ほど強く適用)
|
356 |
+
if (highlightAmount !== 0) {
|
357 |
+
const highlightFactor = luminance / 255; // 0(暗い)~1(明るい)
|
358 |
+
adjustment += highlightAmount * highlightFactor;
|
359 |
+
}
|
360 |
+
|
361 |
+
// 輝度カーブの調整を追加
|
362 |
+
adjustment += curveAdjustment;
|
363 |
+
|
364 |
+
// 調整を適用
|
365 |
+
rgba.r = Math.min(255, Math.max(0, rgba.r + adjustment));
|
366 |
+
rgba.g = Math.min(255, Math.max(0, rgba.g + adjustment));
|
367 |
+
rgba.b = Math.min(255, Math.max(0, rgba.b + adjustment));
|
368 |
+
|
369 |
+
return rgba;
|
370 |
+
});
|
371 |
+
}
|
372 |
+
|
373 |
// カーブデータを作成
|
374 |
function createCurveData(points) {
|
375 |
points.sort((a, b) => a.x - b.x);
|
|
|
406 |
saturationSlider.value = 0;
|
407 |
shadowsSlider.value = 0;
|
408 |
highlightsSlider.value = 0;
|
|
|
|
|
|
|
409 |
|
410 |
brightnessValue.textContent = '0';
|
411 |
contrastValue.textContent = '0';
|
|
|
417 |
redCurvePoints = [{x: 0, y: 0}, {x: 255, y: 255}];
|
418 |
greenCurvePoints = [{x: 0, y: 0}, {x: 255, y: 255}];
|
419 |
blueCurvePoints = [{x: 0, y: 0}, {x: 255, y: 255}];
|
420 |
+
luminanceCurvePoints = [{x: 0, y: 0}, {x: 255, y: 255}];
|
421 |
|
422 |
drawCurve(redCurveCanvas, redCurvePoints, 'red');
|
423 |
drawCurve(greenCurveCanvas, greenCurvePoints, 'green');
|
424 |
drawCurve(blueCurveCanvas, blueCurvePoints, 'blue');
|
425 |
+
drawCurve(luminanceCurveCanvas, luminanceCurvePoints, '#888');
|
426 |
|
427 |
// 画像をリセット
|
428 |
const img = new Image();
|
|
|
438 |
});
|
439 |
};
|
440 |
img.src = originalImageData;
|
|
|
|
|
|
|
441 |
});
|
442 |
|
443 |
// ダウンロードボタン
|
|
|
458 |
function hideLoading() {
|
459 |
loadingOverlay.style.display = 'none';
|
460 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
461 |
});
|