diff --git a/js/mask_painter_manager.js b/js/mask_painter_manager.js
index f21ad9c..4a1a609 100644
--- a/js/mask_painter_manager.js
+++ b/js/mask_painter_manager.js
@@ -5,7 +5,8 @@ export class MaskPainterManager {
this.lastPoint = { x: 0, y: 0 };
this.settings = {
size: 30,
- hardness: 80, // Le retour de la dureté !
+ hardness: 80,
+ opacity: 100, // Le retour de la dureté !
mode: 'brush'
};
@@ -19,90 +20,71 @@ export class MaskPainterManager {
this.createSettingsToolbar();
}
- createSettingsToolbar() {
- if (this.toolbar) this.toolbar.remove();
+createSettingsToolbar() {
+ if (this.toolbar) this.toolbar.remove();
- const styleId = 'ls-mask-painter-style';
- if (!document.getElementById(styleId)) {
- const style = document.createElement('style');
- style.id = styleId;
- style.innerHTML = `
- .ls-mask-painter-toolbar .ls-tool-button {
- border: 1px solid white;
- background-color: transparent;
- color: white;
- border-radius: 4px;
- cursor: pointer;
- transition: background-color 0.2s;
- font-size: 18px;
- width: 32px;
- height: 32px;
- padding: 2px;
- }
- .ls-mask-painter-toolbar .ls-tool-button:not(.active):hover {
- background-color: rgba(255, 255, 255, 0.15);
- }
- .ls-mask-painter-toolbar .ls-tool-button.active {
- background-color: rgba(100, 180, 255, 0.4);
- border-color: #64b4ff;
- }
- `;
- document.head.appendChild(style);
- }
-
- this.toolbar = document.createElement("div");
- this.toolbar.className = 'ls-mask-painter-toolbar';
- Object.assign(this.toolbar.style, {
- position: 'fixed', display: 'none', zIndex: '10002',
- backgroundColor: 'rgba(40, 40, 40, 0.9)', border: '1px solid #555',
- borderRadius: '8px', padding: '8px', alignItems: 'center',
- gap: '6px', color: 'white', fontFamily: 'sans-serif'
- });
-
- // --- CORRECTION : On retire le curseur Opacity ---
- this.toolbar.innerHTML = `
-
-
- ${this.settings.size}
-
-
-
- ${this.settings.hardness}%
-
-
-
-
-
-
+ const styleId = 'ls-mask-painter-style';
+ if (!document.getElementById(styleId)) {
+ const style = document.createElement('style');
+ style.id = styleId;
+ style.innerHTML = `
+ .ls-mask-painter-toolbar .ls-tool-button { border: 1px solid white; background-color: transparent; color: white; border-radius: 4px; cursor: pointer; transition: background-color 0.2s; font-size: 18px; width: 32px; height: 32px; padding: 2px; }
+ .ls-mask-painter-toolbar .ls-tool-button:not(.active):hover { background-color: rgba(255, 255, 255, 0.15); }
+ .ls-mask-painter-toolbar .ls-tool-button.active { background-color: rgba(100, 180, 255, 0.4); border-color: #64b4ff; }
`;
- document.body.appendChild(this.toolbar);
-
- this.toolbar.querySelector('[data-mode="brush"]').classList.add('active');
-
- this.toolbar.addEventListener('input', (e) => {
- const setting = e.target.dataset.setting;
- if (setting) {
- this.settings[setting] = parseInt(e.target.value, 10);
- const suffix = (setting === 'hardness') ? '%' : '';
- this.toolbar.querySelector(`span[data-value="${setting}"]`).textContent = e.target.value + suffix;
- }
- });
-
- this.toolbar.addEventListener('click', (e) => {
- const target = e.target.closest('button');
- if (!target) return;
-
- if (target.dataset.mode) {
- this.settings.mode = target.dataset.mode;
- this.toolbar.querySelectorAll('.ls-tool-button').forEach(btn => btn.classList.remove('active'));
- target.classList.add('active');
- }
-
- if (target.dataset.action === 'apply') this.finalizeDrawing();
- if (target.dataset.action === 'cancel') this.hide();
- });
+ document.head.appendChild(style);
}
+ this.toolbar = document.createElement("div");
+ this.toolbar.className = 'ls-mask-painter-toolbar';
+ Object.assign(this.toolbar.style, {
+ position: 'fixed', display: 'none', zIndex: '10002',
+ backgroundColor: 'rgba(40, 40, 40, 0.9)', border: '1px solid #555',
+ borderRadius: '8px', padding: '8px', alignItems: 'center',
+ gap: '8px', color: 'white', fontFamily: 'sans-serif'
+ });
+
+ this.toolbar.innerHTML = `
+
+
+ ${this.settings.size}
+
+
+ ${this.settings.hardness}%
+
+
+ ${this.settings.opacity}%
+
+
+
+
+ `;
+ document.body.appendChild(this.toolbar);
+
+ this.toolbar.querySelector('[data-mode="brush"]').classList.add('active');
+
+ this.toolbar.addEventListener('input', (e) => {
+ const setting = e.target.dataset.setting;
+ if (setting) {
+ this.settings[setting] = parseInt(e.target.value, 10);
+ const suffix = (setting === 'hardness' || setting === 'opacity') ? '%' : '';
+ this.toolbar.querySelector(`span[data-value="${setting}"]`).textContent = e.target.value + suffix;
+ }
+ });
+
+ this.toolbar.addEventListener('click', (e) => {
+ const target = e.target.closest('button');
+ if (!target) return;
+ if (target.dataset.mode) {
+ this.settings.mode = target.dataset.mode;
+ this.toolbar.querySelectorAll('.ls-tool-button').forEach(btn => btn.classList.remove('active'));
+ target.classList.add('active');
+ }
+ if (target.dataset.action === 'apply') this.finalizeDrawing();
+ if (target.dataset.action === 'cancel') this.hide();
+ });
+}
+
async show() {
this.node.toolbar.activeTool = 'mask_painter';
this.activeLayer = this.node.getActiveLayer();
@@ -192,37 +174,37 @@ async show() {
this.positionToolbar();
}
- drawStamp(point) {
- const ctx = this.maskCtx;
- const radius = this.settings.size / 2;
- const gradient = ctx.createRadialGradient(point.x, point.y, 0, point.x, point.y, radius);
- // La dureté (0-100) contrôle où le dégradé devient transparent
- const hardnessStop = Math.max(0, Math.min(1, this.settings.hardness / 100));
-
- // Pinceau (Révéler) -> Peint de l'opaque avec 'source-over'
- if (this.settings.mode === 'brush') {
- gradient.addColorStop(0, 'white');
- gradient.addColorStop(hardnessStop, 'white');
- gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
- ctx.globalCompositeOperation = 'source-over';
- ctx.fillStyle = gradient;
-
- // Gomme (Cacher) -> Efface vers le transparent avec 'destination-out'
- } else {
- gradient.addColorStop(0, 'black'); // La couleur n'importe pas
- gradient.addColorStop(hardnessStop, 'black');
- gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
- ctx.globalCompositeOperation = 'destination-out';
- ctx.fillStyle = gradient;
- }
-
- ctx.beginPath();
- ctx.arc(point.x, point.y, radius, 0, 2 * Math.PI);
- ctx.fill();
-
- ctx.globalCompositeOperation = 'source-over';
- }
+drawStamp(point) {
+ const ctx = this.maskCtx;
+ const radius = this.settings.size / 2;
+ const gradient = ctx.createRadialGradient(point.x, point.y, 0, point.x, point.y, radius);
+ const hardnessStop = Math.max(0, Math.min(1, this.settings.hardness / 100));
+
+ ctx.save();
+ ctx.globalAlpha = this.settings.opacity / 100;
+
+ if (this.settings.mode === 'brush') {
+ gradient.addColorStop(0, 'white');
+ gradient.addColorStop(hardnessStop, 'white');
+ gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
+ // On revient à 'lighter'
+ ctx.globalCompositeOperation = 'lighter';
+ ctx.fillStyle = gradient;
+ } else { // La gomme reste correcte
+ gradient.addColorStop(0, 'black');
+ gradient.addColorStop(hardnessStop, 'black');
+ gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.fillStyle = gradient;
+ }
+
+ ctx.beginPath();
+ ctx.arc(point.x, point.y, radius, 0, 2 * Math.PI);
+ ctx.fill();
+
+ ctx.restore();
+}
hide() {
if (this.activeLayer) {
const props = this.node.layer_properties[this.activeLayer.name];
@@ -343,45 +325,50 @@ async finalizeDrawing() {
handleMouseEvent(e) {
if (!this.activeLayer) return;
- // On calcule la position et on vérifie si on est sur la barre d'outils
+ // La gestion de la barre d'outils reste la même
const mainCanvasRect = this.node.previewCanvas.getBoundingClientRect();
const zoom = app.canvas.ds.scale || 1;
const mouseX = (e.clientX - mainCanvasRect.left) / zoom;
const mouseY = (e.clientY - mainCanvasRect.top) / zoom;
const isOverToolbar = this.node.toolbar.isClickOnToolbar(mouseX, mouseY);
- // CAS 1 : La souris est sur la barre d'outils principale
if (isOverToolbar) {
- this.liveOverlay.style.cursor = 'default'; // On montre le curseur normal
- this.updateLivePreview(null); // On met à jour l'aperçu sans le curseur personnalisé
-
+ this.liveOverlay.style.cursor = 'default';
+ this.updateLivePreview(null);
if (e.type === 'mousedown') {
this.node.toolbar.handleClick(e, mouseX, mouseY);
}
- return; // On s'arrête ici
+ return;
}
-
- // CAS 2 : La souris est sur la zone de dessin
- this.liveOverlay.style.cursor = 'none'; // On cache le curseur normal pour dessiner le nôtre
+
+ this.liveOverlay.style.cursor = 'none';
if (e.type === 'mousedown') {
this.isDrawing = true;
const coords = this.getOriginalCoords(e);
this.lastPoint = coords;
- this.drawStamp(coords);
+ this.drawStamp(coords); // On dessine le premier point
}
else if (e.type === 'mousemove') {
if (this.isDrawing) {
const coords = this.getOriginalCoords(e);
- this.drawStroke(this.lastPoint, coords);
- this.lastPoint = coords;
+
+ // --- TA SOLUTION : L'ESPACEMENT ---
+ const dist = Math.hypot(coords.x - this.lastPoint.x, coords.y - this.lastPoint.y);
+ const spacing = this.settings.size / 2; // On peut ajuster cette valeur
+
+ // On ne dessine un nouveau segment que si on a bougé suffisamment
+ if (dist >= spacing) {
+ this.drawStroke(this.lastPoint, coords);
+ this.lastPoint = coords; // On met à jour la position du dernier point DESSINÉ
+ }
+ // --- FIN DE LA SOLUTION ---
}
}
else if (e.type === 'mouseup' || e.type === 'mouseleave') {
this.isDrawing = false;
}
- // On met à jour l'aperçu et le curseur personnalisé
this.updateLivePreview(e);
}