border radius
This commit is contained in:
@@ -177,6 +177,7 @@ const ObjectRenderer = ({
|
|||||||
y={relativeY}
|
y={relativeY}
|
||||||
width={mask.width || 100}
|
width={mask.width || 100}
|
||||||
height={mask.height || 100}
|
height={mask.height || 100}
|
||||||
|
cornerRadius={Math.max(0, mask.borderRadius ?? 0)}
|
||||||
rotation={mask.rotation || 0}
|
rotation={mask.rotation || 0}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ const ShapeSettings = ({ selectedObject, onUpdate }: ShapeSettingsProps) => {
|
|||||||
const baseFill = selectedObject.fill ?? "#3b82f6";
|
const baseFill = selectedObject.fill ?? "#3b82f6";
|
||||||
const baseStroke = selectedObject.stroke ?? "#1e40af";
|
const baseStroke = selectedObject.stroke ?? "#1e40af";
|
||||||
const baseStrokeWidth = selectedObject.strokeWidth ?? 0;
|
const baseStrokeWidth = selectedObject.strokeWidth ?? 0;
|
||||||
|
const baseBorderRadius = selectedObject.borderRadius ?? 0;
|
||||||
|
const isSquareShape =
|
||||||
|
selectedObject.shapeType === "square" || selectedObject.shapeType === undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
@@ -65,6 +68,19 @@ const ShapeSettings = ({ selectedObject, onUpdate }: ShapeSettingsProps) => {
|
|||||||
}
|
}
|
||||||
min={0}
|
min={0}
|
||||||
/>
|
/>
|
||||||
|
{isSquareShape && (
|
||||||
|
<Input
|
||||||
|
label="گردی گوشه"
|
||||||
|
type="number"
|
||||||
|
value={baseBorderRadius}
|
||||||
|
onChange={(e) =>
|
||||||
|
onUpdate(selectedObject.id, {
|
||||||
|
borderRadius: Math.max(0, parseInt(e.target.value) || 0),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
min={0}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const RectangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shap
|
|||||||
|
|
||||||
const actualStrokeWidth = obj.strokeWidth ?? 0;
|
const actualStrokeWidth = obj.strokeWidth ?? 0;
|
||||||
const hasStroke = actualStrokeWidth > 0;
|
const hasStroke = actualStrokeWidth > 0;
|
||||||
|
const cornerRadius = Math.max(0, obj.borderRadius ?? 0);
|
||||||
|
|
||||||
// برای نمایش انتخاب: اگر strokeWidth واقعی > 0 است، از آن استفاده کن، وگرنه stroke را برای انتخاب نمایش بده
|
// برای نمایش انتخاب: اگر strokeWidth واقعی > 0 است، از آن استفاده کن، وگرنه stroke را برای انتخاب نمایش بده
|
||||||
const displayStroke = isSelected
|
const displayStroke = isSelected
|
||||||
@@ -36,6 +37,7 @@ const RectangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shap
|
|||||||
y={obj.y}
|
y={obj.y}
|
||||||
width={obj.width || 100}
|
width={obj.width || 100}
|
||||||
height={obj.height || 100}
|
height={obj.height || 100}
|
||||||
|
cornerRadius={cornerRadius}
|
||||||
fill={isMask ? "transparent" : obj.fill}
|
fill={isMask ? "transparent" : obj.fill}
|
||||||
stroke={displayStroke}
|
stroke={displayStroke}
|
||||||
strokeWidth={displayStrokeWidth}
|
strokeWidth={displayStrokeWidth}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ export type EditorObject = {
|
|||||||
scaleX?: number;
|
scaleX?: number;
|
||||||
scaleY?: number;
|
scaleY?: number;
|
||||||
shapeType?: ShapeType;
|
shapeType?: ShapeType;
|
||||||
|
borderRadius?: number;
|
||||||
tableData?: TableData;
|
tableData?: TableData;
|
||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
isMask?: boolean;
|
isMask?: boolean;
|
||||||
|
|||||||
@@ -75,12 +75,32 @@ const drawMaskShape = (
|
|||||||
// Default: Rectangle
|
// Default: Rectangle
|
||||||
const width = maskShape.width || 100;
|
const width = maskShape.width || 100;
|
||||||
const height = maskShape.height || 100;
|
const height = maskShape.height || 100;
|
||||||
ctx.fillRect(0, 0, width, height);
|
const radius = Math.max(0, maskShape.borderRadius || 0);
|
||||||
|
drawRoundedRectPath(ctx, 0, 0, width, height, radius);
|
||||||
|
ctx.fill();
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const drawRoundedRectPath = (
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
radius: number
|
||||||
|
): void => {
|
||||||
|
const normalizedRadius = Math.max(0, Math.min(radius, width / 2, height / 2));
|
||||||
|
ctx.beginPath();
|
||||||
|
if (normalizedRadius === 0) {
|
||||||
|
ctx.rect(x, y, width, height);
|
||||||
|
} else {
|
||||||
|
ctx.roundRect(x, y, width, height, normalizedRadius);
|
||||||
|
}
|
||||||
|
ctx.closePath();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* رسم یک object روی canvas context
|
* رسم یک object روی canvas context
|
||||||
* این تابع یک نسخه ساده است - در پیادهسازی واقعی باید
|
* این تابع یک نسخه ساده است - در پیادهسازی واقعی باید
|
||||||
@@ -122,10 +142,12 @@ const drawObject = async (
|
|||||||
ctx.fill();
|
ctx.fill();
|
||||||
if (ctx.lineWidth > 0) ctx.stroke();
|
if (ctx.lineWidth > 0) ctx.stroke();
|
||||||
} else {
|
} else {
|
||||||
ctx.fillRect(0, 0, object.width || 100, object.height || 100);
|
const width = object.width || 100;
|
||||||
if (ctx.lineWidth > 0) {
|
const height = object.height || 100;
|
||||||
ctx.strokeRect(0, 0, object.width || 100, object.height || 100);
|
const radius = Math.max(0, object.borderRadius || 0);
|
||||||
}
|
drawRoundedRectPath(ctx, 0, 0, width, height, radius);
|
||||||
|
ctx.fill();
|
||||||
|
if (ctx.lineWidth > 0) ctx.stroke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// برای text objects
|
// برای text objects
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderObject = (obj: EditorObject, index: number) => {
|
const renderObject = (obj: EditorObject, index: number) => {
|
||||||
|
const actualStrokeWidth = obj.strokeWidth ?? 0;
|
||||||
|
const hasStroke = actualStrokeWidth > 0;
|
||||||
const baseStyle: React.CSSProperties = {
|
const baseStyle: React.CSSProperties = {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: `${(obj.x || 0) * scale}px`,
|
left: `${(obj.x || 0) * scale}px`,
|
||||||
@@ -220,8 +222,8 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
|||||||
height: `${radius * 2}px`,
|
height: `${radius * 2}px`,
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
backgroundColor: obj.fill || 'transparent',
|
backgroundColor: obj.fill || 'transparent',
|
||||||
border: obj.stroke
|
border: hasStroke && obj.stroke
|
||||||
? `${(obj.strokeWidth || 1) * scale}px solid ${obj.stroke}`
|
? `${actualStrokeWidth * scale}px solid ${obj.stroke}`
|
||||||
: 'none',
|
: 'none',
|
||||||
opacity: obj.opacity ?? 1,
|
opacity: obj.opacity ?? 1,
|
||||||
transform: obj.rotation ? `rotate(${obj.rotation}deg)` : undefined,
|
transform: obj.rotation ? `rotate(${obj.rotation}deg)` : undefined,
|
||||||
@@ -261,7 +263,7 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
|||||||
const bottomLeftX = halfSize - halfWidth;
|
const bottomLeftX = halfSize - halfWidth;
|
||||||
const bottomRightX = halfSize + halfWidth;
|
const bottomRightX = halfSize + halfWidth;
|
||||||
|
|
||||||
const strokeWidth = obj.stroke ? (obj.strokeWidth || 1) * scale : 0;
|
const strokeWidth = hasStroke && obj.stroke ? actualStrokeWidth * scale : 0;
|
||||||
const fillColor = obj.fill || '#000000';
|
const fillColor = obj.fill || '#000000';
|
||||||
const strokeColor = obj.stroke || 'transparent';
|
const strokeColor = obj.stroke || 'transparent';
|
||||||
|
|
||||||
@@ -330,8 +332,9 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
|||||||
width: `${(obj.width || 100) * scale}px`,
|
width: `${(obj.width || 100) * scale}px`,
|
||||||
height: `${(obj.height || 100) * scale}px`,
|
height: `${(obj.height || 100) * scale}px`,
|
||||||
backgroundColor: obj.fill || 'transparent',
|
backgroundColor: obj.fill || 'transparent',
|
||||||
border: obj.stroke
|
borderRadius: `${Math.max(0, (obj.borderRadius || 0) * scale)}px`,
|
||||||
? `${(obj.strokeWidth || 1) * scale}px solid ${obj.stroke}`
|
border: hasStroke && obj.stroke
|
||||||
|
? `${actualStrokeWidth * scale}px solid ${obj.stroke}`
|
||||||
: 'none',
|
: 'none',
|
||||||
zIndex: index, // برای حفظ ترتیب رندر
|
zIndex: index, // برای حفظ ترتیب رندر
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type ViewerDataPage = {
|
|||||||
stroke?: string;
|
stroke?: string;
|
||||||
strokeWidth?: number;
|
strokeWidth?: number;
|
||||||
shapeType?: string;
|
shapeType?: string;
|
||||||
|
borderRadius?: number;
|
||||||
imageUrl?: string;
|
imageUrl?: string;
|
||||||
videoUrl?: string;
|
videoUrl?: string;
|
||||||
linkUrl?: string;
|
linkUrl?: string;
|
||||||
@@ -93,6 +94,9 @@ export function transformViewerDataToPages(data: ViewerData): PageData[] {
|
|||||||
if (obj.type === "rectangle" && obj.shapeType) {
|
if (obj.type === "rectangle" && obj.shapeType) {
|
||||||
baseObject.shapeType = obj.shapeType as EditorObject["shapeType"];
|
baseObject.shapeType = obj.shapeType as EditorObject["shapeType"];
|
||||||
}
|
}
|
||||||
|
if (obj.type === "rectangle" && obj.borderRadius !== undefined) {
|
||||||
|
baseObject.borderRadius = obj.borderRadius;
|
||||||
|
}
|
||||||
|
|
||||||
if (obj.type === "image" && obj.imageUrl) {
|
if (obj.type === "image" && obj.imageUrl) {
|
||||||
baseObject.imageUrl = obj.imageUrl;
|
baseObject.imageUrl = obj.imageUrl;
|
||||||
|
|||||||
Reference in New Issue
Block a user