diff --git a/src/pages/editor/components/sidebar/settings/EntranceAnimationSettings.tsx b/src/pages/editor/components/sidebar/settings/EntranceAnimationSettings.tsx
index 86c4152..c3e3f63 100644
--- a/src/pages/editor/components/sidebar/settings/EntranceAnimationSettings.tsx
+++ b/src/pages/editor/components/sidebar/settings/EntranceAnimationSettings.tsx
@@ -2,7 +2,11 @@ import Select from "@/components/Select";
import Input from "@/components/Input";
import type { EditorObject } from "../../../store/editorStore";
import { ENTRANCE_ANIMATION_SELECT_ITEMS } from "../constants/entranceAnimationOptions";
-import { DEFAULT_ENTRANCE_DURATION_MS } from "@/shared/entranceAnimation";
+import {
+ DEFAULT_ENTRANCE_DURATION_MS,
+ DEFAULT_ENTRANCE_DELAY_MS,
+ MAX_ENTRANCE_DELAY_MS,
+} from "@/shared/entranceAnimation";
type Props = {
selectedObject: EditorObject;
@@ -12,6 +16,7 @@ type Props = {
const EntranceAnimationSettings = ({ selectedObject, onUpdate }: Props) => {
const anim = selectedObject.entranceAnimation ?? "none";
const duration = selectedObject.entranceDurationMs ?? DEFAULT_ENTRANCE_DURATION_MS;
+ const delayMs = selectedObject.entranceDelayMs ?? DEFAULT_ENTRANCE_DELAY_MS;
return (
@@ -23,12 +28,14 @@ const EntranceAnimationSettings = ({ selectedObject, onUpdate }: Props) => {
const v = e.target.value as EditorObject["entranceAnimation"];
onUpdate(selectedObject.id, {
entranceAnimation: v === "none" ? undefined : v,
- ...(v === "none" || !v ? { entranceDurationMs: undefined } : {}),
+ ...(v === "none" || !v
+ ? { entranceDurationMs: undefined, entranceDelayMs: undefined }
+ : {}),
});
}}
/>
{anim !== "none" && (
- {
onUpdate(selectedObject.id, {
entranceDurationMs: Math.min(4000, Math.max(100, n)),
});
- }}
- />
+ }} /> {
+ const n = parseInt(e.target.value, 10);
+ if (Number.isNaN(n)) return;
+ const clamped = Math.min(MAX_ENTRANCE_DELAY_MS, Math.max(0, n));
+ onUpdate(selectedObject.id, {
+ entranceDelayMs: clamped === 0 ? undefined : clamped,
+ });
+ }} />>
)}
);
diff --git a/src/pages/editor/store/editorStore.types.ts b/src/pages/editor/store/editorStore.types.ts
index 1c3e0a9..f3d6380 100644
--- a/src/pages/editor/store/editorStore.types.ts
+++ b/src/pages/editor/store/editorStore.types.ts
@@ -85,6 +85,8 @@ export type EditorObject = {
entranceAnimation?: EntranceAnimationType;
/** مدت انیمیشن ورود به میلیثانیه (پیشفرض ۶۰۰) */
entranceDurationMs?: number;
+ /** تأخیر قبل از شروع انیمیشن ورود به میلیثانیه (پیشفرض ۰) */
+ entranceDelayMs?: number;
};
export type PageGuide = {
diff --git a/src/pages/viewer/utils/dataTransformer.ts b/src/pages/viewer/utils/dataTransformer.ts
index e427757..dbf884e 100644
--- a/src/pages/viewer/utils/dataTransformer.ts
+++ b/src/pages/viewer/utils/dataTransformer.ts
@@ -45,6 +45,7 @@ type ViewerDataPage = {
tableData?: TableData;
entranceAnimation?: EditorObject["entranceAnimation"];
entranceDurationMs?: number;
+ entranceDelayMs?: number;
}>;
};
@@ -142,6 +143,9 @@ export function transformViewerDataToPages(data: ViewerData): PageData[] {
if (obj.entranceDurationMs !== undefined) {
baseObject.entranceDurationMs = obj.entranceDurationMs;
}
+ if (obj.entranceDelayMs !== undefined) {
+ baseObject.entranceDelayMs = obj.entranceDelayMs;
+ }
return baseObject;
});
diff --git a/src/pages/viewer/utils/entranceAnimationStyle.ts b/src/pages/viewer/utils/entranceAnimationStyle.ts
index 15130f5..265b447 100644
--- a/src/pages/viewer/utils/entranceAnimationStyle.ts
+++ b/src/pages/viewer/utils/entranceAnimationStyle.ts
@@ -2,7 +2,9 @@ import type { CSSProperties } from "react";
import type { EditorObject } from "@/pages/editor/store/editorStore";
import {
DEFAULT_ENTRANCE_DURATION_MS,
+ DEFAULT_ENTRANCE_DELAY_MS,
ENTRANCE_ANIMATION_BASE_DELAY_MS,
+ MAX_ENTRANCE_DELAY_MS,
} from "@/shared/entranceAnimation";
const ANIMATION_NAMES: Record<
@@ -76,6 +78,11 @@ export function mergeEntranceAnimationStyle(
1000;
const baseDelaySec = ENTRANCE_ANIMATION_BASE_DELAY_MS / 1000;
const staggerSec = Math.min(elementIndex * 0.045, 1.2);
+ const userDelayMs = Math.min(
+ MAX_ENTRANCE_DELAY_MS,
+ Math.max(DEFAULT_ENTRANCE_DELAY_MS, obj.entranceDelayMs ?? DEFAULT_ENTRANCE_DELAY_MS),
+ );
+ const userDelaySec = userDelayMs / 1000;
const rot = options?.rotationDeg ?? obj.rotation ?? 0;
const opacityEnd = (obj.opacity ?? 100) / 100;
@@ -102,7 +109,7 @@ export function mergeEntranceAnimationStyle(
willChange: "transform, opacity",
animationName: name,
animationDuration: `${durationSec}s`,
- animationDelay: `${baseDelaySec + staggerSec}s`,
+ animationDelay: `${baseDelaySec + staggerSec + userDelaySec}s`,
animationTimingFunction: "cubic-bezier(0.22, 1, 0.36, 1)",
animationFillMode: "both",
};
diff --git a/src/shared/entranceAnimation.ts b/src/shared/entranceAnimation.ts
index ffa37a9..2c67bbc 100644
--- a/src/shared/entranceAnimation.ts
+++ b/src/shared/entranceAnimation.ts
@@ -1,5 +1,11 @@
/** مدت پیشفرض انیمیشن ورود در editor و viewer (میلیثانیه) */
export const DEFAULT_ENTRANCE_DURATION_MS = 1000;
+/** تأخیر اضافهشده توسط کاربر قبل از شروع انیمیشن (میلیثانیه؛ پیشفرض ۰) */
+export const DEFAULT_ENTRANCE_DELAY_MS = 0;
+
+/** سقف تأخیر کاربر (میلیثانیه) */
+export const MAX_ENTRANCE_DELAY_MS = 30_000;
+
/** تأخیر کوتاه قبل از شروع انیمیشن ورود تا با ورق خوردن صفحه همهماهنگ شود (کمتر = حس فوریتر) */
export const ENTRANCE_ANIMATION_BASE_DELAY_MS = 75;