mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-21 20:52:12 -03:00
Update Canvas.js
This commit is contained in:
187
js/Canvas.js
187
js/Canvas.js
@@ -48,17 +48,33 @@ export class Canvas {
|
|||||||
let isResizing = false;
|
let isResizing = false;
|
||||||
let resizeHandle = null;
|
let resizeHandle = null;
|
||||||
let lastClickTime = 0;
|
let lastClickTime = 0;
|
||||||
|
let isAltPressed = false;
|
||||||
|
let dragStartX = 0;
|
||||||
|
let dragStartY = 0;
|
||||||
|
let originalWidth = 0;
|
||||||
|
let originalHeight = 0;
|
||||||
|
|
||||||
document.addEventListener('keydown', (e) => {
|
document.addEventListener('keydown', (e) => {
|
||||||
if (e.key === 'Control') {
|
if (e.key === 'Control') {
|
||||||
this.isCtrlPressed = true;
|
this.isCtrlPressed = true;
|
||||||
}
|
}
|
||||||
|
if (e.key === 'Alt') {
|
||||||
|
isAltPressed = true;
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
if (e.key === 'Delete' && this.selectedLayer) {
|
||||||
|
const index = this.layers.indexOf(this.selectedLayer);
|
||||||
|
this.removeLayer(index);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('keyup', (e) => {
|
document.addEventListener('keyup', (e) => {
|
||||||
if (e.key === 'Control') {
|
if (e.key === 'Control') {
|
||||||
this.isCtrlPressed = false;
|
this.isCtrlPressed = false;
|
||||||
}
|
}
|
||||||
|
if (e.key === 'Alt') {
|
||||||
|
isAltPressed = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.canvas.addEventListener('mousedown', (e) => {
|
this.canvas.addEventListener('mousedown', (e) => {
|
||||||
@@ -80,6 +96,13 @@ export class Canvas {
|
|||||||
if (result) {
|
if (result) {
|
||||||
const clickedLayer = result.layer;
|
const clickedLayer = result.layer;
|
||||||
|
|
||||||
|
dragStartX = mouseX;
|
||||||
|
dragStartY = mouseY;
|
||||||
|
if (clickedLayer) {
|
||||||
|
originalWidth = clickedLayer.width;
|
||||||
|
originalHeight = clickedLayer.height;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.isCtrlPressed) {
|
if (this.isCtrlPressed) {
|
||||||
const index = this.selectedLayers.indexOf(clickedLayer);
|
const index = this.selectedLayers.indexOf(clickedLayer);
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
@@ -125,57 +148,18 @@ export class Canvas {
|
|||||||
const mouseX = e.clientX - rect.left;
|
const mouseX = e.clientX - rect.left;
|
||||||
const mouseY = e.clientY - rect.top;
|
const mouseY = e.clientY - rect.top;
|
||||||
|
|
||||||
if (isResizing && resizeHandle) {
|
if (isDragging && isAltPressed) {
|
||||||
const dx = mouseX - lastX;
|
const dx = mouseX - dragStartX;
|
||||||
const dy = mouseY - lastY;
|
const dy = mouseY - dragStartY;
|
||||||
|
|
||||||
this.selectedLayers.forEach(layer => {
|
if (Math.abs(dx) > Math.abs(dy)) {
|
||||||
const originalWidth = layer.width;
|
this.selectedLayer.width = Math.max(20, originalWidth + dx);
|
||||||
const originalHeight = layer.height;
|
} else {
|
||||||
const originalX = layer.x;
|
this.selectedLayer.height = Math.max(20, originalHeight + dy);
|
||||||
const originalY = layer.y;
|
}
|
||||||
|
|
||||||
switch(resizeHandle) {
|
|
||||||
case 'nw':
|
|
||||||
layer.width = Math.max(20, originalWidth - dx);
|
|
||||||
layer.height = Math.max(20, originalHeight - dy);
|
|
||||||
layer.x = originalX + (originalWidth - layer.width);
|
|
||||||
layer.y = originalY + (originalHeight - layer.height);
|
|
||||||
break;
|
|
||||||
case 'ne':
|
|
||||||
layer.width = Math.max(20, originalWidth + dx);
|
|
||||||
layer.height = Math.max(20, originalHeight - dy);
|
|
||||||
layer.y = originalY + (originalHeight - layer.height);
|
|
||||||
break;
|
|
||||||
case 'se':
|
|
||||||
layer.width = Math.max(20, originalWidth + dx);
|
|
||||||
layer.height = Math.max(20, originalHeight + dy);
|
|
||||||
break;
|
|
||||||
case 'sw':
|
|
||||||
layer.width = Math.max(20, originalWidth - dx);
|
|
||||||
layer.height = Math.max(20, originalHeight + dy);
|
|
||||||
layer.x = originalX + (originalWidth - layer.width);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
lastX = mouseX;
|
|
||||||
lastY = mouseY;
|
|
||||||
this.render();
|
this.render();
|
||||||
} else if (isRotating) {
|
} else if (isDragging && !isAltPressed) {
|
||||||
const currentAngle = Math.atan2(
|
|
||||||
mouseY - this.rotationCenter.y,
|
|
||||||
mouseX - this.rotationCenter.x
|
|
||||||
);
|
|
||||||
let rotation = (currentAngle - this.rotationStartAngle) * (180/Math.PI);
|
|
||||||
const snap = 15;
|
|
||||||
rotation = Math.round(rotation / snap) * snap;
|
|
||||||
|
|
||||||
this.selectedLayers.forEach(layer => {
|
|
||||||
layer.rotation = rotation;
|
|
||||||
});
|
|
||||||
this.render();
|
|
||||||
} else if (isDragging) {
|
|
||||||
const dx = mouseX - lastX;
|
const dx = mouseX - lastX;
|
||||||
const dy = mouseY - lastY;
|
const dy = mouseY - lastY;
|
||||||
|
|
||||||
@@ -189,11 +173,13 @@ export class Canvas {
|
|||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
const cursor = this.getResizeHandle(mouseX, mouseY)
|
const cursor = isAltPressed && isDragging
|
||||||
? 'nw-resize'
|
? (Math.abs(mouseX - dragStartX) > Math.abs(mouseY - dragStartY) ? 'ew-resize' : 'ns-resize')
|
||||||
: this.isRotationHandle(mouseX, mouseY)
|
: this.getResizeHandle(mouseX, mouseY)
|
||||||
? 'grab'
|
? 'nw-resize'
|
||||||
: isDragging ? 'move' : 'default';
|
: this.isRotationHandle(mouseX, mouseY)
|
||||||
|
? 'grab'
|
||||||
|
: isDragging ? 'move' : 'default';
|
||||||
this.canvas.style.cursor = cursor;
|
this.canvas.style.cursor = cursor;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -449,7 +435,7 @@ export class Canvas {
|
|||||||
const sortedLayers = [...this.layers].sort((a, b) => a.zIndex - b.zIndex);
|
const sortedLayers = [...this.layers].sort((a, b) => a.zIndex - b.zIndex);
|
||||||
|
|
||||||
sortedLayers.forEach(layer => {
|
sortedLayers.forEach(layer => {
|
||||||
if (!layer.image || layer.width <= 0 || layer.height <= 0) return;
|
if (!layer.image) return;
|
||||||
|
|
||||||
ctx.save();
|
ctx.save();
|
||||||
|
|
||||||
@@ -457,6 +443,7 @@ export class Canvas {
|
|||||||
const centerY = layer.y + layer.height/2;
|
const centerY = layer.y + layer.height/2;
|
||||||
const rad = layer.rotation * Math.PI / 180;
|
const rad = layer.rotation * Math.PI / 180;
|
||||||
|
|
||||||
|
// 1. 先设置变换
|
||||||
ctx.setTransform(
|
ctx.setTransform(
|
||||||
Math.cos(rad), Math.sin(rad),
|
Math.cos(rad), Math.sin(rad),
|
||||||
-Math.sin(rad), Math.cos(rad),
|
-Math.sin(rad), Math.cos(rad),
|
||||||
@@ -466,6 +453,7 @@ export class Canvas {
|
|||||||
ctx.imageSmoothingEnabled = true;
|
ctx.imageSmoothingEnabled = true;
|
||||||
ctx.imageSmoothingQuality = 'high';
|
ctx.imageSmoothingQuality = 'high';
|
||||||
|
|
||||||
|
// 2. 先绘制原始图像
|
||||||
ctx.drawImage(
|
ctx.drawImage(
|
||||||
layer.image,
|
layer.image,
|
||||||
-layer.width/2,
|
-layer.width/2,
|
||||||
@@ -474,6 +462,39 @@ export class Canvas {
|
|||||||
layer.height
|
layer.height
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 3. 再应用遮罩
|
||||||
|
if (layer.mask) {
|
||||||
|
try {
|
||||||
|
console.log("Applying mask to layer");
|
||||||
|
const maskCanvas = document.createElement('canvas');
|
||||||
|
const maskCtx = maskCanvas.getContext('2d');
|
||||||
|
maskCanvas.width = layer.width;
|
||||||
|
maskCanvas.height = layer.height;
|
||||||
|
|
||||||
|
const maskImageData = maskCtx.createImageData(layer.width, layer.height);
|
||||||
|
const maskData = new Float32Array(layer.mask);
|
||||||
|
for (let i = 0; i < maskData.length; i++) {
|
||||||
|
maskImageData.data[i * 4] =
|
||||||
|
maskImageData.data[i * 4 + 1] =
|
||||||
|
maskImageData.data[i * 4 + 2] = 255;
|
||||||
|
maskImageData.data[i * 4 + 3] = maskData[i] * 255;
|
||||||
|
}
|
||||||
|
maskCtx.putImageData(maskImageData, 0, 0);
|
||||||
|
|
||||||
|
// 使用destination-in混合模式
|
||||||
|
ctx.globalCompositeOperation = 'destination-in';
|
||||||
|
ctx.drawImage(maskCanvas,
|
||||||
|
-layer.width/2, -layer.height/2,
|
||||||
|
layer.width, layer.height
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("Mask applied successfully");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error applying mask:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 最后绘制选择框
|
||||||
if (this.selectedLayers.includes(layer)) {
|
if (this.selectedLayers.includes(layer)) {
|
||||||
this.drawSelectionFrame(layer);
|
this.drawSelectionFrame(layer);
|
||||||
}
|
}
|
||||||
@@ -862,4 +883,60 @@ export class Canvas {
|
|||||||
};
|
};
|
||||||
newImage.src = tempCanvas.toDataURL();
|
newImage.src = tempCanvas.toDataURL();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
async getLayerImageData(layer) {
|
||||||
|
try {
|
||||||
|
const tempCanvas = document.createElement('canvas');
|
||||||
|
const tempCtx = tempCanvas.getContext('2d');
|
||||||
|
|
||||||
|
// 设置画布尺寸
|
||||||
|
tempCanvas.width = layer.width;
|
||||||
|
tempCanvas.height = layer.height;
|
||||||
|
|
||||||
|
// 清除画布
|
||||||
|
tempCtx.clearRect(0, 0, tempCanvas.width, tempCanvas.height);
|
||||||
|
|
||||||
|
// 绘制图层
|
||||||
|
tempCtx.save();
|
||||||
|
tempCtx.translate(layer.width/2, layer.height/2);
|
||||||
|
tempCtx.rotate(layer.rotation * Math.PI / 180);
|
||||||
|
tempCtx.drawImage(
|
||||||
|
layer.image,
|
||||||
|
-layer.width/2,
|
||||||
|
-layer.height/2,
|
||||||
|
layer.width,
|
||||||
|
layer.height
|
||||||
|
);
|
||||||
|
tempCtx.restore();
|
||||||
|
|
||||||
|
// 获取base64数据
|
||||||
|
const dataUrl = tempCanvas.toDataURL('image/png');
|
||||||
|
if (!dataUrl.startsWith('data:image/png;base64,')) {
|
||||||
|
throw new Error("Invalid image data format");
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataUrl;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error getting layer image data:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加带遮罩的图层
|
||||||
|
addMattedLayer(image, mask) {
|
||||||
|
const layer = {
|
||||||
|
image: image,
|
||||||
|
mask: mask,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: image.width,
|
||||||
|
height: image.height,
|
||||||
|
rotation: 0,
|
||||||
|
zIndex: this.layers.length
|
||||||
|
};
|
||||||
|
|
||||||
|
this.layers.push(layer);
|
||||||
|
this.selectedLayer = layer;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user