mask in viewer
This commit is contained in:
@@ -10,8 +10,13 @@ import {
|
||||
} from '@/pages/editor/utils/textStyle';
|
||||
import '@/pages/viewer/styles/entranceAnimations.css';
|
||||
import { mergeEntranceAnimationStyle } from '@/pages/viewer/utils/entranceAnimationStyle';
|
||||
import { getMaskImageStyle, getMaskedLayout } from '@/pages/viewer/utils/maskStyle';
|
||||
import type { EntrancePhase } from '@/pages/viewer/hooks/useBookEntranceController';
|
||||
|
||||
type RenderObjectOptions = {
|
||||
skipEntrance?: boolean;
|
||||
};
|
||||
|
||||
type BookPageProps = {
|
||||
page: PageData;
|
||||
scale?: number;
|
||||
@@ -94,7 +99,24 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
||||
flyLayoutPx,
|
||||
});
|
||||
|
||||
const renderObject = (obj: EditorObject, index: number) => {
|
||||
const renderObjectContent = (
|
||||
obj: EditorObject,
|
||||
index: number,
|
||||
options: RenderObjectOptions = {},
|
||||
) => {
|
||||
const applyStyle = (
|
||||
style: React.CSSProperties,
|
||||
styleObj: EditorObject,
|
||||
styleIndex: number,
|
||||
extra?: {
|
||||
transformOrigin?: React.CSSProperties['transformOrigin'];
|
||||
rotationDeg?: number;
|
||||
},
|
||||
) =>
|
||||
options.skipEntrance
|
||||
? style
|
||||
: withEntrance(style, styleObj, styleIndex, extra);
|
||||
|
||||
const actualStrokeWidth = obj.strokeWidth ?? 0;
|
||||
const hasStroke = actualStrokeWidth > 0;
|
||||
const baseStyle: React.CSSProperties = {
|
||||
@@ -146,7 +168,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
||||
return (
|
||||
<div
|
||||
key={obj.id || index}
|
||||
style={withEntrance(
|
||||
style={applyStyle(
|
||||
{
|
||||
...textBaseStyle,
|
||||
left: `${textLayout.leftPx}px`,
|
||||
@@ -182,7 +204,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
||||
key={obj.id || index}
|
||||
src={obj.imageUrl || ''}
|
||||
alt=""
|
||||
style={withEntrance(
|
||||
style={applyStyle(
|
||||
{
|
||||
...baseStyle,
|
||||
width: obj.width ? `${obj.width * scale}px` : 'auto',
|
||||
@@ -202,7 +224,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
||||
key={obj.id || index}
|
||||
src={obj.videoUrl || ''}
|
||||
controls
|
||||
style={withEntrance(
|
||||
style={applyStyle(
|
||||
{
|
||||
...baseStyle,
|
||||
width: obj.width ? `${obj.width * scale}px` : 'auto',
|
||||
@@ -233,7 +255,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
||||
src={obj.audioUrl || ''}
|
||||
controls
|
||||
preload="metadata"
|
||||
style={withEntrance(
|
||||
style={applyStyle(
|
||||
{
|
||||
...baseStyle,
|
||||
width: obj.width ? `${obj.width * scale}px` : `${320 * scale}px`,
|
||||
@@ -279,7 +301,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
||||
<button
|
||||
key={obj.id || index}
|
||||
type="button"
|
||||
style={withEntrance(
|
||||
style={applyStyle(
|
||||
{
|
||||
...linkStyle,
|
||||
zIndex: 9999,
|
||||
@@ -317,7 +339,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
||||
href={obj.linkUrl || '#'}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={withEntrance(
|
||||
style={applyStyle(
|
||||
{
|
||||
...linkStyle,
|
||||
zIndex: 9999,
|
||||
@@ -350,7 +372,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
||||
return (
|
||||
<div
|
||||
key={obj.id || index}
|
||||
style={withEntrance(
|
||||
style={applyStyle(
|
||||
{
|
||||
position: 'absolute',
|
||||
left: `${centerX - radius}px`,
|
||||
@@ -413,7 +435,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
||||
return (
|
||||
<svg
|
||||
key={obj.id || index}
|
||||
style={withEntrance(
|
||||
style={applyStyle(
|
||||
{
|
||||
position: 'absolute',
|
||||
left: `${centerX - radius}px`,
|
||||
@@ -471,7 +493,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
||||
return (
|
||||
<div
|
||||
key={obj.id || index}
|
||||
style={withEntrance(
|
||||
style={applyStyle(
|
||||
{
|
||||
position: 'absolute',
|
||||
left: `${centerX - radius}px`,
|
||||
@@ -510,7 +532,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
||||
zIndex: index, // برای حفظ ترتیب رندر
|
||||
};
|
||||
|
||||
return <div key={obj.id || index} style={withEntrance(shapeStyle, obj, index)} />;
|
||||
return <div key={obj.id || index} style={applyStyle(shapeStyle, obj, index)} />;
|
||||
}
|
||||
|
||||
case 'line': {
|
||||
@@ -529,7 +551,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
||||
return (
|
||||
<div
|
||||
key={obj.id || index}
|
||||
style={withEntrance(
|
||||
style={applyStyle(
|
||||
{
|
||||
position: 'absolute',
|
||||
left: `${startX}px`,
|
||||
@@ -566,7 +588,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
||||
return (
|
||||
<div
|
||||
key={obj.id || index}
|
||||
style={withEntrance(
|
||||
style={applyStyle(
|
||||
{
|
||||
position: 'absolute',
|
||||
left: `${startX}px`,
|
||||
@@ -609,7 +631,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
||||
return (
|
||||
<div
|
||||
key={obj.id || index}
|
||||
style={withEntrance(
|
||||
style={applyStyle(
|
||||
{
|
||||
...baseStyle,
|
||||
display: 'grid',
|
||||
@@ -653,6 +675,50 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
||||
}
|
||||
};
|
||||
|
||||
const renderObject = (obj: EditorObject, index: number, allElements: EditorObject[]) => {
|
||||
if (obj.isMask) return null;
|
||||
|
||||
const maskShape = obj.maskId
|
||||
? allElements.find((m) => m.id === obj.maskId)
|
||||
: null;
|
||||
|
||||
if (!maskShape) {
|
||||
return renderObjectContent(obj, index);
|
||||
}
|
||||
|
||||
const layout = getMaskedLayout(obj, maskShape, scale);
|
||||
const unionLeft = layout.left / scale;
|
||||
const unionTop = layout.top / scale;
|
||||
|
||||
const content = renderObjectContent(
|
||||
{ ...obj, x: (obj.x || 0) - unionLeft, y: (obj.y || 0) - unionTop },
|
||||
index,
|
||||
{ skipEntrance: true },
|
||||
);
|
||||
|
||||
if (!content) return null;
|
||||
|
||||
const wrapperStyle: React.CSSProperties = {
|
||||
position: 'absolute',
|
||||
left: `${layout.left}px`,
|
||||
top: `${layout.top}px`,
|
||||
width: `${layout.width}px`,
|
||||
height: `${layout.height}px`,
|
||||
overflow: 'visible',
|
||||
zIndex: index,
|
||||
...getMaskImageStyle(obj, maskShape, scale, layout),
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
key={`masked-${obj.id || index}`}
|
||||
style={withEntrance(wrapperStyle, obj, index)}
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const width = pageWidth ?? 794 * scale;
|
||||
const height = pageHeight ?? 1123 * scale;
|
||||
|
||||
@@ -706,7 +772,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
||||
/>
|
||||
)}
|
||||
<div style={{ position: 'absolute', inset: 0, zIndex: 1 }}>
|
||||
{page.elements.map((element, index) => renderObject(element, index))}
|
||||
{page.elements.map((element, index) => renderObject(element, index, page.elements))}
|
||||
</div>
|
||||
{useHardPage && showPaperShadow && <div aria-hidden className="page-paper-shadow" />}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user