mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-09-14 20:22:22 +02:00
120 lines
4.1 KiB
JavaScript
120 lines
4.1 KiB
JavaScript
/**
|
|
* Local two-color gradient tool.
|
|
*
|
|
* The drag becomes a retained effect on completion, so previews never mutate
|
|
* source pixels or compound across pointer-move events.
|
|
*/
|
|
import { state } from '../state.js';
|
|
import { canvasCoords } from '../canvas-coords.js';
|
|
import { isLayerPixelLocked } from '../layer-groups.js';
|
|
|
|
export function createGradientTool({ activeLayer, saveState, composite, schedulePersist, renderLayerPanel, getSettings }) {
|
|
let start = null;
|
|
let lastPoint = null;
|
|
let activeLayerId = null;
|
|
let historySaved = false;
|
|
|
|
function settings() {
|
|
const values = getSettings?.() || {};
|
|
return {
|
|
start: values.start || state.gradientStart || state.color,
|
|
mid: values.mid || state.gradientMid || '#808080',
|
|
midPosition: values.midPosition ?? state.gradientMidPosition ?? 50,
|
|
midEnabled: values.midEnabled ?? state.gradientMidEnabled ?? false,
|
|
extraStops: Array.isArray(values.extraStops) ? values.extraStops : (state.gradientStops || []),
|
|
end: values.end || state.gradientEnd || '#ffffff',
|
|
endAlpha: values.endAlpha ?? state.gradientEndAlpha ?? 100,
|
|
opacity: values.opacity ?? state.gradientOpacity ?? 100,
|
|
type: values.type || state.gradientType || 'linear-gradient',
|
|
};
|
|
}
|
|
|
|
function preview(point) {
|
|
const layer = activeLayer();
|
|
if (!start || !layer || layer.id !== activeLayerId) return false;
|
|
const dx = point.x - start.x;
|
|
const dy = point.y - start.y;
|
|
if (Math.hypot(dx, dy) < 0.5) return false;
|
|
const values = settings();
|
|
const stops = [
|
|
{ position: 0, color: values.start, alpha: 1 },
|
|
...(values.midEnabled ? [{ position: Number(values.midPosition), color: values.mid, alpha: 1 }] : []),
|
|
...values.extraStops.slice(0, 10).map(stop => ({
|
|
position: Math.max(1, Math.min(99, Number(stop.position) || 50)),
|
|
color: stop.color || '#808080',
|
|
alpha: 1,
|
|
})),
|
|
{ position: 100, color: values.end, alpha: Math.max(0, Math.min(100, Number(values.endAlpha))) / 100 },
|
|
].sort((a, b) => a.position - b.position);
|
|
layer._effectPreview = {
|
|
id: 'gradient-preview', type: values.type, name: values.type === 'radial-gradient' ? 'Radial Gradient' : 'Gradient', visible: true, opacity: 1,
|
|
params: { x1: start.x, y1: start.y, x2: point.x, y2: point.y, stops, opacity: Math.max(0, Math.min(100, Number(values.opacity))) / 100 },
|
|
};
|
|
composite();
|
|
return true;
|
|
}
|
|
|
|
return {
|
|
begin(e) {
|
|
const layer = activeLayer();
|
|
if (!layer) return false;
|
|
if (isLayerPixelLocked(state, layer)) return false;
|
|
if (layer.kind === 'placed') return false;
|
|
activeLayerId = layer.id;
|
|
start = canvasCoords(e, state.mainCanvas);
|
|
lastPoint = { ...start };
|
|
state.gradientActive = true;
|
|
historySaved = false;
|
|
return true;
|
|
},
|
|
|
|
drag(e) {
|
|
if (!state.gradientActive) return false;
|
|
lastPoint = canvasCoords(e, state.mainCanvas);
|
|
preview(lastPoint);
|
|
return true;
|
|
},
|
|
|
|
end(e) {
|
|
if (!state.gradientActive) return false;
|
|
const point = e && (Number.isFinite(e.clientX) || (e.touches && e.touches.length))
|
|
? canvasCoords(e, state.mainCanvas)
|
|
: lastPoint;
|
|
const changed = point ? preview(point) : false;
|
|
const layer = activeLayer();
|
|
state.gradientActive = false;
|
|
if (layer?._effectPreview && changed) {
|
|
if (!historySaved) {
|
|
saveState('Gradient');
|
|
historySaved = true;
|
|
}
|
|
layer.effects = Array.isArray(layer.effects) ? layer.effects : [];
|
|
layer.effects.push(layer._effectPreview);
|
|
}
|
|
if (layer) layer._effectPreview = null;
|
|
start = null;
|
|
lastPoint = null;
|
|
activeLayerId = null;
|
|
historySaved = false;
|
|
if (changed) {
|
|
renderLayerPanel?.();
|
|
schedulePersist?.();
|
|
}
|
|
return true;
|
|
},
|
|
|
|
cancel() {
|
|
if (!state.gradientActive) return false;
|
|
const layer = activeLayer();
|
|
if (layer) layer._effectPreview = null;
|
|
state.gradientActive = false;
|
|
start = null;
|
|
lastPoint = null;
|
|
activeLayerId = null;
|
|
historySaved = false;
|
|
composite();
|
|
return true;
|
|
},
|
|
};
|
|
}
|