diff --git a/blocksuite/affine/gfx/shape/src/element-renderer/shape-dom/index.ts b/blocksuite/affine/gfx/shape/src/element-renderer/shape-dom/index.ts index 5a9078d28..4e9c314eb 100644 --- a/blocksuite/affine/gfx/shape/src/element-renderer/shape-dom/index.ts +++ b/blocksuite/affine/gfx/shape/src/element-renderer/shape-dom/index.ts @@ -6,10 +6,16 @@ import { manageClassNames, setStyles } from './utils'; function applyShapeSpecificStyles( model: ShapeElementModel, - element: HTMLElement + element: HTMLElement, + zoom: number ) { if (model.shapeType === 'rect') { - element.style.borderRadius = `${model.radius ?? 0}px`; + const w = model.w * zoom; + const h = model.h * zoom; + const r = model.radius ?? 0; + const borderRadius = + r < 1 ? `${Math.min(w * r, h * r)}px` : `${r * zoom}px`; + element.style.borderRadius = borderRadius; } else if (model.shapeType === 'ellipse') { element.style.borderRadius = '50%'; } else { @@ -20,11 +26,12 @@ function applyShapeSpecificStyles( function applyBorderStyles( model: ShapeElementModel, element: HTMLElement, - strokeColor: string + strokeColor: string, + zoom: number ) { element.style.border = model.strokeStyle !== 'none' - ? `${model.strokeWidth}px ${model.strokeStyle === 'dash' ? 'dashed' : 'solid'} ${strokeColor}` + ? `${model.strokeWidth * zoom}px ${model.strokeStyle === 'dash' ? 'dashed' : 'solid'} ${strokeColor}` : 'none'; } @@ -85,11 +92,11 @@ export const shapeDomRenderer = ( element.style.width = `${model.w * zoom}px`; element.style.height = `${model.h * zoom}px`; - applyShapeSpecificStyles(model, element); + applyShapeSpecificStyles(model, element, zoom); element.style.backgroundColor = model.filled ? fillColor : 'transparent'; - applyBorderStyles(model, element, strokeColor); + applyBorderStyles(model, element, strokeColor, zoom); applyTransformStyles(model, element); element.style.boxSizing = 'border-box'; diff --git a/blocksuite/integration-test/src/__tests__/edgeless/shape-dom.spec.ts b/blocksuite/integration-test/src/__tests__/edgeless/shape-dom.spec.ts index ce6580afd..b0abb469d 100644 --- a/blocksuite/integration-test/src/__tests__/edgeless/shape-dom.spec.ts +++ b/blocksuite/integration-test/src/__tests__/edgeless/shape-dom.spec.ts @@ -1,6 +1,7 @@ import { DomRenderer } from '@blocksuite/affine-block-surface'; import { beforeEach, describe, expect, test } from 'vitest'; +import { wait } from '../utils/common.js'; import { getSurface } from '../utils/edgeless.js'; import { setupEditor } from '../utils/setup.js'; @@ -40,6 +41,27 @@ describe('Shape rendering with DOM renderer', () => { expect(shapeElement).toBeInstanceOf(HTMLElement); }); + test('should correctly apply percentage-based border radius', async () => { + const surfaceView = getSurface(window.doc, window.editor); + const surfaceModel = surfaceView.model; + const shapeProps = { + type: 'shape', + subType: 'rectangle', + xywh: '[150, 150, 80, 60]', // width: 80, height: 60 + radius: 0.1, // 10% of min(width, height) = 10% of 60 = 6 + fill: '#ff0000', + stroke: '#000000', + }; + const shapeId = surfaceModel.addElement(shapeProps); + await wait(100); + const shapeElement = surfaceView?.renderRoot.querySelector( + `[data-element-id="${shapeId}"]` + ); + + expect(shapeElement).not.toBeNull(); + expect(shapeElement?.style.borderRadius).toBe('6px'); + }); + test('should remove shape DOM node when element is deleted', async () => { const surfaceView = getSurface(window.doc, window.editor); const surfaceModel = surfaceView.model;