@@ -14,6 +14,7 @@ import { useCustomShapeDrawing } from "./canvas/useCustomShapeDrawing";
|
|||||||
import { usePencilDrawing } from "./canvas/usePencilDrawing";
|
import { usePencilDrawing } from "./canvas/usePencilDrawing";
|
||||||
import ObjectsLayer from "./canvas/ObjectsLayer";
|
import ObjectsLayer from "./canvas/ObjectsLayer";
|
||||||
import GuidesLayer from "./canvas/GuidesLayer";
|
import GuidesLayer from "./canvas/GuidesLayer";
|
||||||
|
import BlurBackdropLayer from "./canvas/BlurBackdropLayer";
|
||||||
import CustomShapeDrawingLayer from "./canvas/CustomShapeDrawingLayer";
|
import CustomShapeDrawingLayer from "./canvas/CustomShapeDrawingLayer";
|
||||||
import PencilDrawingLayer from "./canvas/PencilDrawingLayer";
|
import PencilDrawingLayer from "./canvas/PencilDrawingLayer";
|
||||||
import {
|
import {
|
||||||
@@ -627,6 +628,13 @@ const EditorCanvas = ({ catalogSize }: EditorCanvasProps) => {
|
|||||||
<CustomShapeDrawingLayer drawingState={drawingState} />
|
<CustomShapeDrawingLayer drawingState={drawingState} />
|
||||||
<PencilDrawingLayer drawingState={pencilDrawingState} />
|
<PencilDrawingLayer drawingState={pencilDrawingState} />
|
||||||
</Stage>
|
</Stage>
|
||||||
|
<BlurBackdropLayer
|
||||||
|
objects={objects}
|
||||||
|
stageWidth={stageSize.width}
|
||||||
|
stageHeight={stageSize.height}
|
||||||
|
scale={finalScale}
|
||||||
|
stageRef={stageRef}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,195 @@
|
|||||||
|
import { useEffect, useMemo, useState, type CSSProperties } from "react";
|
||||||
|
import type Konva from "konva";
|
||||||
|
import type { EditorObject } from "../../store/editorStore";
|
||||||
|
import { getColorWithOpacity } from "../../utils/colorOpacity";
|
||||||
|
import { toCssGradientAngle, type LinearGradient } from "../../utils/gradient";
|
||||||
|
|
||||||
|
type BlurBackdropLayerProps = {
|
||||||
|
objects: EditorObject[];
|
||||||
|
stageWidth: number;
|
||||||
|
stageHeight: number;
|
||||||
|
/** Stage scale applied to Konva (editor internal scale + zoom). */
|
||||||
|
scale: number;
|
||||||
|
stageRef: React.RefObject<Konva.Stage | null>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TRIANGLE_TOP_Y_PERCENT = 6.69873; // (1 - sqrt(3)/2) / 2
|
||||||
|
const TRIANGLE_BOTTOM_Y_PERCENT = 93.30127; // 100 - TRIANGLE_TOP_Y_PERCENT
|
||||||
|
|
||||||
|
const BlurBackdropLayer = ({ objects, stageWidth, stageHeight, scale, stageRef }: BlurBackdropLayerProps) => {
|
||||||
|
const [dragRevision, setDragRevision] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const stage = stageRef.current;
|
||||||
|
if (!stage) return;
|
||||||
|
|
||||||
|
let raf = 0;
|
||||||
|
const bump = () => setDragRevision((v) => v + 1);
|
||||||
|
|
||||||
|
const scheduleBump = () => {
|
||||||
|
if (raf) return;
|
||||||
|
raf = window.requestAnimationFrame(() => {
|
||||||
|
raf = 0;
|
||||||
|
bump();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDragMove = () => scheduleBump();
|
||||||
|
const onDragEnd = () => scheduleBump();
|
||||||
|
|
||||||
|
stage.on("dragmove", onDragMove);
|
||||||
|
stage.on("dragend", onDragEnd);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
stage.off("dragmove", onDragMove);
|
||||||
|
stage.off("dragend", onDragEnd);
|
||||||
|
if (raf) window.cancelAnimationFrame(raf);
|
||||||
|
};
|
||||||
|
}, [stageRef]);
|
||||||
|
|
||||||
|
const overlays = useMemo(() => {
|
||||||
|
const stage = stageRef.current;
|
||||||
|
|
||||||
|
return objects
|
||||||
|
.map((obj, index) => {
|
||||||
|
if (obj.visible === false) return null;
|
||||||
|
if (obj.isMask) return null;
|
||||||
|
if (obj.type !== "rectangle") return null;
|
||||||
|
|
||||||
|
const blur = obj.blur ?? 0;
|
||||||
|
if (blur <= 0) return null;
|
||||||
|
|
||||||
|
const blurPx = blur * scale;
|
||||||
|
const rotation = obj.rotation ?? 0;
|
||||||
|
const shapeType = obj.shapeType ?? "square";
|
||||||
|
|
||||||
|
// Konva shapes are positioned differently depending on shapeType.
|
||||||
|
let leftPx = 0;
|
||||||
|
let topPx = 0;
|
||||||
|
let widthPx = (obj.width ?? 100) * scale;
|
||||||
|
let heightPx = (obj.height ?? 100) * scale;
|
||||||
|
let borderRadiusPx = 0;
|
||||||
|
let clipPath: string | undefined;
|
||||||
|
let transformOrigin: CSSProperties["transformOrigin"] = "top left";
|
||||||
|
|
||||||
|
const node = stage?.findOne(`#${obj.id}`);
|
||||||
|
const absPos = node && "getAbsolutePosition" in node ? node.getAbsolutePosition() : null;
|
||||||
|
const nodeX = absPos?.x ?? node?.x?.() ?? obj.x ?? 0;
|
||||||
|
const nodeY = absPos?.y ?? node?.y?.() ?? obj.y ?? 0;
|
||||||
|
|
||||||
|
if (shapeType === "circle") {
|
||||||
|
const sizePx = (obj.width ?? 100) * scale;
|
||||||
|
const radiusPx = sizePx / 2;
|
||||||
|
// `getAbsolutePosition()` is already in the same pixel space as our overlay container.
|
||||||
|
// Konva.Circle uses x/y as CENTER coordinates, while our overlay uses top-left.
|
||||||
|
leftPx = nodeX - radiusPx;
|
||||||
|
topPx = nodeY - radiusPx;
|
||||||
|
widthPx = sizePx;
|
||||||
|
heightPx = sizePx;
|
||||||
|
borderRadiusPx = radiusPx;
|
||||||
|
transformOrigin = "center center";
|
||||||
|
} else if (shapeType === "triangle") {
|
||||||
|
const baseWidth = obj.width ?? 100;
|
||||||
|
const baseHeight = obj.height ?? 100;
|
||||||
|
const side = Math.min(baseWidth, baseHeight);
|
||||||
|
const radius = side / 2;
|
||||||
|
const radiusPx = radius * scale;
|
||||||
|
// In Konva TriangleShape, x/y are center coordinates.
|
||||||
|
leftPx = nodeX - radiusPx;
|
||||||
|
topPx = nodeY - radiusPx;
|
||||||
|
widthPx = side * scale;
|
||||||
|
heightPx = side * scale;
|
||||||
|
clipPath = `polygon(50% ${TRIANGLE_TOP_Y_PERCENT}%, 0% ${TRIANGLE_BOTTOM_Y_PERCENT}%, 100% ${TRIANGLE_BOTTOM_Y_PERCENT}%)`;
|
||||||
|
transformOrigin = "center center";
|
||||||
|
} else if (shapeType === "abstract") {
|
||||||
|
const rawW = obj.width ?? 100;
|
||||||
|
const rawH = obj.height ?? 100;
|
||||||
|
const width = rawW * scale;
|
||||||
|
const height = rawH * scale;
|
||||||
|
|
||||||
|
const baseRadius = Math.min(width, height) / 2;
|
||||||
|
const abstractScale = 0.85;
|
||||||
|
const radius = baseRadius * abstractScale;
|
||||||
|
|
||||||
|
// In Konva AbstractShape, x/y are center coordinates.
|
||||||
|
const centerX = nodeX;
|
||||||
|
const centerY = nodeY;
|
||||||
|
leftPx = centerX - radius;
|
||||||
|
topPx = centerY - radius;
|
||||||
|
widthPx = radius * 2;
|
||||||
|
heightPx = radius * 2;
|
||||||
|
clipPath =
|
||||||
|
"polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%)";
|
||||||
|
transformOrigin = "center center";
|
||||||
|
} else {
|
||||||
|
// rectangle / square (Konva Rect uses x/y as top-left).
|
||||||
|
leftPx = nodeX;
|
||||||
|
topPx = nodeY;
|
||||||
|
widthPx = (obj.width ?? 100) * scale;
|
||||||
|
heightPx = (obj.height ?? 100) * scale;
|
||||||
|
borderRadiusPx = Math.max(0, obj.borderRadius ?? 0) * scale;
|
||||||
|
transformOrigin = "0% 0%";
|
||||||
|
}
|
||||||
|
|
||||||
|
const objOpacity = obj.opacity ?? 100; // 0-100 (editor convention)
|
||||||
|
// Make blur tint translucent so the backdrop blur stays visible.
|
||||||
|
const BLUR_TINT_OPACITY_FACTOR = 0.25;
|
||||||
|
const tintOpacity = Math.max(0, Math.min(100, objOpacity * BLUR_TINT_OPACITY_FACTOR));
|
||||||
|
const defaultFill = "#3b82f6";
|
||||||
|
|
||||||
|
const overlayBackground: CSSProperties =
|
||||||
|
obj.fillType === "gradient" && obj.gradient
|
||||||
|
? (() => {
|
||||||
|
const g = obj.gradient as LinearGradient;
|
||||||
|
const angle = toCssGradientAngle(g.angle);
|
||||||
|
const from = getColorWithOpacity(g.from, tintOpacity);
|
||||||
|
const to = getColorWithOpacity(g.to, tintOpacity);
|
||||||
|
return {
|
||||||
|
backgroundImage: `linear-gradient(${angle}deg, ${from} 0%, ${to} 100%)`,
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
};
|
||||||
|
})()
|
||||||
|
: {
|
||||||
|
backgroundColor: getColorWithOpacity(obj.fill ?? defaultFill, tintOpacity),
|
||||||
|
};
|
||||||
|
|
||||||
|
const style: CSSProperties = {
|
||||||
|
position: "absolute",
|
||||||
|
left: `${leftPx}px`,
|
||||||
|
top: `${topPx}px`,
|
||||||
|
width: `${widthPx}px`,
|
||||||
|
height: `${heightPx}px`,
|
||||||
|
zIndex: index,
|
||||||
|
pointerEvents: "none",
|
||||||
|
backdropFilter: `blur(${blurPx}px)`,
|
||||||
|
WebkitBackdropFilter: `blur(${blurPx}px)`,
|
||||||
|
...overlayBackground,
|
||||||
|
overflow: clipPath ? "hidden" : borderRadiusPx > 0 ? "hidden" : undefined,
|
||||||
|
borderRadius: borderRadiusPx > 0 ? `${borderRadiusPx}px` : undefined,
|
||||||
|
clipPath,
|
||||||
|
transform: rotation ? `rotate(${rotation}deg)` : undefined,
|
||||||
|
transformOrigin,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <div key={`blur-backdrop-${obj.id}`} style={style} />;
|
||||||
|
})
|
||||||
|
;
|
||||||
|
}, [objects, scale, dragRevision, stageRef]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
inset: 0,
|
||||||
|
width: stageWidth * scale,
|
||||||
|
height: stageHeight * scale,
|
||||||
|
pointerEvents: "none",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{overlays}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BlurBackdropLayer;
|
||||||
|
|
||||||
@@ -3,7 +3,8 @@ import { Star } from "react-konva";
|
|||||||
import Konva from "konva";
|
import Konva from "konva";
|
||||||
import type { ShapeProps } from "./types";
|
import type { ShapeProps } from "./types";
|
||||||
import { getKonvaGradientProps } from "../../utils/gradient";
|
import { getKonvaGradientProps } from "../../utils/gradient";
|
||||||
import { getKonvaBlurProps, useShapeBlurCache } from "../../utils/shapeBlur";
|
import { getColorWithOpacity } from "../../utils/colorOpacity";
|
||||||
|
import { useShapeBlurCache } from "../../utils/shapeBlur";
|
||||||
|
|
||||||
const AbstractShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProps) => {
|
const AbstractShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProps) => {
|
||||||
const shapeRef = useRef<Konva.Star>(null);
|
const shapeRef = useRef<Konva.Star>(null);
|
||||||
@@ -19,9 +20,13 @@ const AbstractShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shape
|
|||||||
|
|
||||||
const actualStrokeWidth = obj.strokeWidth ?? 0;
|
const actualStrokeWidth = obj.strokeWidth ?? 0;
|
||||||
const hasStroke = actualStrokeWidth > 0;
|
const hasStroke = actualStrokeWidth > 0;
|
||||||
const blurProps = isMask ? {} : getKonvaBlurProps(obj.blur);
|
const blurRadius = obj.blur ?? 0;
|
||||||
|
const blurHitFill = blurRadius > 0 ? getColorWithOpacity(obj.fill ?? "#000000", 0.5) : undefined;
|
||||||
|
// Editor blur is implemented via backdrop-filter (see BlurBackdropLayer).
|
||||||
|
// Avoid applying Konva blur filters on the element itself.
|
||||||
|
const blurProps = {};
|
||||||
|
|
||||||
useShapeBlurCache(shapeRef, isMask ? 0 : obj.blur, [
|
useShapeBlurCache(shapeRef, 0, [
|
||||||
obj.width,
|
obj.width,
|
||||||
obj.height,
|
obj.height,
|
||||||
obj.fill,
|
obj.fill,
|
||||||
@@ -29,7 +34,6 @@ const AbstractShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shape
|
|||||||
obj.gradient,
|
obj.gradient,
|
||||||
obj.stroke,
|
obj.stroke,
|
||||||
actualStrokeWidth,
|
actualStrokeWidth,
|
||||||
obj.blur,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// برای نمایش انتخاب: اگر strokeWidth واقعی > 0 است، از آن استفاده کن، وگرنه stroke را برای انتخاب نمایش بده
|
// برای نمایش انتخاب: اگر strokeWidth واقعی > 0 است، از آن استفاده کن، وگرنه stroke را برای انتخاب نمایش بده
|
||||||
@@ -44,14 +48,14 @@ const AbstractShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shape
|
|||||||
const displayStrokeWidth = isSelected
|
const displayStrokeWidth = isSelected
|
||||||
? (hasStroke ? actualStrokeWidth : 3)
|
? (hasStroke ? actualStrokeWidth : 3)
|
||||||
: (isMask && showGuide ? 2 : actualStrokeWidth);
|
: (isMask && showGuide ? 2 : actualStrokeWidth);
|
||||||
const gradientProps = isMask
|
const gradientProps = isMask || blurRadius > 0
|
||||||
? {}
|
? {}
|
||||||
: getKonvaGradientProps(
|
: getKonvaGradientProps(
|
||||||
obj.fillType,
|
obj.fillType,
|
||||||
obj.gradient,
|
obj.gradient,
|
||||||
obj.width || 100,
|
obj.width || 100,
|
||||||
obj.height || 100,
|
obj.height || 100,
|
||||||
"centered",
|
"centered",
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -64,7 +68,8 @@ const AbstractShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shape
|
|||||||
numPoints={5}
|
numPoints={5}
|
||||||
innerRadius={innerRadius}
|
innerRadius={innerRadius}
|
||||||
outerRadius={radius}
|
outerRadius={radius}
|
||||||
fill={isMask ? "transparent" : obj.fill}
|
// When blur is enabled, tint is rendered by BlurBackdropLayer.
|
||||||
|
fill={isMask ? "transparent" : blurRadius > 0 ? blurHitFill : (obj.fill ?? "#000000")}
|
||||||
{...gradientProps}
|
{...gradientProps}
|
||||||
stroke={displayStroke}
|
stroke={displayStroke}
|
||||||
strokeWidth={displayStrokeWidth}
|
strokeWidth={displayStrokeWidth}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import { Circle } from "react-konva";
|
|||||||
import Konva from "konva";
|
import Konva from "konva";
|
||||||
import type { ShapeProps } from "./types";
|
import type { ShapeProps } from "./types";
|
||||||
import { getKonvaGradientProps } from "../../utils/gradient";
|
import { getKonvaGradientProps } from "../../utils/gradient";
|
||||||
import { getKonvaBlurProps, useShapeBlurCache } from "../../utils/shapeBlur";
|
import { getColorWithOpacity } from "../../utils/colorOpacity";
|
||||||
|
import { useShapeBlurCache } from "../../utils/shapeBlur";
|
||||||
|
|
||||||
const CircleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProps) => {
|
const CircleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProps) => {
|
||||||
const shapeRef = useRef<Konva.Circle>(null);
|
const shapeRef = useRef<Konva.Circle>(null);
|
||||||
@@ -15,16 +16,19 @@ const CircleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapePr
|
|||||||
|
|
||||||
const actualStrokeWidth = obj.strokeWidth ?? 0;
|
const actualStrokeWidth = obj.strokeWidth ?? 0;
|
||||||
const hasStroke = actualStrokeWidth > 0;
|
const hasStroke = actualStrokeWidth > 0;
|
||||||
const blurProps = isMask ? {} : getKonvaBlurProps(obj.blur);
|
const blurRadius = obj.blur ?? 0;
|
||||||
|
const blurHitFill = blurRadius > 0 ? getColorWithOpacity(obj.fill ?? "#000000", 0.5) : undefined;
|
||||||
|
// Editor blur is implemented via backdrop-filter (see BlurBackdropLayer).
|
||||||
|
// Avoid applying Konva blur filters on the element itself.
|
||||||
|
const blurProps = {};
|
||||||
|
|
||||||
useShapeBlurCache(shapeRef, isMask ? 0 : obj.blur, [
|
useShapeBlurCache(shapeRef, 0, [
|
||||||
obj.width,
|
obj.width,
|
||||||
obj.fill,
|
obj.fill,
|
||||||
obj.fillType,
|
obj.fillType,
|
||||||
obj.gradient,
|
obj.gradient,
|
||||||
obj.stroke,
|
obj.stroke,
|
||||||
actualStrokeWidth,
|
actualStrokeWidth,
|
||||||
obj.blur,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// برای نمایش انتخاب: اگر strokeWidth واقعی > 0 است، از آن استفاده کن، وگرنه stroke را برای انتخاب نمایش بده
|
// برای نمایش انتخاب: اگر strokeWidth واقعی > 0 است، از آن استفاده کن، وگرنه stroke را برای انتخاب نمایش بده
|
||||||
@@ -40,9 +44,9 @@ const CircleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapePr
|
|||||||
? (hasStroke ? actualStrokeWidth : 3)
|
? (hasStroke ? actualStrokeWidth : 3)
|
||||||
: (isMask && showGuide ? 2 : actualStrokeWidth);
|
: (isMask && showGuide ? 2 : actualStrokeWidth);
|
||||||
const diameter = obj.width || 100;
|
const diameter = obj.width || 100;
|
||||||
const gradientProps = isMask
|
const gradientProps = isMask || blurRadius > 0
|
||||||
? {}
|
? {}
|
||||||
: getKonvaGradientProps(obj.fillType, obj.gradient, diameter, diameter, "centered");
|
: getKonvaGradientProps(obj.fillType, obj.gradient, diameter, diameter, "centered");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Circle
|
<Circle
|
||||||
@@ -52,7 +56,8 @@ const CircleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapePr
|
|||||||
x={obj.x}
|
x={obj.x}
|
||||||
y={obj.y}
|
y={obj.y}
|
||||||
radius={(obj.width || 50) / 2}
|
radius={(obj.width || 50) / 2}
|
||||||
fill={isMask ? "transparent" : obj.fill}
|
// When blur is enabled, tint is rendered by BlurBackdropLayer.
|
||||||
|
fill={isMask ? "transparent" : blurRadius > 0 ? blurHitFill : (obj.fill ?? "#000000")}
|
||||||
{...gradientProps}
|
{...gradientProps}
|
||||||
stroke={displayStroke}
|
stroke={displayStroke}
|
||||||
strokeWidth={displayStrokeWidth}
|
strokeWidth={displayStrokeWidth}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import { Rect } from "react-konva";
|
|||||||
import Konva from "konva";
|
import Konva from "konva";
|
||||||
import type { ShapeProps } from "./types";
|
import type { ShapeProps } from "./types";
|
||||||
import { getKonvaGradientProps } from "../../utils/gradient";
|
import { getKonvaGradientProps } from "../../utils/gradient";
|
||||||
import { getKonvaBlurProps, useShapeBlurCache } from "../../utils/shapeBlur";
|
import { getColorWithOpacity } from "../../utils/colorOpacity";
|
||||||
|
import { useShapeBlurCache } from "../../utils/shapeBlur";
|
||||||
|
|
||||||
const RectangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProps) => {
|
const RectangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProps) => {
|
||||||
const shapeRef = useRef<Konva.Rect>(null);
|
const shapeRef = useRef<Konva.Rect>(null);
|
||||||
@@ -16,9 +17,13 @@ const RectangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shap
|
|||||||
const actualStrokeWidth = obj.strokeWidth ?? 0;
|
const actualStrokeWidth = obj.strokeWidth ?? 0;
|
||||||
const hasStroke = actualStrokeWidth > 0;
|
const hasStroke = actualStrokeWidth > 0;
|
||||||
const cornerRadius = Math.max(0, obj.borderRadius ?? 0);
|
const cornerRadius = Math.max(0, obj.borderRadius ?? 0);
|
||||||
const blurProps = isMask ? {} : getKonvaBlurProps(obj.blur);
|
const blurRadius = obj.blur ?? 0;
|
||||||
|
const blurHitFill = blurRadius > 0 ? getColorWithOpacity(obj.fill ?? "#000000", 0.5) : undefined;
|
||||||
|
// Editor blur is implemented via backdrop-filter (see BlurBackdropLayer),
|
||||||
|
// so we intentionally do not apply Konva blur filters here.
|
||||||
|
const blurProps = {};
|
||||||
|
|
||||||
useShapeBlurCache(shapeRef, isMask ? 0 : obj.blur, [
|
useShapeBlurCache(shapeRef, 0, [
|
||||||
obj.width,
|
obj.width,
|
||||||
obj.height,
|
obj.height,
|
||||||
cornerRadius,
|
cornerRadius,
|
||||||
@@ -27,7 +32,6 @@ const RectangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shap
|
|||||||
obj.gradient,
|
obj.gradient,
|
||||||
obj.stroke,
|
obj.stroke,
|
||||||
actualStrokeWidth,
|
actualStrokeWidth,
|
||||||
obj.blur,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// برای نمایش انتخاب: اگر strokeWidth واقعی > 0 است، از آن استفاده کن، وگرنه stroke را برای انتخاب نمایش بده
|
// برای نمایش انتخاب: اگر strokeWidth واقعی > 0 است، از آن استفاده کن، وگرنه stroke را برای انتخاب نمایش بده
|
||||||
@@ -42,7 +46,7 @@ const RectangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shap
|
|||||||
const displayStrokeWidth = isSelected
|
const displayStrokeWidth = isSelected
|
||||||
? (hasStroke ? actualStrokeWidth : 3)
|
? (hasStroke ? actualStrokeWidth : 3)
|
||||||
: (isMask && showGuide ? 2 : actualStrokeWidth);
|
: (isMask && showGuide ? 2 : actualStrokeWidth);
|
||||||
const gradientProps = isMask
|
const gradientProps = isMask || blurRadius > 0
|
||||||
? {}
|
? {}
|
||||||
: getKonvaGradientProps(
|
: getKonvaGradientProps(
|
||||||
obj.fillType,
|
obj.fillType,
|
||||||
@@ -62,7 +66,9 @@ const RectangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shap
|
|||||||
width={obj.width || 100}
|
width={obj.width || 100}
|
||||||
height={obj.height || 100}
|
height={obj.height || 100}
|
||||||
cornerRadius={cornerRadius}
|
cornerRadius={cornerRadius}
|
||||||
fill={isMask ? "transparent" : obj.fill}
|
// When blur is enabled, visual tint is rendered by BlurBackdropLayer.
|
||||||
|
// Use a tiny non-zero alpha so Konva hit-detection still works for dragging.
|
||||||
|
fill={isMask ? "transparent" : blurRadius > 0 ? blurHitFill : (obj.fill ?? "#000000")}
|
||||||
{...gradientProps}
|
{...gradientProps}
|
||||||
stroke={displayStroke}
|
stroke={displayStroke}
|
||||||
strokeWidth={displayStrokeWidth}
|
strokeWidth={displayStrokeWidth}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import { RegularPolygon } from "react-konva";
|
|||||||
import Konva from "konva";
|
import Konva from "konva";
|
||||||
import type { ShapeProps } from "./types";
|
import type { ShapeProps } from "./types";
|
||||||
import { getKonvaGradientProps } from "../../utils/gradient";
|
import { getKonvaGradientProps } from "../../utils/gradient";
|
||||||
import { getKonvaBlurProps, useShapeBlurCache } from "../../utils/shapeBlur";
|
import { getColorWithOpacity } from "../../utils/colorOpacity";
|
||||||
|
import { useShapeBlurCache } from "../../utils/shapeBlur";
|
||||||
|
|
||||||
const TriangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProps) => {
|
const TriangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProps) => {
|
||||||
const shapeRef = useRef<Konva.RegularPolygon>(null);
|
const shapeRef = useRef<Konva.RegularPolygon>(null);
|
||||||
@@ -16,17 +17,20 @@ const TriangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shape
|
|||||||
|
|
||||||
const actualStrokeWidth = obj.strokeWidth ?? 0;
|
const actualStrokeWidth = obj.strokeWidth ?? 0;
|
||||||
const hasStroke = actualStrokeWidth > 0;
|
const hasStroke = actualStrokeWidth > 0;
|
||||||
const blurProps = isMask ? {} : getKonvaBlurProps(obj.blur);
|
const blurRadius = obj.blur ?? 0;
|
||||||
|
const blurHitFill = blurRadius > 0 ? getColorWithOpacity(obj.fill ?? "#000000", 0.5) : undefined;
|
||||||
|
// Editor blur is implemented via backdrop-filter (see BlurBackdropLayer).
|
||||||
|
// Avoid applying Konva blur filters on the element itself.
|
||||||
|
const blurProps = {};
|
||||||
|
|
||||||
useShapeBlurCache(shapeRef, isMask ? 0 : obj.blur, [
|
useShapeBlurCache(shapeRef, 0, [
|
||||||
obj.width,
|
obj.width,
|
||||||
obj.height,
|
|
||||||
obj.fill,
|
obj.fill,
|
||||||
obj.fillType,
|
obj.fillType,
|
||||||
obj.gradient,
|
obj.gradient,
|
||||||
obj.stroke,
|
obj.stroke,
|
||||||
actualStrokeWidth,
|
actualStrokeWidth,
|
||||||
obj.blur,
|
obj.height,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// برای نمایش انتخاب: اگر strokeWidth واقعی > 0 است، از آن استفاده کن، وگرنه stroke را برای انتخاب نمایش بده
|
// برای نمایش انتخاب: اگر strokeWidth واقعی > 0 است، از آن استفاده کن، وگرنه stroke را برای انتخاب نمایش بده
|
||||||
@@ -41,14 +45,14 @@ const TriangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shape
|
|||||||
const displayStrokeWidth = isSelected
|
const displayStrokeWidth = isSelected
|
||||||
? (hasStroke ? actualStrokeWidth : 3)
|
? (hasStroke ? actualStrokeWidth : 3)
|
||||||
: (isMask && showGuide ? 2 : actualStrokeWidth);
|
: (isMask && showGuide ? 2 : actualStrokeWidth);
|
||||||
const gradientProps = isMask
|
const gradientProps = isMask || blurRadius > 0
|
||||||
? {}
|
? {}
|
||||||
: getKonvaGradientProps(
|
: getKonvaGradientProps(
|
||||||
obj.fillType,
|
obj.fillType,
|
||||||
obj.gradient,
|
obj.gradient,
|
||||||
obj.width || 100,
|
obj.width || 100,
|
||||||
obj.height || 100,
|
obj.height || 100,
|
||||||
"centered",
|
"centered",
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -60,7 +64,8 @@ const TriangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shape
|
|||||||
y={obj.y + (obj.height || 100) / 2}
|
y={obj.y + (obj.height || 100) / 2}
|
||||||
sides={3}
|
sides={3}
|
||||||
radius={radius}
|
radius={radius}
|
||||||
fill={isMask ? "transparent" : obj.fill}
|
// When blur is enabled, tint is rendered by BlurBackdropLayer.
|
||||||
|
fill={isMask ? "transparent" : blurRadius > 0 ? blurHitFill : (obj.fill ?? "#000000")}
|
||||||
{...gradientProps}
|
{...gradientProps}
|
||||||
stroke={displayStroke}
|
stroke={displayStroke}
|
||||||
strokeWidth={displayStrokeWidth}
|
strokeWidth={displayStrokeWidth}
|
||||||
|
|||||||
Reference in New Issue
Block a user