mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-17 14:27:02 +08:00
refactor(editor): add dom renderer entry for canvas element (#12149)
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
import { DomElementRendererExtension } from '@blocksuite/affine-block-surface';
|
||||
|
||||
import { shapeDomRenderer } from './shape-dom/index.js';
|
||||
|
||||
/**
|
||||
* Extension to register the DOM-based renderer for 'shape' elements.
|
||||
*/
|
||||
export const ShapeDomRendererExtension = DomElementRendererExtension(
|
||||
'shape',
|
||||
shapeDomRenderer
|
||||
);
|
||||
@@ -0,0 +1,100 @@
|
||||
import type { DomRenderer } from '@blocksuite/affine-block-surface';
|
||||
import type { ShapeElementModel } from '@blocksuite/affine-model';
|
||||
import { DefaultTheme } from '@blocksuite/affine-model';
|
||||
|
||||
import { manageClassNames, setStyles } from './utils';
|
||||
|
||||
function applyShapeSpecificStyles(
|
||||
model: ShapeElementModel,
|
||||
element: HTMLElement
|
||||
) {
|
||||
if (model.shapeType === 'rect') {
|
||||
element.style.borderRadius = `${model.radius ?? 0}px`;
|
||||
} else if (model.shapeType === 'ellipse') {
|
||||
element.style.borderRadius = '50%';
|
||||
} else {
|
||||
element.style.borderRadius = '';
|
||||
}
|
||||
}
|
||||
|
||||
function applyBorderStyles(
|
||||
model: ShapeElementModel,
|
||||
element: HTMLElement,
|
||||
strokeColor: string
|
||||
) {
|
||||
element.style.border =
|
||||
model.strokeStyle !== 'none'
|
||||
? `${model.strokeWidth}px ${model.strokeStyle === 'dash' ? 'dashed' : 'solid'} ${strokeColor}`
|
||||
: 'none';
|
||||
}
|
||||
|
||||
function applyTransformStyles(model: ShapeElementModel, element: HTMLElement) {
|
||||
if (model.rotate && model.rotate !== 0) {
|
||||
setStyles(element, {
|
||||
transform: `rotate(${model.rotate}deg)`,
|
||||
transformOrigin: 'center',
|
||||
});
|
||||
} else {
|
||||
setStyles(element, {
|
||||
transform: '',
|
||||
transformOrigin: '',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function applyShadowStyles(
|
||||
model: ShapeElementModel,
|
||||
element: HTMLElement,
|
||||
renderer: DomRenderer
|
||||
) {
|
||||
if (model.shadow) {
|
||||
const { offsetX, offsetY, blur, color } = model.shadow;
|
||||
setStyles(element, {
|
||||
boxShadow: `${offsetX}px ${offsetY}px ${blur}px ${renderer.getColorValue(color)}`,
|
||||
});
|
||||
} else {
|
||||
setStyles(element, { boxShadow: '' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a ShapeElementModel to a given HTMLElement using DOM properties.
|
||||
* This function is intended to be registered via the DomElementRendererExtension.
|
||||
*
|
||||
* @param model - The shape element model containing rendering properties.
|
||||
* @param element - The HTMLElement to apply the shape's styles to.
|
||||
* @param renderer - The main DOMRenderer instance, providing access to viewport and color utilities.
|
||||
*/
|
||||
export const shapeDomRenderer = (
|
||||
model: ShapeElementModel,
|
||||
element: HTMLElement,
|
||||
renderer: DomRenderer
|
||||
): void => {
|
||||
const { zoom } = renderer.viewport;
|
||||
const fillColor = renderer.getColorValue(
|
||||
model.fillColor,
|
||||
DefaultTheme.shapeFillColor,
|
||||
true
|
||||
);
|
||||
const strokeColor = renderer.getColorValue(
|
||||
model.strokeColor,
|
||||
DefaultTheme.shapeStrokeColor,
|
||||
true
|
||||
);
|
||||
|
||||
element.style.width = `${model.w * zoom}px`;
|
||||
element.style.height = `${model.h * zoom}px`;
|
||||
|
||||
applyShapeSpecificStyles(model, element);
|
||||
|
||||
element.style.backgroundColor = model.filled ? fillColor : 'transparent';
|
||||
|
||||
applyBorderStyles(model, element, strokeColor);
|
||||
applyTransformStyles(model, element);
|
||||
|
||||
element.style.boxSizing = 'border-box';
|
||||
element.style.zIndex = renderer.layerManager.getZIndex(model).toString();
|
||||
|
||||
manageClassNames(model, element);
|
||||
applyShadowStyles(model, element, renderer);
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
import type { ShapeElementModel } from '@blocksuite/affine-model';
|
||||
|
||||
/**
|
||||
* Utility to manage a class on an element, tracking the previous class via dataset.
|
||||
* If the new class is different from the previous one (stored in dataset),
|
||||
* the previous class is removed. The new class is added if not already present.
|
||||
* The dataset is then updated with the new class name.
|
||||
*
|
||||
* @param element The HTMLElement to update.
|
||||
* @param newClassName The new class name to apply.
|
||||
* @param datasetKeyForPreviousClass The key in `element.dataset` used to store the previous class name.
|
||||
*/
|
||||
function updateClass(
|
||||
element: HTMLElement,
|
||||
newClassName: string,
|
||||
datasetKeyForPreviousClass: string
|
||||
): void {
|
||||
const previousClassName = element.dataset[datasetKeyForPreviousClass];
|
||||
|
||||
if (previousClassName && previousClassName !== newClassName) {
|
||||
element.classList.remove(previousClassName);
|
||||
}
|
||||
|
||||
if (!element.classList.contains(newClassName)) {
|
||||
element.classList.add(newClassName);
|
||||
}
|
||||
|
||||
element.dataset[datasetKeyForPreviousClass] = newClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility to set multiple CSS styles on an HTMLElement.
|
||||
*
|
||||
* @param element The HTMLElement to apply styles to.
|
||||
* @param styles An object where keys are camelCased CSS property names and values are their string values.
|
||||
*/
|
||||
export function setStyles(
|
||||
element: HTMLElement,
|
||||
styles: Record<string, string>
|
||||
): void {
|
||||
for (const property in styles) {
|
||||
if (Object.prototype.hasOwnProperty.call(styles, property)) {
|
||||
// Using `any` for `element.style` index is a common practice for dynamic style assignment.
|
||||
// Assumes `property` is a valid camelCased CSS property.
|
||||
(element.style as any)[property] = styles[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function manageClassNames(
|
||||
model: ShapeElementModel,
|
||||
element: HTMLElement
|
||||
) {
|
||||
const currentShapeTypeClass = `shape-${model.shapeType}`;
|
||||
const currentShapeStyleClass = `shape-style-${model.shapeStyle.toLowerCase()}`;
|
||||
|
||||
updateClass(element, currentShapeTypeClass, 'prevShapeTypeClass');
|
||||
updateClass(element, currentShapeStyleClass, 'prevShapeStyleClass');
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
HighlighterElementRendererExtension,
|
||||
ShapeElementRendererExtension,
|
||||
} from './element-renderer';
|
||||
import { ShapeDomRendererExtension } from './element-renderer/shape-dom';
|
||||
import { ShapeElementView, ShapeViewInteraction } from './element-view';
|
||||
import { ShapeTool } from './shape-tool';
|
||||
import { shapeSeniorTool, shapeToolbarExtension } from './toolbar';
|
||||
@@ -25,6 +26,7 @@ export class ShapeViewExtension extends ViewExtensionProvider {
|
||||
if (this.isEdgeless(context.scope)) {
|
||||
context.register(HighlighterElementRendererExtension);
|
||||
context.register(ShapeElementRendererExtension);
|
||||
context.register(ShapeDomRendererExtension);
|
||||
context.register(ShapeElementView);
|
||||
context.register(ShapeTool);
|
||||
context.register(shapeSeniorTool);
|
||||
|
||||
Reference in New Issue
Block a user