mirror of
https://github.com/YuzuZensai/Pien-Studio.git
synced 2026-09-02 14:18:35 +00:00
♻️ refactor!: massive refactor
This commit is contained in:
@@ -13,8 +13,17 @@ import { getToolUiDefinition } from "../lib/tools/registry";
|
||||
import { getToolDefinition } from "@pien-studio/editor-core";
|
||||
import { useCanvasInteractions } from "../hooks/use-canvas-interactions";
|
||||
import { useTranslations } from "../hooks/use-translations";
|
||||
import { createStroke, paintSegment, type BrushStroke, type BrushOptions } from "../lib/brush-painter";
|
||||
import type { Layer, LayerEffect } from "@pien-studio/types";
|
||||
import {
|
||||
createStroke,
|
||||
paintSegment,
|
||||
type BrushStroke,
|
||||
type BrushOptions,
|
||||
} from "../lib/brush-painter";
|
||||
import {
|
||||
getLayerRuntimeSource,
|
||||
type Layer,
|
||||
type LayerEffect,
|
||||
} from "@pien-studio/types";
|
||||
|
||||
interface CanvasRendererProps {
|
||||
layers: Layer[];
|
||||
@@ -50,7 +59,15 @@ interface CanvasRendererProps {
|
||||
} | null;
|
||||
}
|
||||
|
||||
function BrushOverlayCanvas({ stroke, width, height }: { stroke: HTMLCanvasElement; width: number; height: number }) {
|
||||
function BrushOverlayCanvas({
|
||||
stroke,
|
||||
width,
|
||||
height,
|
||||
}: {
|
||||
stroke: HTMLCanvasElement;
|
||||
width: number;
|
||||
height: number;
|
||||
}) {
|
||||
const canvasRef = React.useRef<HTMLCanvasElement | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
@@ -87,6 +104,7 @@ function EffectImageLayer({
|
||||
const imageRef = React.useRef<HTMLImageElement | null>(null);
|
||||
|
||||
const activeEffects = effectsOverride ?? layer.effects;
|
||||
const sourceUri = getLayerRuntimeSource(layer);
|
||||
|
||||
const draw = React.useCallback(() => {
|
||||
const canvas = canvasRef.current;
|
||||
@@ -95,11 +113,17 @@ function EffectImageLayer({
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
renderLayerWithEffects(ctx, image, activeEffects, canvas.width, canvas.height);
|
||||
renderLayerWithEffects(
|
||||
ctx,
|
||||
image,
|
||||
activeEffects,
|
||||
canvas.width,
|
||||
canvas.height,
|
||||
);
|
||||
}, [activeEffects]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!layer.sourceUri) return;
|
||||
if (!sourceUri) return;
|
||||
let canceled = false;
|
||||
const image = new Image();
|
||||
image.crossOrigin = "anonymous";
|
||||
@@ -108,11 +132,11 @@ function EffectImageLayer({
|
||||
imageRef.current = image;
|
||||
draw();
|
||||
};
|
||||
image.src = layer.sourceUri;
|
||||
image.src = sourceUri;
|
||||
return () => {
|
||||
canceled = true;
|
||||
};
|
||||
}, [draw, layer.sourceUri]);
|
||||
}, [draw, sourceUri]);
|
||||
|
||||
React.useEffect(() => {
|
||||
draw();
|
||||
@@ -156,32 +180,51 @@ export function CanvasRenderer({
|
||||
|
||||
const brushStrokeRef = React.useRef<BrushStroke | null>(null);
|
||||
const brushLastPosRef = React.useRef<{ x: number; y: number } | null>(null);
|
||||
const [brushOverlay, setBrushOverlay] = React.useState<{ layerId: string; canvas: HTMLCanvasElement } | null>(null);
|
||||
const [brushOverlay, setBrushOverlay] = React.useState<{
|
||||
layerId: string;
|
||||
canvas: HTMLCanvasElement;
|
||||
} | null>(null);
|
||||
|
||||
const handleBrushStrokeStart = React.useCallback((layerId: string, x: number, y: number, layerWidth: number, layerHeight: number): void => {
|
||||
if (!brushOptions) return;
|
||||
const stroke = createStroke(layerWidth, layerHeight);
|
||||
brushStrokeRef.current = stroke;
|
||||
brushLastPosRef.current = { x, y };
|
||||
paintSegment(stroke, x, y, x, y, brushOptions);
|
||||
setBrushOverlay({ layerId, canvas: stroke.canvas });
|
||||
}, [brushOptions]);
|
||||
const handleBrushStrokeStart = React.useCallback(
|
||||
(
|
||||
layerId: string,
|
||||
x: number,
|
||||
y: number,
|
||||
layerWidth: number,
|
||||
layerHeight: number,
|
||||
): void => {
|
||||
if (!brushOptions) return;
|
||||
const stroke = createStroke(layerWidth, layerHeight);
|
||||
brushStrokeRef.current = stroke;
|
||||
brushLastPosRef.current = { x, y };
|
||||
paintSegment(stroke, x, y, x, y, brushOptions);
|
||||
setBrushOverlay({ layerId, canvas: stroke.canvas });
|
||||
},
|
||||
[brushOptions],
|
||||
);
|
||||
|
||||
const handleBrushStrokeMove = React.useCallback((_layerId: string, x: number, y: number) => {
|
||||
if (!brushStrokeRef.current || !brushLastPosRef.current || !brushOptions) return;
|
||||
const { x: lx, y: ly } = brushLastPosRef.current;
|
||||
paintSegment(brushStrokeRef.current, lx, ly, x, y, brushOptions);
|
||||
brushLastPosRef.current = { x, y };
|
||||
setBrushOverlay((prev) => prev ? { ...prev } : prev);
|
||||
}, [brushOptions]);
|
||||
const handleBrushStrokeMove = React.useCallback(
|
||||
(_layerId: string, x: number, y: number) => {
|
||||
if (!brushStrokeRef.current || !brushLastPosRef.current || !brushOptions)
|
||||
return;
|
||||
const { x: lx, y: ly } = brushLastPosRef.current;
|
||||
paintSegment(brushStrokeRef.current, lx, ly, x, y, brushOptions);
|
||||
brushLastPosRef.current = { x, y };
|
||||
setBrushOverlay((prev) => (prev ? { ...prev } : prev));
|
||||
},
|
||||
[brushOptions],
|
||||
);
|
||||
|
||||
const handleBrushStrokeEnd = React.useCallback((layerId: string) => {
|
||||
const stroke = brushStrokeRef.current;
|
||||
brushStrokeRef.current = null;
|
||||
brushLastPosRef.current = null;
|
||||
setBrushOverlay(null);
|
||||
if (stroke && onBrushCommit) onBrushCommit(layerId, stroke);
|
||||
}, [onBrushCommit]);
|
||||
const handleBrushStrokeEnd = React.useCallback(
|
||||
(layerId: string) => {
|
||||
const stroke = brushStrokeRef.current;
|
||||
brushStrokeRef.current = null;
|
||||
brushLastPosRef.current = null;
|
||||
setBrushOverlay(null);
|
||||
if (stroke && onBrushCommit) onBrushCommit(layerId, stroke);
|
||||
},
|
||||
[onBrushCommit],
|
||||
);
|
||||
|
||||
const {
|
||||
containerRef,
|
||||
@@ -226,13 +269,7 @@ export function CanvasRenderer({
|
||||
faceDetections,
|
||||
viewport,
|
||||
);
|
||||
}, [
|
||||
faceDetections,
|
||||
faceOverlayLayerId,
|
||||
layers,
|
||||
tool,
|
||||
viewport,
|
||||
]);
|
||||
}, [faceDetections, faceOverlayLayerId, layers, tool, viewport]);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -243,10 +280,16 @@ export function CanvasRenderer({
|
||||
height: "100%",
|
||||
touchAction: "none",
|
||||
userSelect: "none",
|
||||
cursor: isSpacePan ? "grab" : (getToolUiDefinition(tool)?.cursor ?? "default"),
|
||||
cursor: isSpacePan
|
||||
? "grab"
|
||||
: (getToolUiDefinition(tool)?.cursor ?? "default"),
|
||||
}}
|
||||
onPointerDown={(e) => {
|
||||
if (getToolDefinition(tool)?.interactionMode === "select" && e.button === 0) onSelectLayer(null);
|
||||
if (
|
||||
getToolDefinition(tool)?.interactionMode === "select" &&
|
||||
e.button === 0
|
||||
)
|
||||
onSelectLayer(null);
|
||||
onContainerPointerDown(e);
|
||||
}}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
@@ -282,12 +325,9 @@ export function CanvasRenderer({
|
||||
{layers.map((layer, idx) => {
|
||||
const isSelected = layer.id === selectedLayerId;
|
||||
const isImage = layer.type === "raster";
|
||||
const layerWidth =
|
||||
layer.width ??
|
||||
(isImage ? Math.round(200 * layer.scale) : undefined);
|
||||
const layerHeight =
|
||||
layer.height ??
|
||||
(isImage ? Math.round(150 * layer.scale) : undefined);
|
||||
const sourceUri = getLayerRuntimeSource(layer);
|
||||
const layerWidth = layer.width;
|
||||
const layerHeight = layer.height;
|
||||
const handleSize = CANVAS_HANDLE_BASE_SIZE / viewport.scale;
|
||||
const handleSizePx = `${handleSize}px`;
|
||||
const largeHandleSize =
|
||||
@@ -319,8 +359,9 @@ export function CanvasRenderer({
|
||||
onPointerDown={(e) => onLayerPointerDown(e, layer)}
|
||||
onClick={() => onSelectLayer(layer.id)}
|
||||
>
|
||||
{isImage && layer.sourceUri ? (
|
||||
layer.effects.length > 0 || (faceBlurPreview && faceBlurPreview.layerId === layer.id) ? (
|
||||
{isImage && sourceUri ? (
|
||||
layer.effects.length > 0 ||
|
||||
(faceBlurPreview && faceBlurPreview.layerId === layer.id) ? (
|
||||
<EffectImageLayer
|
||||
layer={layer}
|
||||
width={layerWidth ?? 1}
|
||||
@@ -333,7 +374,7 @@ export function CanvasRenderer({
|
||||
/>
|
||||
) : (
|
||||
<NextImage
|
||||
src={layer.sourceUri}
|
||||
src={sourceUri}
|
||||
alt={layer.name ?? t("editor.layer")}
|
||||
width={layerWidth ?? 1}
|
||||
height={layerHeight ?? 1}
|
||||
@@ -356,7 +397,11 @@ export function CanvasRenderer({
|
||||
</div>
|
||||
)}
|
||||
{brushOverlay && brushOverlay.layerId === layer.id ? (
|
||||
<BrushOverlayCanvas stroke={brushOverlay.canvas} width={layerWidth ?? 1} height={layerHeight ?? 1} />
|
||||
<BrushOverlayCanvas
|
||||
stroke={brushOverlay.canvas}
|
||||
width={layerWidth ?? 1}
|
||||
height={layerHeight ?? 1}
|
||||
/>
|
||||
) : null}
|
||||
{tool === "face" && faceOverlayLayerId === layer.id
|
||||
? faceDetections.map((face, index) => (
|
||||
|
||||
Reference in New Issue
Block a user