background video
This commit is contained in:
@@ -32,6 +32,7 @@ function getPageDataFromEditorPage(page: Page): PageData | null {
|
|||||||
backgroundColor: page.backgroundColor,
|
backgroundColor: page.backgroundColor,
|
||||||
backgroundGradient: page.backgroundGradient,
|
backgroundGradient: page.backgroundGradient,
|
||||||
backgroundImageUrl: page.backgroundImageUrl,
|
backgroundImageUrl: page.backgroundImageUrl,
|
||||||
|
backgroundVideoUrl: page.backgroundVideoUrl,
|
||||||
objects: page.objects,
|
objects: page.objects,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ const EditorCanvas = ({ catalogSize }: EditorCanvasProps) => {
|
|||||||
? currentPage.backgroundColor
|
? currentPage.backgroundColor
|
||||||
: '#ffffff';
|
: '#ffffff';
|
||||||
const bgGradient = currentPage?.backgroundGradient;
|
const bgGradient = currentPage?.backgroundGradient;
|
||||||
|
const hasVideoBackground =
|
||||||
|
currentPage?.backgroundType === "video" &&
|
||||||
|
Boolean(currentPage.backgroundVideoUrl);
|
||||||
|
|
||||||
const [bgImage] = useImage(
|
const [bgImage] = useImage(
|
||||||
currentPage?.backgroundType === 'image'
|
currentPage?.backgroundType === 'image'
|
||||||
@@ -527,10 +530,20 @@ const EditorCanvas = ({ catalogSize }: EditorCanvasProps) => {
|
|||||||
<div className="absolute -top-6 -left-6 w-6 h-6 bg-slate-200 border border-slate-300" />
|
<div className="absolute -top-6 -left-6 w-6 h-6 bg-slate-200 border border-slate-300" />
|
||||||
<div
|
<div
|
||||||
ref={stageWrapRef}
|
ref={stageWrapRef}
|
||||||
className="shadow-lg"
|
className="relative shadow-lg"
|
||||||
onDragOver={handleSidebarDragOver}
|
onDragOver={handleSidebarDragOver}
|
||||||
onDrop={handleSidebarDrop}
|
onDrop={handleSidebarDrop}
|
||||||
>
|
>
|
||||||
|
{hasVideoBackground && (
|
||||||
|
<video
|
||||||
|
src={currentPage?.backgroundVideoUrl}
|
||||||
|
autoPlay
|
||||||
|
loop
|
||||||
|
muted
|
||||||
|
playsInline
|
||||||
|
className="absolute inset-0 w-full h-full object-cover pointer-events-none"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Stage
|
<Stage
|
||||||
ref={stageRef}
|
ref={stageRef}
|
||||||
width={stageSize.width * finalScale}
|
width={stageSize.width * finalScale}
|
||||||
@@ -549,16 +562,18 @@ const EditorCanvas = ({ catalogSize }: EditorCanvasProps) => {
|
|||||||
onDragEnd={handleDragEnd}
|
onDragEnd={handleDragEnd}
|
||||||
>
|
>
|
||||||
<Layer listening={false}>
|
<Layer listening={false}>
|
||||||
<Rect
|
{!hasVideoBackground && (
|
||||||
x={0}
|
<Rect
|
||||||
y={0}
|
x={0}
|
||||||
width={stageSize.width}
|
y={0}
|
||||||
height={stageSize.height}
|
width={stageSize.width}
|
||||||
fill={bgColor}
|
height={stageSize.height}
|
||||||
{...backgroundGradientProps}
|
fill={bgColor}
|
||||||
listening={false}
|
{...backgroundGradientProps}
|
||||||
/>
|
listening={false}
|
||||||
{currentPage?.backgroundType === 'image' && bgImage && (
|
/>
|
||||||
|
)}
|
||||||
|
{!hasVideoBackground && currentPage?.backgroundType === 'image' && bgImage && (
|
||||||
<KonvaImage
|
<KonvaImage
|
||||||
image={bgImage}
|
image={bgImage}
|
||||||
x={0}
|
x={0}
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ function isPageEmptyForPreview(page: Page): boolean {
|
|||||||
if (page.backgroundType === 'image' && page.backgroundImageUrl.trim() !== '') {
|
if (page.backgroundType === 'image' && page.backgroundImageUrl.trim() !== '') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if (page.backgroundType === 'video' && page.backgroundVideoUrl.trim() !== '') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if (page.backgroundType === 'gradient') return false
|
if (page.backgroundType === 'gradient') return false
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const BACKGROUND_TYPE_OPTIONS: { value: BackgroundType; label: string }[] = [
|
|||||||
{ value: 'color', label: 'رنگ' },
|
{ value: 'color', label: 'رنگ' },
|
||||||
{ value: 'gradient', label: 'گرادیانت' },
|
{ value: 'gradient', label: 'گرادیانت' },
|
||||||
{ value: 'image', label: 'تصویر' },
|
{ value: 'image', label: 'تصویر' },
|
||||||
|
{ value: 'video', label: 'ویدیو' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const SettingsPanel = () => {
|
const SettingsPanel = () => {
|
||||||
@@ -38,7 +39,7 @@ const SettingsPanel = () => {
|
|||||||
updateCurrentPageBackground,
|
updateCurrentPageBackground,
|
||||||
} = useEditorStore()
|
} = useEditorStore()
|
||||||
const colorInputRef = useRef<HTMLInputElement>(null)
|
const colorInputRef = useRef<HTMLInputElement>(null)
|
||||||
const { mutateAsync: uploadFile, isPending: imageLoading } = useSingleUpload()
|
const { mutateAsync: uploadFile, isPending: mediaLoading } = useSingleUpload()
|
||||||
const currentPage = pages.find((page) => page.id === currentPageId)
|
const currentPage = pages.find((page) => page.id === currentPageId)
|
||||||
const backgroundType = currentPage?.backgroundType ?? 'color'
|
const backgroundType = currentPage?.backgroundType ?? 'color'
|
||||||
const backgroundColor = currentPage?.backgroundColor ?? '#ffffff'
|
const backgroundColor = currentPage?.backgroundColor ?? '#ffffff'
|
||||||
@@ -48,6 +49,7 @@ const SettingsPanel = () => {
|
|||||||
angle: 135,
|
angle: 135,
|
||||||
}
|
}
|
||||||
const backgroundImageUrl = currentPage?.backgroundImageUrl ?? ''
|
const backgroundImageUrl = currentPage?.backgroundImageUrl ?? ''
|
||||||
|
const backgroundVideoUrl = currentPage?.backgroundVideoUrl ?? ''
|
||||||
const [uploadedPreviews, setUploadedPreviews] = useState<string[]>(
|
const [uploadedPreviews, setUploadedPreviews] = useState<string[]>(
|
||||||
backgroundImageUrl ? [backgroundImageUrl] : []
|
backgroundImageUrl ? [backgroundImageUrl] : []
|
||||||
)
|
)
|
||||||
@@ -66,6 +68,11 @@ const SettingsPanel = () => {
|
|||||||
)
|
)
|
||||||
}, [backgroundImageUrl])
|
}, [backgroundImageUrl])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (backgroundType !== 'video') return
|
||||||
|
setUploadedPreviews(backgroundVideoUrl ? [backgroundVideoUrl] : [])
|
||||||
|
}, [backgroundType, backgroundVideoUrl])
|
||||||
|
|
||||||
const handleImageFiles = async (files: File[]) => {
|
const handleImageFiles = async (files: File[]) => {
|
||||||
if (files.length === 0) return
|
if (files.length === 0) return
|
||||||
const file = files[files.length - 1]
|
const file = files[files.length - 1]
|
||||||
@@ -83,10 +90,31 @@ const SettingsPanel = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleVideoFiles = async (files: File[]) => {
|
||||||
|
if (files.length === 0) return
|
||||||
|
const file = files[files.length - 1]
|
||||||
|
if (!file) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await uploadFile({ file })
|
||||||
|
const videoUrl = result?.data?.url
|
||||||
|
if (!videoUrl) return
|
||||||
|
|
||||||
|
updateCurrentPageBackground({ backgroundVideoUrl: videoUrl })
|
||||||
|
setUploadedPreviews([videoUrl])
|
||||||
|
} catch {
|
||||||
|
// TODO: show error toast
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handlePreviewChange = (previews: string[]) => {
|
const handlePreviewChange = (previews: string[]) => {
|
||||||
setUploadedPreviews(previews)
|
setUploadedPreviews(previews)
|
||||||
if (previews.length === 0) {
|
if (previews.length === 0) {
|
||||||
updateCurrentPageBackground({ backgroundImageUrl: '' })
|
if (backgroundType === 'image') {
|
||||||
|
updateCurrentPageBackground({ backgroundImageUrl: '' })
|
||||||
|
} else if (backgroundType === 'video') {
|
||||||
|
updateCurrentPageBackground({ backgroundVideoUrl: '' })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +177,7 @@ const SettingsPanel = () => {
|
|||||||
<h3 className="text-sm mb-4">پس زمینه</h3>
|
<h3 className="text-sm mb-4">پس زمینه</h3>
|
||||||
|
|
||||||
{/* نوع پسزمینه */}
|
{/* نوع پسزمینه */}
|
||||||
<div className="flex items-center gap-5 mb-4">
|
<div className="flex flex-wrap items-center gap-5 mb-4">
|
||||||
{BACKGROUND_TYPE_OPTIONS.map((opt) => (
|
{BACKGROUND_TYPE_OPTIONS.map((opt) => (
|
||||||
<label key={opt.value} className="flex items-center gap-1.5 cursor-pointer select-none">
|
<label key={opt.value} className="flex items-center gap-1.5 cursor-pointer select-none">
|
||||||
<span className="text-xs text-gray-600">{opt.label}</span>
|
<span className="text-xs text-gray-600">{opt.label}</span>
|
||||||
@@ -166,6 +194,8 @@ const SettingsPanel = () => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
|
...(opt.value !== 'image' ? { backgroundImageUrl: '' } : {}),
|
||||||
|
...(opt.value !== 'video' ? { backgroundVideoUrl: '' } : {}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
className={clx(
|
className={clx(
|
||||||
@@ -301,7 +331,7 @@ const SettingsPanel = () => {
|
|||||||
{/* ─── آپلود تصویر ─── */}
|
{/* ─── آپلود تصویر ─── */}
|
||||||
{backgroundType === 'image' && (
|
{backgroundType === 'image' && (
|
||||||
<div>
|
<div>
|
||||||
{imageLoading ? (
|
{mediaLoading ? (
|
||||||
<div className="flex flex-col items-center justify-center gap-3 py-8">
|
<div className="flex flex-col items-center justify-center gap-3 py-8">
|
||||||
<span className="h-6 w-6 animate-spin rounded-full border-2 border-gray-300 border-t-gray-800" />
|
<span className="h-6 w-6 animate-spin rounded-full border-2 border-gray-300 border-t-gray-800" />
|
||||||
<span className="text-xs text-gray-500">در حال آپلود...</span>
|
<span className="text-xs text-gray-500">در حال آپلود...</span>
|
||||||
@@ -317,6 +347,44 @@ const SettingsPanel = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{backgroundType === 'video' && (
|
||||||
|
<div>
|
||||||
|
{mediaLoading ? (
|
||||||
|
<div className="flex flex-col items-center justify-center gap-3 py-8">
|
||||||
|
<span className="h-6 w-6 animate-spin rounded-full border-2 border-gray-300 border-t-gray-800" />
|
||||||
|
<span className="text-xs text-gray-500">در حال آپلود...</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<UploadBoxDraggble
|
||||||
|
label="ویدیوی پسزمینه را آپلود کنید"
|
||||||
|
onChange={handleVideoFiles}
|
||||||
|
isMultiple={false}
|
||||||
|
hidePreview
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{backgroundVideoUrl && (
|
||||||
|
<div className="mt-3 space-y-2">
|
||||||
|
<video
|
||||||
|
src={backgroundVideoUrl}
|
||||||
|
controls
|
||||||
|
muted
|
||||||
|
className="w-full h-36 rounded-lg border border-border bg-black object-cover"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
updateCurrentPageBackground({ backgroundVideoUrl: '' })
|
||||||
|
setUploadedPreviews([])
|
||||||
|
}}
|
||||||
|
className="text-xs text-red-500 hover:text-red-600"
|
||||||
|
>
|
||||||
|
حذف ویدیو
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ export const createInitialPage = (name: string): Page => ({
|
|||||||
angle: 135,
|
angle: 135,
|
||||||
},
|
},
|
||||||
backgroundImageUrl: "",
|
backgroundImageUrl: "",
|
||||||
|
backgroundVideoUrl: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
export const getDefaultPageName = (index: number): string =>
|
export const getDefaultPageName = (index: number): string =>
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ type PageBackgroundSettings = Pick<
|
|||||||
| "backgroundColor"
|
| "backgroundColor"
|
||||||
| "backgroundGradient"
|
| "backgroundGradient"
|
||||||
| "backgroundImageUrl"
|
| "backgroundImageUrl"
|
||||||
|
| "backgroundVideoUrl"
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type EditorStoreType = {
|
type EditorStoreType = {
|
||||||
@@ -191,6 +192,7 @@ const DEFAULT_DOCUMENT_SETTINGS: DocumentSettings = {
|
|||||||
angle: 135,
|
angle: 135,
|
||||||
},
|
},
|
||||||
backgroundImageUrl: "",
|
backgroundImageUrl: "",
|
||||||
|
backgroundVideoUrl: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useEditorStore = create<EditorStoreType>((set, get) => {
|
export const useEditorStore = create<EditorStoreType>((set, get) => {
|
||||||
@@ -858,6 +860,7 @@ export const useEditorStore = create<EditorStoreType>((set, get) => {
|
|||||||
backgroundColor: pageToDuplicate.backgroundColor,
|
backgroundColor: pageToDuplicate.backgroundColor,
|
||||||
backgroundGradient: pageToDuplicate.backgroundGradient,
|
backgroundGradient: pageToDuplicate.backgroundGradient,
|
||||||
backgroundImageUrl: pageToDuplicate.backgroundImageUrl,
|
backgroundImageUrl: pageToDuplicate.backgroundImageUrl,
|
||||||
|
backgroundVideoUrl: pageToDuplicate.backgroundVideoUrl,
|
||||||
};
|
};
|
||||||
|
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
@@ -1058,6 +1061,8 @@ export const useEditorStore = create<EditorStoreType>((set, get) => {
|
|||||||
},
|
},
|
||||||
backgroundImageUrl:
|
backgroundImageUrl:
|
||||||
page.backgroundImageUrl ?? fallbackSettings.backgroundImageUrl ?? "",
|
page.backgroundImageUrl ?? fallbackSettings.backgroundImageUrl ?? "",
|
||||||
|
backgroundVideoUrl:
|
||||||
|
page.backgroundVideoUrl ?? fallbackSettings.backgroundVideoUrl ?? "",
|
||||||
}));
|
}));
|
||||||
const firstPage = normalizedPages[0];
|
const firstPage = normalizedPages[0];
|
||||||
const currentSettings = get().documentSettings;
|
const currentSettings = get().documentSettings;
|
||||||
|
|||||||
@@ -119,10 +119,11 @@ export type Page = {
|
|||||||
backgroundColor: string;
|
backgroundColor: string;
|
||||||
backgroundGradient?: LinearGradient;
|
backgroundGradient?: LinearGradient;
|
||||||
backgroundImageUrl: string;
|
backgroundImageUrl: string;
|
||||||
|
backgroundVideoUrl: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DisplayStyle = "single" | "double";
|
export type DisplayStyle = "single" | "double";
|
||||||
export type BackgroundType = "color" | "gradient" | "image";
|
export type BackgroundType = "color" | "gradient" | "image" | "video";
|
||||||
|
|
||||||
export type DocumentSettings = {
|
export type DocumentSettings = {
|
||||||
displayStyle: DisplayStyle;
|
displayStyle: DisplayStyle;
|
||||||
@@ -134,6 +135,7 @@ export type DocumentSettings = {
|
|||||||
backgroundColor: string;
|
backgroundColor: string;
|
||||||
backgroundGradient?: LinearGradient;
|
backgroundGradient?: LinearGradient;
|
||||||
backgroundImageUrl: string;
|
backgroundImageUrl: string;
|
||||||
|
backgroundVideoUrl: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** تصویر آپلودشده در گالری کاتالوگ (مشترک بین همهٔ صفحات) */
|
/** تصویر آپلودشده در گالری کاتالوگ (مشترک بین همهٔ صفحات) */
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ type BookPageProps = {
|
|||||||
pageWidth?: number;
|
pageWidth?: number;
|
||||||
pageHeight?: number;
|
pageHeight?: number;
|
||||||
onLinkClick?: (linkUrl: string) => void;
|
onLinkClick?: (linkUrl: string) => void;
|
||||||
backgroundType?: 'color' | 'gradient' | 'image';
|
backgroundType?: 'color' | 'gradient' | 'image' | 'video';
|
||||||
backgroundColor?: string;
|
backgroundColor?: string;
|
||||||
backgroundGradient?: {
|
backgroundGradient?: {
|
||||||
from: string;
|
from: string;
|
||||||
@@ -56,6 +56,7 @@ type BookPageProps = {
|
|||||||
angle: number;
|
angle: number;
|
||||||
};
|
};
|
||||||
backgroundImageUrl?: string;
|
backgroundImageUrl?: string;
|
||||||
|
backgroundVideoUrl?: string;
|
||||||
/** فاز انیمیشن ورود — از BookViewer کنترل میشود */
|
/** فاز انیمیشن ورود — از BookViewer کنترل میشود */
|
||||||
entrancePhase?: EntrancePhase;
|
entrancePhase?: EntrancePhase;
|
||||||
/** در پیشنمایش کاتالوگ انیمی션 ورود غیرفعال باشد */
|
/** در پیشنمایش کاتالوگ انیمی션 ورود غیرفعال باشد */
|
||||||
@@ -82,7 +83,7 @@ type BookPageProps = {
|
|||||||
* نیاز به forwardRef دارد تا react-pageflip بتواند ref را مدیریت کند
|
* نیاز به forwardRef دارد تا react-pageflip بتواند ref را مدیریت کند
|
||||||
*/
|
*/
|
||||||
const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
||||||
({ page, scale = 1, pageWidth, pageHeight, onLinkClick, backgroundType, backgroundColor, backgroundGradient, backgroundImageUrl, entrancePhase = 'idle', disableEntranceAnimations = false, staticMedia = false, showPaperShadow = true, useHardPage = false, idSuffix = '' }, ref) => {
|
({ page, scale = 1, pageWidth, pageHeight, onLinkClick, backgroundType, backgroundColor, backgroundGradient, backgroundImageUrl, backgroundVideoUrl, entrancePhase = 'idle', disableEntranceAnimations = false, staticMedia = false, showPaperShadow = true, useHardPage = false, idSuffix = '' }, ref) => {
|
||||||
const phase: EntrancePhase = disableEntranceAnimations ? 'settled' : entrancePhase;
|
const phase: EntrancePhase = disableEntranceAnimations ? 'settled' : entrancePhase;
|
||||||
// تابع برای تبدیل opacity به رنگ (مثل editor)
|
// تابع برای تبدیل opacity به رنگ (مثل editor)
|
||||||
// در editor، getColorWithOpacity انتظار opacity 0-100 دارد
|
// در editor، getColorWithOpacity انتظار opacity 0-100 دارد
|
||||||
@@ -933,6 +934,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
|||||||
const effectiveBackgroundColor = backgroundColor ?? page.backgroundColor ?? '#ffffff';
|
const effectiveBackgroundColor = backgroundColor ?? page.backgroundColor ?? '#ffffff';
|
||||||
const effectiveBackgroundGradient = backgroundGradient ?? page.backgroundGradient;
|
const effectiveBackgroundGradient = backgroundGradient ?? page.backgroundGradient;
|
||||||
const effectiveBackgroundImageUrl = backgroundImageUrl ?? page.backgroundImageUrl ?? '';
|
const effectiveBackgroundImageUrl = backgroundImageUrl ?? page.backgroundImageUrl ?? '';
|
||||||
|
const effectiveBackgroundVideoUrl = backgroundVideoUrl ?? page.backgroundVideoUrl ?? '';
|
||||||
|
|
||||||
const bgStyle: React.CSSProperties =
|
const bgStyle: React.CSSProperties =
|
||||||
effectiveBackgroundType === 'image' && effectiveBackgroundImageUrl
|
effectiveBackgroundType === 'image' && effectiveBackgroundImageUrl
|
||||||
@@ -968,16 +970,38 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!useHardPage && (
|
{!useHardPage && (
|
||||||
<div
|
<>
|
||||||
aria-hidden
|
<div
|
||||||
style={{
|
aria-hidden
|
||||||
position: 'absolute',
|
style={{
|
||||||
inset: 0,
|
position: 'absolute',
|
||||||
...bgStyle,
|
inset: 0,
|
||||||
pointerEvents: 'none',
|
...bgStyle,
|
||||||
zIndex: 0,
|
pointerEvents: 'none',
|
||||||
}}
|
zIndex: 0,
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
{effectiveBackgroundType === 'video' && effectiveBackgroundVideoUrl && (
|
||||||
|
<video
|
||||||
|
aria-hidden
|
||||||
|
src={effectiveBackgroundVideoUrl}
|
||||||
|
autoPlay
|
||||||
|
loop
|
||||||
|
muted
|
||||||
|
playsInline
|
||||||
|
preload="metadata"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
inset: 0,
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
objectFit: 'cover',
|
||||||
|
pointerEvents: 'none',
|
||||||
|
zIndex: 0,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<div style={{ position: 'absolute', inset: 0, zIndex: 1 }}>
|
<div style={{ position: 'absolute', inset: 0, zIndex: 1 }}>
|
||||||
{page.elements.map((element, index) => renderObject(element, index, page.elements))}
|
{page.elements.map((element, index) => renderObject(element, index, page.elements))}
|
||||||
|
|||||||
@@ -185,9 +185,10 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
|||||||
const backgroundColor = documentSettings?.backgroundColor ?? "#ffffff";
|
const backgroundColor = documentSettings?.backgroundColor ?? "#ffffff";
|
||||||
const backgroundGradient = documentSettings?.backgroundGradient;
|
const backgroundGradient = documentSettings?.backgroundGradient;
|
||||||
const backgroundImageUrl = documentSettings?.backgroundImageUrl ?? "";
|
const backgroundImageUrl = documentSettings?.backgroundImageUrl ?? "";
|
||||||
|
const backgroundVideoUrl = documentSettings?.backgroundVideoUrl ?? "";
|
||||||
const pageBackgroundDefaults = useMemo(
|
const pageBackgroundDefaults = useMemo(
|
||||||
() => ({ backgroundType, backgroundColor, backgroundGradient, backgroundImageUrl }),
|
() => ({ backgroundType, backgroundColor, backgroundGradient, backgroundImageUrl, backgroundVideoUrl }),
|
||||||
[backgroundType, backgroundColor, backgroundGradient, backgroundImageUrl],
|
[backgroundType, backgroundColor, backgroundGradient, backgroundImageUrl, backgroundVideoUrl],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -610,6 +611,7 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
|||||||
backgroundColor={background.backgroundColor}
|
backgroundColor={background.backgroundColor}
|
||||||
backgroundGradient={background.backgroundGradient}
|
backgroundGradient={background.backgroundGradient}
|
||||||
backgroundImageUrl={background.backgroundImageUrl}
|
backgroundImageUrl={background.backgroundImageUrl}
|
||||||
|
backgroundVideoUrl={background.backgroundVideoUrl}
|
||||||
useHardPage={legacyIOS}
|
useHardPage={legacyIOS}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ const Magnifier = memo(
|
|||||||
backgroundColor={background.backgroundColor}
|
backgroundColor={background.backgroundColor}
|
||||||
backgroundGradient={background.backgroundGradient}
|
backgroundGradient={background.backgroundGradient}
|
||||||
backgroundImageUrl={background.backgroundImageUrl}
|
backgroundImageUrl={background.backgroundImageUrl}
|
||||||
|
backgroundVideoUrl={background.backgroundVideoUrl}
|
||||||
useHardPage={useHardPage}
|
useHardPage={useHardPage}
|
||||||
disableEntranceAnimations
|
disableEntranceAnimations
|
||||||
showPaperShadow={false}
|
showPaperShadow={false}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export type PageData = {
|
|||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
elements: EditorObject[];
|
elements: EditorObject[];
|
||||||
backgroundType?: "color" | "gradient" | "image";
|
backgroundType?: "color" | "gradient" | "image" | "video";
|
||||||
backgroundColor?: string;
|
backgroundColor?: string;
|
||||||
backgroundGradient?: {
|
backgroundGradient?: {
|
||||||
from: string;
|
from: string;
|
||||||
@@ -13,5 +13,6 @@ export type PageData = {
|
|||||||
angle: number;
|
angle: number;
|
||||||
};
|
};
|
||||||
backgroundImageUrl?: string;
|
backgroundImageUrl?: string;
|
||||||
|
backgroundVideoUrl?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type { PageData } from "../types";
|
|||||||
type ViewerDataPage = {
|
type ViewerDataPage = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
backgroundType?: "color" | "gradient" | "image";
|
backgroundType?: "color" | "gradient" | "image" | "video";
|
||||||
backgroundColor?: string;
|
backgroundColor?: string;
|
||||||
backgroundGradient?: {
|
backgroundGradient?: {
|
||||||
from: string;
|
from: string;
|
||||||
@@ -13,6 +13,7 @@ type ViewerDataPage = {
|
|||||||
angle: number;
|
angle: number;
|
||||||
};
|
};
|
||||||
backgroundImageUrl?: string;
|
backgroundImageUrl?: string;
|
||||||
|
backgroundVideoUrl?: string;
|
||||||
objects: Array<{
|
objects: Array<{
|
||||||
id: string;
|
id: string;
|
||||||
type: string;
|
type: string;
|
||||||
@@ -200,6 +201,7 @@ export function transformViewerDataToPages(data: ViewerData): PageData[] {
|
|||||||
backgroundColor: page.backgroundColor,
|
backgroundColor: page.backgroundColor,
|
||||||
backgroundGradient: page.backgroundGradient,
|
backgroundGradient: page.backgroundGradient,
|
||||||
backgroundImageUrl: page.backgroundImageUrl,
|
backgroundImageUrl: page.backgroundImageUrl,
|
||||||
|
backgroundVideoUrl: page.backgroundVideoUrl,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import type { PageData } from "@/pages/viewer/types";
|
import type { PageData } from "@/pages/viewer/types";
|
||||||
|
|
||||||
export type PageBackgroundDefaults = {
|
export type PageBackgroundDefaults = {
|
||||||
backgroundType?: "color" | "gradient" | "image";
|
backgroundType?: "color" | "gradient" | "image" | "video";
|
||||||
backgroundColor?: string;
|
backgroundColor?: string;
|
||||||
backgroundGradient?: { from: string; to: string; angle: number };
|
backgroundGradient?: { from: string; to: string; angle: number };
|
||||||
backgroundImageUrl?: string;
|
backgroundImageUrl?: string;
|
||||||
|
backgroundVideoUrl?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** ترکیب پسزمینهٔ اختصاصی صفحه با مقادیر پیشفرض سند (همان قاعدهٔ استفادهشده در BookViewer) */
|
/** ترکیب پسزمینهٔ اختصاصی صفحه با مقادیر پیشفرض سند (همان قاعدهٔ استفادهشده در BookViewer) */
|
||||||
@@ -14,5 +15,6 @@ export function resolvePageBackground(page: PageData, defaults: PageBackgroundDe
|
|||||||
backgroundColor: page.backgroundColor ?? defaults.backgroundColor ?? "#ffffff",
|
backgroundColor: page.backgroundColor ?? defaults.backgroundColor ?? "#ffffff",
|
||||||
backgroundGradient: page.backgroundGradient ?? defaults.backgroundGradient ?? { from: "#ffffff", to: "#ffffff", angle: 0 },
|
backgroundGradient: page.backgroundGradient ?? defaults.backgroundGradient ?? { from: "#ffffff", to: "#ffffff", angle: 0 },
|
||||||
backgroundImageUrl: page.backgroundImageUrl ?? defaults.backgroundImageUrl ?? "",
|
backgroundImageUrl: page.backgroundImageUrl ?? defaults.backgroundImageUrl ?? "",
|
||||||
|
backgroundVideoUrl: page.backgroundVideoUrl ?? defaults.backgroundVideoUrl ?? "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ export function extractPageMediaAssets(
|
|||||||
for (const page of pages) {
|
for (const page of pages) {
|
||||||
if (page.backgroundType === 'image') {
|
if (page.backgroundType === 'image') {
|
||||||
addImageUrl(assets, page.backgroundImageUrl);
|
addImageUrl(assets, page.backgroundImageUrl);
|
||||||
|
} else if (page.backgroundType === 'video') {
|
||||||
|
addVideoUrl(assets, page.backgroundVideoUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const element of page.elements) {
|
for (const element of page.elements) {
|
||||||
@@ -51,6 +53,8 @@ export function extractPageMediaAssets(
|
|||||||
|
|
||||||
if (documentSettings?.backgroundType === 'image') {
|
if (documentSettings?.backgroundType === 'image') {
|
||||||
addImageUrl(assets, documentSettings.backgroundImageUrl);
|
addImageUrl(assets, documentSettings.backgroundImageUrl);
|
||||||
|
} else if (documentSettings?.backgroundType === 'video') {
|
||||||
|
addVideoUrl(assets, documentSettings.backgroundVideoUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.from(assets.values());
|
return Array.from(assets.values());
|
||||||
|
|||||||
Reference in New Issue
Block a user