blue
deploy to danak / build_and_deploy (push) Has been cancelled

This commit is contained in:
hamid zarghami
2026-07-04 14:51:24 +03:30
parent a448ff10de
commit 46926c66e7
10 changed files with 141 additions and 1 deletions
@@ -88,7 +88,7 @@ const ObjectRenderer = ({
groupNode?.clearCache();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [shouldApplyMask, isSelected, maskRelXDep, maskRelYDep, maskShape?.width, maskShape?.height, maskShape?.rotation, obj.width, obj.height, obj.rotation, obj.maskInvert, obj.strokeWidth, obj.stroke, obj.fill, obj.fillType, obj.gradient]);
}, [shouldApplyMask, isSelected, maskRelXDep, maskRelYDep, maskShape?.width, maskShape?.height, maskShape?.rotation, obj.width, obj.height, obj.rotation, obj.maskInvert, obj.strokeWidth, obj.stroke, obj.fill, obj.fillType, obj.gradient, obj.blur]);
// Refresh cache after transformer is attached (when isSelected changes)
useEffect(() => {
@@ -20,6 +20,7 @@ const ShapeSettings = ({ selectedObject, onUpdate }: ShapeSettingsProps) => {
const baseStroke = selectedObject.stroke ?? "#1e40af";
const baseStrokeWidth = selectedObject.strokeWidth ?? 0;
const baseBorderRadius = selectedObject.borderRadius ?? 0;
const baseBlur = selectedObject.blur ?? 0;
const isSquareShape =
selectedObject.shapeType === "square" || selectedObject.shapeType === undefined;
@@ -179,6 +180,35 @@ const ShapeSettings = ({ selectedObject, onUpdate }: ShapeSettingsProps) => {
min={0}
/>
)}
<div className="space-y-1">
<label className="text-sm">بلور</label>
<div className="flex items-center gap-2">
<input
type="range"
min={0}
max={50}
value={baseBlur}
onChange={(e) =>
onUpdate(selectedObject.id, {
blur: Math.max(0, Math.min(50, Number(e.target.value))),
})
}
className="w-full"
/>
<input
type="number"
min={0}
max={50}
value={baseBlur}
onChange={(e) =>
onUpdate(selectedObject.id, {
blur: Math.max(0, Math.min(50, Number(e.target.value) || 0)),
})
}
className="w-16 h-9 rounded-lg border border-border px-2 text-xs"
/>
</div>
</div>
</div>
);
};
@@ -3,6 +3,7 @@ import { Star } from "react-konva";
import Konva from "konva";
import type { ShapeProps } from "./types";
import { getKonvaGradientProps } from "../../utils/gradient";
import { getKonvaBlurProps, useShapeBlurCache } from "../../utils/shapeBlur";
const AbstractShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProps) => {
const shapeRef = useRef<Konva.Star>(null);
@@ -18,6 +19,18 @@ const AbstractShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shape
const actualStrokeWidth = obj.strokeWidth ?? 0;
const hasStroke = actualStrokeWidth > 0;
const blurProps = isMask ? {} : getKonvaBlurProps(obj.blur);
useShapeBlurCache(shapeRef, isMask ? 0 : obj.blur, [
obj.width,
obj.height,
obj.fill,
obj.fillType,
obj.gradient,
obj.stroke,
actualStrokeWidth,
obj.blur,
]);
// برای نمایش انتخاب: اگر strokeWidth واقعی > 0 است، از آن استفاده کن، وگرنه stroke را برای انتخاب نمایش بده
const displayStroke = isSelected
@@ -57,6 +70,7 @@ const AbstractShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shape
strokeWidth={displayStrokeWidth}
dash={isMask && showGuide ? [10, 5] : undefined}
rotation={obj.rotation || 0}
{...blurProps}
draggable={draggable}
onClick={(e) => {
if (shapeRef.current) {
@@ -3,6 +3,7 @@ import { Circle } from "react-konva";
import Konva from "konva";
import type { ShapeProps } from "./types";
import { getKonvaGradientProps } from "../../utils/gradient";
import { getKonvaBlurProps, useShapeBlurCache } from "../../utils/shapeBlur";
const CircleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProps) => {
const shapeRef = useRef<Konva.Circle>(null);
@@ -14,6 +15,17 @@ const CircleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapePr
const actualStrokeWidth = obj.strokeWidth ?? 0;
const hasStroke = actualStrokeWidth > 0;
const blurProps = isMask ? {} : getKonvaBlurProps(obj.blur);
useShapeBlurCache(shapeRef, isMask ? 0 : obj.blur, [
obj.width,
obj.fill,
obj.fillType,
obj.gradient,
obj.stroke,
actualStrokeWidth,
obj.blur,
]);
// برای نمایش انتخاب: اگر strokeWidth واقعی > 0 است، از آن استفاده کن، وگرنه stroke را برای انتخاب نمایش بده
const displayStroke = isSelected
@@ -46,6 +58,7 @@ const CircleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapePr
strokeWidth={displayStrokeWidth}
dash={isMask && showGuide ? [10, 5] : undefined}
rotation={obj.rotation || 0}
{...blurProps}
draggable={draggable}
onClick={(e) => {
if (shapeRef.current) {
@@ -3,6 +3,7 @@ import { Rect } from "react-konva";
import Konva from "konva";
import type { ShapeProps } from "./types";
import { getKonvaGradientProps } from "../../utils/gradient";
import { getKonvaBlurProps, useShapeBlurCache } from "../../utils/shapeBlur";
const RectangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProps) => {
const shapeRef = useRef<Konva.Rect>(null);
@@ -15,6 +16,19 @@ const RectangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shap
const actualStrokeWidth = obj.strokeWidth ?? 0;
const hasStroke = actualStrokeWidth > 0;
const cornerRadius = Math.max(0, obj.borderRadius ?? 0);
const blurProps = isMask ? {} : getKonvaBlurProps(obj.blur);
useShapeBlurCache(shapeRef, isMask ? 0 : obj.blur, [
obj.width,
obj.height,
cornerRadius,
obj.fill,
obj.fillType,
obj.gradient,
obj.stroke,
actualStrokeWidth,
obj.blur,
]);
// برای نمایش انتخاب: اگر strokeWidth واقعی > 0 است، از آن استفاده کن، وگرنه stroke را برای انتخاب نمایش بده
const displayStroke = isSelected
@@ -54,6 +68,7 @@ const RectangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shap
strokeWidth={displayStrokeWidth}
dash={isMask && showGuide ? [10, 5] : undefined}
rotation={obj.rotation || 0}
{...blurProps}
draggable={draggable}
onClick={(e) => {
if (shapeRef.current) {
@@ -3,6 +3,7 @@ import { RegularPolygon } from "react-konva";
import Konva from "konva";
import type { ShapeProps } from "./types";
import { getKonvaGradientProps } from "../../utils/gradient";
import { getKonvaBlurProps, useShapeBlurCache } from "../../utils/shapeBlur";
const TriangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProps) => {
const shapeRef = useRef<Konva.RegularPolygon>(null);
@@ -15,6 +16,18 @@ const TriangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shape
const actualStrokeWidth = obj.strokeWidth ?? 0;
const hasStroke = actualStrokeWidth > 0;
const blurProps = isMask ? {} : getKonvaBlurProps(obj.blur);
useShapeBlurCache(shapeRef, isMask ? 0 : obj.blur, [
obj.width,
obj.height,
obj.fill,
obj.fillType,
obj.gradient,
obj.stroke,
actualStrokeWidth,
obj.blur,
]);
// برای نمایش انتخاب: اگر strokeWidth واقعی > 0 است، از آن استفاده کن، وگرنه stroke را برای انتخاب نمایش بده
const displayStroke = isSelected
@@ -53,6 +66,7 @@ const TriangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shape
strokeWidth={displayStrokeWidth}
dash={isMask && showGuide ? [10, 5] : undefined}
rotation={obj.rotation || 0}
{...blurProps}
draggable={draggable}
onClick={(e) => {
if (shapeRef.current) {
@@ -82,6 +82,8 @@ export type EditorObject = {
scaleY?: number;
shapeType?: ShapeType;
borderRadius?: number;
/** میزان بلور شکل به پیکسل (۰ = بدون بلور) */
blur?: number;
tableData?: TableData;
visible?: boolean;
isMask?: boolean;
+42
View File
@@ -0,0 +1,42 @@
import { useLayoutEffect, type CSSProperties } from "react";
import type Konva from "konva";
import KonvaLib from "konva";
export function getKonvaBlurProps(blur?: number) {
const blurRadius = blur ?? 0;
if (blurRadius <= 0) return {};
return {
filters: [KonvaLib.Filters.Blur],
blurRadius,
};
}
export function getCssBlurStyle(blur?: number, scale = 1): CSSProperties {
const blurPx = blur ?? 0;
if (blurPx <= 0) return {};
return { filter: `blur(${blurPx * scale}px)` };
}
export function useShapeBlurCache(
shapeRef: React.RefObject<Konva.Shape | null>,
blur: number | undefined,
cacheKey: unknown,
) {
useLayoutEffect(() => {
const node = shapeRef.current;
if (!node) return;
const blurRadius = blur ?? 0;
if (blurRadius > 0) {
node.clearCache();
node.cache({
offset: blurRadius,
drawBorder: false,
});
} else {
node.clearCache();
}
node.getLayer()?.batchDraw();
}, [blur, cacheKey, shapeRef]);
}
+6
View File
@@ -11,6 +11,7 @@ import {
resolveTextMaxWidth,
usesWrappedLayout,
} from '@/pages/editor/utils/textStyle';
import { getCssBlurStyle } from '@/pages/editor/utils/shapeBlur';
import '@/pages/viewer/styles/entranceAnimations.css';
import { mergeEntranceAnimationStyle } from '@/pages/viewer/utils/entranceAnimationStyle';
import { getMaskImageStyle, getMaskedLayout } from '@/pages/viewer/utils/maskStyle';
@@ -174,6 +175,7 @@ const BookPage = memo(forwardRef<HTMLDivElement, BookPageProps>(
obj.fillType === 'gradient' && obj.gradient
? { backgroundImage: toCssLinearGradient(obj.gradient) }
: { backgroundColor: obj.fill || 'transparent' };
const shapeBlurStyle = getCssBlurStyle(obj.blur, scale);
switch (obj.type) {
case 'text': {
@@ -487,6 +489,7 @@ const BookPage = memo(forwardRef<HTMLDivElement, BookPageProps>(
height: `${radius * 2}px`,
borderRadius: '50%',
...objectFillStyle,
...shapeBlurStyle,
border: hasStroke && obj.stroke
? `${actualStrokeWidth * scale}px solid ${obj.stroke}`
: 'none',
@@ -549,6 +552,7 @@ const BookPage = memo(forwardRef<HTMLDivElement, BookPageProps>(
width: `${size}px`,
height: `${size}px`,
opacity: (obj.opacity ?? 100) / 100,
...shapeBlurStyle,
transform: obj.rotation ? `rotate(${obj.rotation}deg)` : undefined,
transformOrigin: 'center center',
zIndex: index,
@@ -608,6 +612,7 @@ const BookPage = memo(forwardRef<HTMLDivElement, BookPageProps>(
height: `${radius * 2}px`,
clipPath: 'polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%)',
...objectFillStyle,
...shapeBlurStyle,
border: obj.stroke
? `${(obj.strokeWidth || 1) * scale}px solid ${obj.stroke}`
: 'none',
@@ -630,6 +635,7 @@ const BookPage = memo(forwardRef<HTMLDivElement, BookPageProps>(
width: `${(obj.width || 100) * scale}px`,
height: `${(obj.height || 100) * scale}px`,
...objectFillStyle,
...shapeBlurStyle,
borderRadius: `${Math.max(0, (obj.borderRadius || 0) * scale)}px`,
border: hasStroke && obj.stroke
? `${actualStrokeWidth * scale}px solid ${obj.stroke}`
@@ -38,6 +38,7 @@ type ViewerDataPage = {
strokeWidth?: number;
shapeType?: string;
borderRadius?: number;
blur?: number;
imageUrl?: string;
videoUrl?: string;
audioUrl?: string;
@@ -129,6 +130,9 @@ export function transformViewerDataToPages(data: ViewerData): PageData[] {
if (obj.type === "rectangle" && obj.borderRadius !== undefined) {
baseObject.borderRadius = obj.borderRadius;
}
if (obj.type === "rectangle" && obj.blur !== undefined) {
baseObject.blur = obj.blur;
}
if (
(obj.type === "image" || obj.type === "sticker" || obj.type === "document") &&