delay in animation

This commit is contained in:
hamid zarghami
2026-05-12 16:05:06 +03:30
parent 6a8eaab8cb
commit 75c9a2515b
5 changed files with 44 additions and 6 deletions
@@ -2,7 +2,11 @@ import Select from "@/components/Select";
import Input from "@/components/Input"; import Input from "@/components/Input";
import type { EditorObject } from "../../../store/editorStore"; import type { EditorObject } from "../../../store/editorStore";
import { ENTRANCE_ANIMATION_SELECT_ITEMS } from "../constants/entranceAnimationOptions"; 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 = { type Props = {
selectedObject: EditorObject; selectedObject: EditorObject;
@@ -12,6 +16,7 @@ type Props = {
const EntranceAnimationSettings = ({ selectedObject, onUpdate }: Props) => { const EntranceAnimationSettings = ({ selectedObject, onUpdate }: Props) => {
const anim = selectedObject.entranceAnimation ?? "none"; const anim = selectedObject.entranceAnimation ?? "none";
const duration = selectedObject.entranceDurationMs ?? DEFAULT_ENTRANCE_DURATION_MS; const duration = selectedObject.entranceDurationMs ?? DEFAULT_ENTRANCE_DURATION_MS;
const delayMs = selectedObject.entranceDelayMs ?? DEFAULT_ENTRANCE_DELAY_MS;
return ( return (
<div className="pt-4 flex flex-col gap-4 border-t border-border"> <div className="pt-4 flex flex-col gap-4 border-t border-border">
@@ -23,12 +28,14 @@ const EntranceAnimationSettings = ({ selectedObject, onUpdate }: Props) => {
const v = e.target.value as EditorObject["entranceAnimation"]; const v = e.target.value as EditorObject["entranceAnimation"];
onUpdate(selectedObject.id, { onUpdate(selectedObject.id, {
entranceAnimation: v === "none" ? undefined : v, entranceAnimation: v === "none" ? undefined : v,
...(v === "none" || !v ? { entranceDurationMs: undefined } : {}), ...(v === "none" || !v
? { entranceDurationMs: undefined, entranceDelayMs: undefined }
: {}),
}); });
}} }}
/> />
{anim !== "none" && ( {anim !== "none" && (
<Input <><Input
label="مدت انیمیشن (میلی‌ثانیه)" label="مدت انیمیشن (میلی‌ثانیه)"
type="number" type="number"
min={100} min={100}
@@ -40,8 +47,20 @@ const EntranceAnimationSettings = ({ selectedObject, onUpdate }: Props) => {
onUpdate(selectedObject.id, { onUpdate(selectedObject.id, {
entranceDurationMs: Math.min(4000, Math.max(100, n)), entranceDurationMs: Math.min(4000, Math.max(100, n)),
}); });
}} }} /><Input
/> label="تأخیر قبل از شروع (میلی‌ثانیه)"
type="number"
min={0}
max={MAX_ENTRANCE_DELAY_MS}
value={delayMs}
onChange={(e) => {
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,
});
}} /></>
)} )}
</div> </div>
); );
@@ -85,6 +85,8 @@ export type EditorObject = {
entranceAnimation?: EntranceAnimationType; entranceAnimation?: EntranceAnimationType;
/** مدت انیمیشن ورود به میلی‌ثانیه (پیش‌فرض ۶۰۰) */ /** مدت انیمیشن ورود به میلی‌ثانیه (پیش‌فرض ۶۰۰) */
entranceDurationMs?: number; entranceDurationMs?: number;
/** تأخیر قبل از شروع انیمیشن ورود به میلی‌ثانیه (پیش‌فرض ۰) */
entranceDelayMs?: number;
}; };
export type PageGuide = { export type PageGuide = {
@@ -45,6 +45,7 @@ type ViewerDataPage = {
tableData?: TableData; tableData?: TableData;
entranceAnimation?: EditorObject["entranceAnimation"]; entranceAnimation?: EditorObject["entranceAnimation"];
entranceDurationMs?: number; entranceDurationMs?: number;
entranceDelayMs?: number;
}>; }>;
}; };
@@ -142,6 +143,9 @@ export function transformViewerDataToPages(data: ViewerData): PageData[] {
if (obj.entranceDurationMs !== undefined) { if (obj.entranceDurationMs !== undefined) {
baseObject.entranceDurationMs = obj.entranceDurationMs; baseObject.entranceDurationMs = obj.entranceDurationMs;
} }
if (obj.entranceDelayMs !== undefined) {
baseObject.entranceDelayMs = obj.entranceDelayMs;
}
return baseObject; return baseObject;
}); });
@@ -2,7 +2,9 @@ import type { CSSProperties } from "react";
import type { EditorObject } from "@/pages/editor/store/editorStore"; import type { EditorObject } from "@/pages/editor/store/editorStore";
import { import {
DEFAULT_ENTRANCE_DURATION_MS, DEFAULT_ENTRANCE_DURATION_MS,
DEFAULT_ENTRANCE_DELAY_MS,
ENTRANCE_ANIMATION_BASE_DELAY_MS, ENTRANCE_ANIMATION_BASE_DELAY_MS,
MAX_ENTRANCE_DELAY_MS,
} from "@/shared/entranceAnimation"; } from "@/shared/entranceAnimation";
const ANIMATION_NAMES: Record< const ANIMATION_NAMES: Record<
@@ -76,6 +78,11 @@ export function mergeEntranceAnimationStyle(
1000; 1000;
const baseDelaySec = ENTRANCE_ANIMATION_BASE_DELAY_MS / 1000; const baseDelaySec = ENTRANCE_ANIMATION_BASE_DELAY_MS / 1000;
const staggerSec = Math.min(elementIndex * 0.045, 1.2); 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 rot = options?.rotationDeg ?? obj.rotation ?? 0;
const opacityEnd = (obj.opacity ?? 100) / 100; const opacityEnd = (obj.opacity ?? 100) / 100;
@@ -102,7 +109,7 @@ export function mergeEntranceAnimationStyle(
willChange: "transform, opacity", willChange: "transform, opacity",
animationName: name, animationName: name,
animationDuration: `${durationSec}s`, animationDuration: `${durationSec}s`,
animationDelay: `${baseDelaySec + staggerSec}s`, animationDelay: `${baseDelaySec + staggerSec + userDelaySec}s`,
animationTimingFunction: "cubic-bezier(0.22, 1, 0.36, 1)", animationTimingFunction: "cubic-bezier(0.22, 1, 0.36, 1)",
animationFillMode: "both", animationFillMode: "both",
}; };
+6
View File
@@ -1,5 +1,11 @@
/** مدت پیش‌فرض انیمیشن ورود در editor و viewer (میلی‌ثانیه) */ /** مدت پیش‌فرض انیمیشن ورود در editor و viewer (میلی‌ثانیه) */
export const DEFAULT_ENTRANCE_DURATION_MS = 1000; 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; export const ENTRANCE_ANIMATION_BASE_DELAY_MS = 75;