grediant in a shape

This commit is contained in:
hamid zarghami
2026-05-09 09:50:13 +03:30
parent 0229a1355d
commit b513ecb33f
16 changed files with 378 additions and 15 deletions
+39 -6
View File
@@ -1,6 +1,7 @@
import { forwardRef } from 'react';
import { type PageData } from '../types';
import type { EditorObject } from '@/pages/editor/store/editorStore';
import { toCssLinearGradient } from '@/pages/editor/utils/gradient';
type BookPageProps = {
page: PageData;
@@ -8,8 +9,13 @@ type BookPageProps = {
pageWidth?: number;
pageHeight?: number;
onLinkClick?: (linkUrl: string) => void;
backgroundType?: 'color' | 'image';
backgroundType?: 'color' | 'gradient' | 'image';
backgroundColor?: string;
backgroundGradient?: {
from: string;
to: string;
angle: number;
};
backgroundImageUrl?: string;
};
@@ -21,7 +27,7 @@ type BookPageProps = {
* نیاز به forwardRef دارد تا react-pageflip بتواند ref را مدیریت کند
*/
const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
({ page, scale = 1, pageWidth, pageHeight, onLinkClick, backgroundType, backgroundColor, backgroundImageUrl }, ref) => {
({ page, scale = 1, pageWidth, pageHeight, onLinkClick, backgroundType, backgroundColor, backgroundGradient, backgroundImageUrl }, ref) => {
// تابع برای تبدیل opacity به رنگ (مثل editor)
// در editor، getColorWithOpacity انتظار opacity 0-100 دارد
// در dataTransformer، opacity از 0-1 به 0-100 تبدیل شده است
@@ -69,6 +75,11 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
return null;
}
const objectFillStyle: React.CSSProperties =
obj.fillType === 'gradient' && obj.gradient
? { backgroundImage: toCssLinearGradient(obj.gradient) }
: { backgroundColor: obj.fill || 'transparent' };
switch (obj.type) {
case 'text': {
// برای text، opacity در رنگ اعمال می‌شود، پس باید opacity را از baseStyle حذف کنیم
@@ -234,7 +245,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
width: `${radius * 2}px`,
height: `${radius * 2}px`,
borderRadius: '50%',
backgroundColor: obj.fill || 'transparent',
...objectFillStyle,
border: hasStroke && obj.stroke
? `${actualStrokeWidth * scale}px solid ${obj.stroke}`
: 'none',
@@ -280,6 +291,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
const strokeWidth = hasStroke && obj.stroke ? actualStrokeWidth * scale : 0;
const fillColor = obj.fill || '#000000';
const strokeColor = obj.stroke || 'transparent';
const gradientId = `triangle-grad-${obj.id}-${index}`;
return (
<svg
@@ -298,9 +310,25 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
}}
viewBox={`0 0 ${size} ${size}`}
>
{obj.fillType === 'gradient' && obj.gradient ? (
<defs>
<linearGradient
id={gradientId}
gradientUnits="userSpaceOnUse"
x1="0"
y1="0"
x2={size}
y2={size}
gradientTransform={`rotate(${obj.gradient.angle}, ${size / 2}, ${size / 2})`}
>
<stop offset="0%" stopColor={obj.gradient.from} />
<stop offset="100%" stopColor={obj.gradient.to} />
</linearGradient>
</defs>
) : null}
<polygon
points={`${topX},${topY} ${bottomLeftX},${bottomY} ${bottomRightX},${bottomY}`}
fill={fillColor}
fill={obj.fillType === 'gradient' && obj.gradient ? `url(#${gradientId})` : fillColor}
stroke={strokeColor}
strokeWidth={strokeWidth}
/>
@@ -328,7 +356,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
width: `${radius * 2}px`,
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%)',
backgroundColor: obj.fill || 'transparent',
...objectFillStyle,
border: obj.stroke
? `${(obj.strokeWidth || 1) * scale}px solid ${obj.stroke}`
: 'none',
@@ -346,7 +374,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
...baseStyle,
width: `${(obj.width || 100) * scale}px`,
height: `${(obj.height || 100) * scale}px`,
backgroundColor: obj.fill || 'transparent',
...objectFillStyle,
borderRadius: `${Math.max(0, (obj.borderRadius || 0) * scale)}px`,
border: hasStroke && obj.stroke
? `${actualStrokeWidth * scale}px solid ${obj.stroke}`
@@ -489,6 +517,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
const effectiveBackgroundType = backgroundType ?? page.backgroundType ?? 'color';
const effectiveBackgroundColor = backgroundColor ?? page.backgroundColor ?? '#ffffff';
const effectiveBackgroundGradient = backgroundGradient ?? page.backgroundGradient;
const effectiveBackgroundImageUrl = backgroundImageUrl ?? page.backgroundImageUrl ?? '';
const bgStyle: React.CSSProperties =
@@ -499,6 +528,10 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
}
: effectiveBackgroundType === 'gradient' && effectiveBackgroundGradient
? {
backgroundImage: toCssLinearGradient(effectiveBackgroundGradient),
}
: { backgroundColor: effectiveBackgroundColor };
return (
@@ -117,6 +117,7 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
const displayStyle = documentSettings?.displayStyle ?? 'double';
const backgroundType = documentSettings?.backgroundType ?? 'color';
const backgroundColor = documentSettings?.backgroundColor ?? '#ffffff';
const backgroundGradient = documentSettings?.backgroundGradient;
const backgroundImageUrl = documentSettings?.backgroundImageUrl ?? '';
useEffect(() => {
@@ -486,6 +487,7 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
onLinkClick={handleLinkClick}
backgroundType={page.backgroundType ?? backgroundType}
backgroundColor={page.backgroundColor ?? backgroundColor}
backgroundGradient={page.backgroundGradient ?? backgroundGradient}
backgroundImageUrl={page.backgroundImageUrl ?? backgroundImageUrl}
/>
))}
+6 -1
View File
@@ -5,8 +5,13 @@ export type PageData = {
width: number;
height: number;
elements: EditorObject[];
backgroundType?: "color" | "image";
backgroundType?: "color" | "gradient" | "image";
backgroundColor?: string;
backgroundGradient?: {
from: string;
to: string;
angle: number;
};
backgroundImageUrl?: string;
};
+15 -1
View File
@@ -4,8 +4,13 @@ import type { PageData } from "../types";
type ViewerDataPage = {
id: string;
name: string;
backgroundType?: "color" | "image";
backgroundType?: "color" | "gradient" | "image";
backgroundColor?: string;
backgroundGradient?: {
from: string;
to: string;
angle: number;
};
backgroundImageUrl?: string;
objects: Array<{
id: string;
@@ -21,6 +26,12 @@ type ViewerDataPage = {
lineHeight?: number;
textAlign?: "left" | "center" | "right";
fill?: string;
fillType?: "solid" | "gradient";
gradient?: {
from: string;
to: string;
angle: number;
};
stroke?: string;
strokeWidth?: number;
shapeType?: string;
@@ -82,6 +93,8 @@ export function transformViewerDataToPages(data: ViewerData): PageData[] {
// baseObject.y = obj.y + (obj.height || 100) / 2;
// اما فعلاً JSON به صورت مرکز است، پس تبدیل نمی‌کنیم
if (obj.fill !== undefined) baseObject.fill = obj.fill;
if (obj.fillType !== undefined) baseObject.fillType = obj.fillType;
if (obj.gradient !== undefined) baseObject.gradient = obj.gradient;
if (obj.stroke !== undefined) baseObject.stroke = obj.stroke;
if (obj.strokeWidth !== undefined)
baseObject.strokeWidth = obj.strokeWidth;
@@ -131,6 +144,7 @@ export function transformViewerDataToPages(data: ViewerData): PageData[] {
elements: objects,
backgroundType: page.backgroundType,
backgroundColor: page.backgroundColor,
backgroundGradient: page.backgroundGradient,
backgroundImageUrl: page.backgroundImageUrl,
};
});