mirror of
https://github.com/idrawjs/idraw
synced 2026-05-24 10:08:34 +00:00
feat: optimize render and events
This commit is contained in:
parent
320286355d
commit
ef7e53ee4c
26 changed files with 420 additions and 196 deletions
|
|
@ -8,7 +8,7 @@ import type {
|
|||
BoardWatcherEventMap,
|
||||
ViewSizeInfo,
|
||||
PointSize,
|
||||
BoardExtendEvent,
|
||||
BoardExtendEventMap,
|
||||
UtilEventEmitter
|
||||
} from '@idraw/types';
|
||||
import { Calculator } from './lib/calculator';
|
||||
|
|
@ -23,7 +23,7 @@ interface BoardMiddlewareMapItem {
|
|||
middlewareObject: BoardMiddlewareObject;
|
||||
}
|
||||
|
||||
export class Board<T extends BoardExtendEvent = BoardExtendEvent> {
|
||||
export class Board<T extends BoardExtendEventMap = BoardExtendEventMap> {
|
||||
#opts: BoardOptions;
|
||||
#middlewareMap: WeakMap<BoardMiddleware, BoardMiddlewareMapItem> = new WeakMap();
|
||||
#middlewares: BoardMiddleware[] = [];
|
||||
|
|
@ -120,6 +120,10 @@ export class Board<T extends BoardExtendEvent = BoardExtendEvent> {
|
|||
this.#watcher.on('scrollY', this.#handleScrollY.bind(this));
|
||||
this.#watcher.on('resize', this.#handleResize.bind(this));
|
||||
this.#watcher.on('doubleClick', this.#handleDoubleClick.bind(this));
|
||||
|
||||
this.#renderer.on('load', () => {
|
||||
this.#eventHub.trigger('loadResource');
|
||||
});
|
||||
}
|
||||
|
||||
#handlePointStart(e: BoardWatcherEventMap['pointStart']) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { Data, PointSize, CoreOptions, BoardMiddleware, ViewSizeInfo, CoreEvent, ViewScaleInfo, LoadItemMap } from '@idraw/types';
|
||||
import type { Data, PointSize, CoreOptions, BoardMiddleware, ViewSizeInfo, CoreEventMap, ViewScaleInfo, LoadItemMap } from '@idraw/types';
|
||||
import { Board } from '@idraw/board';
|
||||
import { createBoardContent, validateElements } from '@idraw/util';
|
||||
import { Cursor } from './lib/cursor';
|
||||
|
|
@ -8,10 +8,10 @@ export { MiddlewareSelector, middlewareEventSelect, middlewareEventSelectClear }
|
|||
export { MiddlewareScroller } from './middleware/scroller';
|
||||
export { MiddlewareScaler, middlewareEventScale } from './middleware/scaler';
|
||||
export { MiddlewareRuler, middlewareEventRuler } from './middleware/ruler';
|
||||
export { MiddlewareTextEditor, middlewareEventTextEdit } from './middleware/text-editor';
|
||||
export { MiddlewareTextEditor, middlewareEventTextEdit, middlewareEventTextChange } from './middleware/text-editor';
|
||||
export { MiddlewareDragger } from './middleware/dragger';
|
||||
|
||||
export class Core<E extends CoreEvent = CoreEvent> {
|
||||
export class Core<E extends CoreEventMap = CoreEventMap> {
|
||||
#board: Board<E>;
|
||||
// #opts: CoreOptions;
|
||||
#canvas: HTMLCanvasElement;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import type { UtilEventEmitter, CoreEvent } from '@idraw/types';
|
||||
import type { UtilEventEmitter, CoreEventMap } from '@idraw/types';
|
||||
import { limitAngle, loadImage, parseAngleToRadian } from '@idraw/util';
|
||||
import { CURSOR, CURSOR_RESIZE, CURSOR_DRAG_DEFAULT, CURSOR_DRAG_ACTIVE, CURSOR_RESIZE_ROTATE } from './cursor-image';
|
||||
|
||||
export class Cursor {
|
||||
#eventHub: UtilEventEmitter<CoreEvent>;
|
||||
#eventHub: UtilEventEmitter<CoreEventMap>;
|
||||
#container: HTMLDivElement;
|
||||
#cursorType: 'default' | string | null = null;
|
||||
#resizeCursorBaseImage: HTMLImageElement | null = null;
|
||||
|
|
@ -17,7 +17,7 @@ export class Cursor {
|
|||
constructor(
|
||||
container: HTMLDivElement,
|
||||
opts: {
|
||||
eventHub: UtilEventEmitter<CoreEvent>;
|
||||
eventHub: UtilEventEmitter<CoreEventMap>;
|
||||
}
|
||||
) {
|
||||
this.#container = container;
|
||||
|
|
@ -78,7 +78,7 @@ export class Cursor {
|
|||
}
|
||||
}
|
||||
|
||||
#setCursorResize(e: CoreEvent['cursor']) {
|
||||
#setCursorResize(e: CoreEventMap['cursor']) {
|
||||
let totalAngle = 0;
|
||||
if (e.type === 'resize-top') {
|
||||
totalAngle += 0;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { BoardMiddleware, CoreEvent, Point } from '@idraw/types';
|
||||
import type { BoardMiddleware, CoreEventMap, Point } from '@idraw/types';
|
||||
|
||||
const key = 'DRAG';
|
||||
const keyPrevPoint = Symbol(`${key}_prevPoint`);
|
||||
|
|
@ -7,7 +7,7 @@ type DraggerSharedStorage = {
|
|||
[keyPrevPoint]: Point | null;
|
||||
};
|
||||
|
||||
export const MiddlewareDragger: BoardMiddleware<DraggerSharedStorage, CoreEvent> = (opts) => {
|
||||
export const MiddlewareDragger: BoardMiddleware<DraggerSharedStorage, CoreEventMap> = (opts) => {
|
||||
const { eventHub, sharer, viewer } = opts;
|
||||
let isDragging = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import type { BoardMiddleware, CoreEvent } from '@idraw/types';
|
||||
import type { BoardMiddleware, CoreEventMap } from '@idraw/types';
|
||||
import { getViewScaleInfoFromSnapshot, getViewSizeInfoFromSnapshot } from '@idraw/util';
|
||||
import { drawRulerBackground, drawXRuler, drawYRuler, calcXRulerScaleList, calcYRulerScaleList, drawUnderGrid } from './util';
|
||||
|
||||
export const middlewareEventRuler = '@middleware/show-ruler';
|
||||
|
||||
export const MiddlewareRuler: BoardMiddleware<Record<string, any>, CoreEvent> = (opts) => {
|
||||
export const MiddlewareRuler: BoardMiddleware<Record<string, any>, CoreEventMap> = (opts) => {
|
||||
const { boardContent, viewer, eventHub } = opts;
|
||||
const { helperContext, underContext } = boardContent;
|
||||
let show: boolean = true;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import type { BoardMiddleware, CoreEvent } from '@idraw/types';
|
||||
import type { BoardMiddleware, CoreEventMap } from '@idraw/types';
|
||||
import { formatNumber } from '@idraw/util';
|
||||
|
||||
export const middlewareEventScale = '@middleware/scale';
|
||||
|
||||
export const MiddlewareScaler: BoardMiddleware<Record<string, any>, CoreEvent> = (opts) => {
|
||||
export const MiddlewareScaler: BoardMiddleware<Record<string, any>, CoreEventMap> = (opts) => {
|
||||
const { viewer, sharer, eventHub } = opts;
|
||||
const maxScale = 50;
|
||||
const minScale = 0.05;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export const keySelectedElementListVertexes = Symbol(`${key}_selectedElementList
|
|||
export const keySelectedElementController = Symbol(`${key}_selectedElementController`); // ElementSizeController
|
||||
export const keyGroupQueue = Symbol(`${key}_groupQueue`); // Array<Element<'group'>> | []
|
||||
export const keyGroupQueueVertexesList = Symbol(`${key}_groupQueueVertexesList`); // Array<ViewRectVertexes> | []
|
||||
export const keyIsMoving = Symbol(`${key}_isMoving`); // boolean | null
|
||||
|
||||
export const keyDebugElemCenter = Symbol(`${key}_debug_elemCenter`);
|
||||
export const keyDebugStartVertical = Symbol(`${key}_debug_startVertical`);
|
||||
|
|
|
|||
69
packages/core/src/middleware/selector/draw-auxiliary.ts
Normal file
69
packages/core/src/middleware/selector/draw-auxiliary.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import type { ViewContext2D, ViewRectVertexes, Element } from '@idraw/types';
|
||||
import { drawLine } from './draw-base';
|
||||
|
||||
const auxiliaryColor = '#f7276e';
|
||||
// const auxiliaryTextColor = '#FFFFFF';
|
||||
// const fontSize = 12;
|
||||
// const fontFamily = 'Consolas,Monaco,monospace';
|
||||
// const fontWeight = 600;
|
||||
|
||||
// function drawLabel(
|
||||
// ctx,
|
||||
// opts: {
|
||||
// text: string;
|
||||
// start: PointSize;
|
||||
// textColor: string;
|
||||
// background: string;
|
||||
// }
|
||||
// ) {
|
||||
// // TODO
|
||||
// }
|
||||
|
||||
export function drawSizeAuxiliaryLines(
|
||||
ctx: ViewContext2D,
|
||||
opts: {
|
||||
vertexes: ViewRectVertexes;
|
||||
element: Element | null;
|
||||
groupQueue: Element<'group'>[];
|
||||
}
|
||||
) {
|
||||
const { vertexes, element, groupQueue } = opts;
|
||||
const point = vertexes[0];
|
||||
const lineOpts = {
|
||||
borderColor: auxiliaryColor,
|
||||
borderWidth: 1,
|
||||
lineDash: []
|
||||
// lineDash: [4, 4]
|
||||
};
|
||||
if (groupQueue.length > 0) {
|
||||
// TODO
|
||||
} else {
|
||||
if (!element?.angle) {
|
||||
drawLine(ctx, { x: point.x, y: 0 }, point, lineOpts);
|
||||
drawLine(ctx, { x: 0, y: point.y }, point, lineOpts);
|
||||
|
||||
// {
|
||||
// const xNum = `${element?.x}`;
|
||||
// const w = xNum.length * fontSize;
|
||||
// const h = fontSize;
|
||||
// const x = point.x / 2 - xNum.length * fontSize;
|
||||
// const y = point.y + fontSize / 2;
|
||||
// ctx.$setFont({
|
||||
// fontSize,
|
||||
// fontFamily,
|
||||
// fontWeight
|
||||
// });
|
||||
// ctx.moveTo(x, y);
|
||||
// ctx.lineTo(x + w, y);
|
||||
// ctx.lineTo(x + w, y + h);
|
||||
// ctx.lineTo(x, y + h);
|
||||
// ctx.lineTo(x, y);
|
||||
// ctx.fillStyle = auxiliaryColor;
|
||||
// ctx.fill();
|
||||
|
||||
// ctx.fillStyle = auxiliaryTextColor;
|
||||
// ctx.fillText(xNum, x, y, w);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
109
packages/core/src/middleware/selector/draw-base.ts
Normal file
109
packages/core/src/middleware/selector/draw-base.ts
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import type { PointSize, ViewContext2D, ViewRectVertexes } from '@idraw/types';
|
||||
|
||||
export function drawVertexes(
|
||||
ctx: ViewContext2D,
|
||||
vertexes: ViewRectVertexes,
|
||||
opts: { borderColor: string; borderWidth: number; background: string; lineDash: number[] }
|
||||
) {
|
||||
const { borderColor, borderWidth, background, lineDash } = opts;
|
||||
ctx.setLineDash([]);
|
||||
ctx.lineWidth = borderWidth;
|
||||
ctx.strokeStyle = borderColor;
|
||||
ctx.fillStyle = background;
|
||||
ctx.setLineDash(lineDash);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(vertexes[0].x, vertexes[0].y);
|
||||
ctx.lineTo(vertexes[1].x, vertexes[1].y);
|
||||
ctx.lineTo(vertexes[2].x, vertexes[2].y);
|
||||
ctx.lineTo(vertexes[3].x, vertexes[3].y);
|
||||
ctx.lineTo(vertexes[0].x, vertexes[0].y);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
export function drawLine(ctx: ViewContext2D, start: PointSize, end: PointSize, opts: { borderColor: string; borderWidth: number; lineDash: number[] }) {
|
||||
const { borderColor, borderWidth, lineDash } = opts;
|
||||
ctx.setLineDash([]);
|
||||
ctx.lineWidth = borderWidth;
|
||||
ctx.strokeStyle = borderColor;
|
||||
ctx.setLineDash(lineDash);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(start.x, start.y);
|
||||
ctx.lineTo(end.x, end.y);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
export function drawCircleController(
|
||||
ctx: ViewContext2D,
|
||||
circleCenter: PointSize,
|
||||
opts: { borderColor: string; borderWidth: number; background: string; lineDash: number[]; size: number }
|
||||
) {
|
||||
const { size, borderColor, borderWidth, background } = opts;
|
||||
const center = circleCenter;
|
||||
const r = size / 2;
|
||||
|
||||
const a = r;
|
||||
const b = r;
|
||||
// 'content-box'
|
||||
|
||||
if (a >= 0 && b >= 0) {
|
||||
// draw border
|
||||
if (typeof borderWidth === 'number' && borderWidth > 0) {
|
||||
const ba = borderWidth / 2 + a;
|
||||
const bb = borderWidth / 2 + b;
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = borderColor;
|
||||
ctx.lineWidth = borderWidth;
|
||||
ctx.circle(center.x, center.y, ba, bb, 0, 0, 2 * Math.PI);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// draw content
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = background;
|
||||
ctx.circle(center.x, center.y, a, b, 0, 0, 2 * Math.PI);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
// ctx.setLineDash([]);
|
||||
// ctx.lineWidth = borderWidth;
|
||||
// ctx.strokeStyle = borderColor;
|
||||
// ctx.fillStyle = background;
|
||||
// ctx.setLineDash(lineDash);
|
||||
// ctx.beginPath();
|
||||
// ctx.moveTo(vertexes[0].x, vertexes[0].y);
|
||||
// ctx.lineTo(vertexes[1].x, vertexes[1].y);
|
||||
// ctx.lineTo(vertexes[2].x, vertexes[2].y);
|
||||
// ctx.lineTo(vertexes[3].x, vertexes[3].y);
|
||||
// ctx.lineTo(vertexes[0].x, vertexes[0].y);
|
||||
// ctx.closePath();
|
||||
// ctx.stroke();
|
||||
// ctx.fill();
|
||||
}
|
||||
|
||||
export function drawCrossVertexes(
|
||||
ctx: ViewContext2D,
|
||||
vertexes: ViewRectVertexes,
|
||||
opts: { borderColor: string; borderWidth: number; background: string; lineDash: number[] }
|
||||
) {
|
||||
const { borderColor, borderWidth, background, lineDash } = opts;
|
||||
ctx.setLineDash([]);
|
||||
ctx.lineWidth = borderWidth;
|
||||
ctx.strokeStyle = borderColor;
|
||||
ctx.fillStyle = background;
|
||||
ctx.setLineDash(lineDash);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(vertexes[0].x, vertexes[0].y);
|
||||
ctx.lineTo(vertexes[2].x, vertexes[2].y);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(vertexes[1].x, vertexes[1].y);
|
||||
ctx.lineTo(vertexes[3].x, vertexes[3].y);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
|
|
@ -11,93 +11,9 @@ import type {
|
|||
} from '@idraw/types';
|
||||
import { rotateElementVertexes, calcViewPointSize, calcViewVertexes } from '@idraw/util';
|
||||
import type { AreaSize } from './types';
|
||||
|
||||
import { resizeControllerBorderWidth, areaBorderWidth, wrapperColor, selectWrapperBorderWidth, lockColor, controllerSize } from './config';
|
||||
|
||||
function drawVertexes(
|
||||
ctx: ViewContext2D,
|
||||
vertexes: ViewRectVertexes,
|
||||
opts: { borderColor: string; borderWidth: number; background: string; lineDash: number[] }
|
||||
) {
|
||||
const { borderColor, borderWidth, background, lineDash } = opts;
|
||||
ctx.setLineDash([]);
|
||||
ctx.lineWidth = borderWidth;
|
||||
ctx.strokeStyle = borderColor;
|
||||
ctx.fillStyle = background;
|
||||
ctx.setLineDash(lineDash);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(vertexes[0].x, vertexes[0].y);
|
||||
ctx.lineTo(vertexes[1].x, vertexes[1].y);
|
||||
ctx.lineTo(vertexes[2].x, vertexes[2].y);
|
||||
ctx.lineTo(vertexes[3].x, vertexes[3].y);
|
||||
ctx.lineTo(vertexes[0].x, vertexes[0].y);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
function drawLine(ctx: ViewContext2D, start: PointSize, end: PointSize, opts: { borderColor: string; borderWidth: number; lineDash: number[] }) {
|
||||
const { borderColor, borderWidth, lineDash } = opts;
|
||||
ctx.setLineDash([]);
|
||||
ctx.lineWidth = borderWidth;
|
||||
ctx.strokeStyle = borderColor;
|
||||
ctx.setLineDash(lineDash);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(start.x, start.y);
|
||||
ctx.lineTo(end.x, end.y);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
function drawCircleController(
|
||||
ctx: ViewContext2D,
|
||||
circleCenter: PointSize,
|
||||
opts: { borderColor: string; borderWidth: number; background: string; lineDash: number[]; size: number }
|
||||
) {
|
||||
const { size, borderColor, borderWidth, background } = opts;
|
||||
const center = circleCenter;
|
||||
const r = size / 2;
|
||||
|
||||
const a = r;
|
||||
const b = r;
|
||||
// 'content-box'
|
||||
|
||||
if (a >= 0 && b >= 0) {
|
||||
// draw border
|
||||
if (typeof borderWidth === 'number' && borderWidth > 0) {
|
||||
const ba = borderWidth / 2 + a;
|
||||
const bb = borderWidth / 2 + b;
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = borderColor;
|
||||
ctx.lineWidth = borderWidth;
|
||||
ctx.circle(center.x, center.y, ba, bb, 0, 0, 2 * Math.PI);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// draw content
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = background;
|
||||
ctx.circle(center.x, center.y, a, b, 0, 0, 2 * Math.PI);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
// ctx.setLineDash([]);
|
||||
// ctx.lineWidth = borderWidth;
|
||||
// ctx.strokeStyle = borderColor;
|
||||
// ctx.fillStyle = background;
|
||||
// ctx.setLineDash(lineDash);
|
||||
// ctx.beginPath();
|
||||
// ctx.moveTo(vertexes[0].x, vertexes[0].y);
|
||||
// ctx.lineTo(vertexes[1].x, vertexes[1].y);
|
||||
// ctx.lineTo(vertexes[2].x, vertexes[2].y);
|
||||
// ctx.lineTo(vertexes[3].x, vertexes[3].y);
|
||||
// ctx.lineTo(vertexes[0].x, vertexes[0].y);
|
||||
// ctx.closePath();
|
||||
// ctx.stroke();
|
||||
// ctx.fill();
|
||||
}
|
||||
import { drawVertexes, drawLine, drawCircleController, drawCrossVertexes } from './draw-base';
|
||||
// import { drawSizeAuxiliaryLines } from './draw-auxiliary';
|
||||
|
||||
export function drawHoverVertexesWrapper(
|
||||
ctx: ViewContext2D,
|
||||
|
|
@ -114,29 +30,6 @@ export function drawHoverVertexesWrapper(
|
|||
drawVertexes(ctx, calcViewVertexes(vertexes, opts), wrapperOpts);
|
||||
}
|
||||
|
||||
function drawCrossVertexes(
|
||||
ctx: ViewContext2D,
|
||||
vertexes: ViewRectVertexes,
|
||||
opts: { borderColor: string; borderWidth: number; background: string; lineDash: number[] }
|
||||
) {
|
||||
const { borderColor, borderWidth, background, lineDash } = opts;
|
||||
ctx.setLineDash([]);
|
||||
ctx.lineWidth = borderWidth;
|
||||
ctx.strokeStyle = borderColor;
|
||||
ctx.fillStyle = background;
|
||||
ctx.setLineDash(lineDash);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(vertexes[0].x, vertexes[0].y);
|
||||
ctx.lineTo(vertexes[2].x, vertexes[2].y);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(vertexes[1].x, vertexes[1].y);
|
||||
ctx.lineTo(vertexes[3].x, vertexes[3].y);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
export function drawLockVertexesWrapper(
|
||||
ctx: ViewContext2D,
|
||||
vertexes: ViewRectVertexes | null,
|
||||
|
|
@ -172,26 +65,47 @@ export function drawLockVertexesWrapper(
|
|||
export function drawSelectedElementControllersVertexes(
|
||||
ctx: ViewContext2D,
|
||||
controller: ElementSizeController | null,
|
||||
opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }
|
||||
opts: {
|
||||
hideControllers: boolean;
|
||||
viewScaleInfo: ViewScaleInfo;
|
||||
viewSizeInfo: ViewSizeInfo;
|
||||
element: Element | null;
|
||||
groupQueue: Element<'group'>[];
|
||||
}
|
||||
) {
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
const { hideControllers } = opts;
|
||||
// const { element, groupQueue } = opts;
|
||||
const { elementWrapper, topLeft, topRight, bottomLeft, bottomRight, top, rotate } = controller;
|
||||
const wrapperOpts = { borderColor: wrapperColor, borderWidth: selectWrapperBorderWidth, background: 'transparent', lineDash: [] };
|
||||
const ctrlOpts = { ...wrapperOpts, borderWidth: resizeControllerBorderWidth, background: '#FFFFFF' };
|
||||
|
||||
drawLine(ctx, calcViewPointSize(top.center, opts), calcViewPointSize(rotate.center, opts), { ...ctrlOpts, borderWidth: 2 });
|
||||
drawVertexes(ctx, calcViewVertexes(elementWrapper, opts), wrapperOpts);
|
||||
// drawVertexes(ctx, calcViewVertexes(left.vertexes, opts), ctrlOpts);
|
||||
// drawVertexes(ctx, calcViewVertexes(right.vertexes, opts), ctrlOpts);
|
||||
// drawVertexes(ctx, calcViewVertexes(top.vertexes, opts), ctrlOpts);
|
||||
// drawVertexes(ctx, calcViewVertexes(bottom.vertexes, opts), ctrlOpts);
|
||||
drawVertexes(ctx, calcViewVertexes(topLeft.vertexes, opts), ctrlOpts);
|
||||
drawVertexes(ctx, calcViewVertexes(topRight.vertexes, opts), ctrlOpts);
|
||||
drawVertexes(ctx, calcViewVertexes(bottomLeft.vertexes, opts), ctrlOpts);
|
||||
drawVertexes(ctx, calcViewVertexes(bottomRight.vertexes, opts), ctrlOpts);
|
||||
drawCircleController(ctx, calcViewPointSize(rotate.center, opts), { ...ctrlOpts, size: controllerSize, borderWidth: 2 });
|
||||
if (!hideControllers) {
|
||||
drawLine(ctx, calcViewPointSize(top.center, opts), calcViewPointSize(rotate.center, opts), { ...ctrlOpts, borderWidth: 2 });
|
||||
drawVertexes(ctx, calcViewVertexes(topLeft.vertexes, opts), ctrlOpts);
|
||||
drawVertexes(ctx, calcViewVertexes(topRight.vertexes, opts), ctrlOpts);
|
||||
drawVertexes(ctx, calcViewVertexes(bottomLeft.vertexes, opts), ctrlOpts);
|
||||
drawVertexes(ctx, calcViewVertexes(bottomRight.vertexes, opts), ctrlOpts);
|
||||
drawCircleController(ctx, calcViewPointSize(rotate.center, opts), { ...ctrlOpts, size: controllerSize, borderWidth: 2 });
|
||||
}
|
||||
|
||||
// drawSizeAuxiliaryLines(ctx, {
|
||||
// vertexes: [
|
||||
// calcViewPointSize(topLeft.center, opts),
|
||||
// calcViewPointSize(topRight.center, opts),
|
||||
// calcViewPointSize(bottomRight.center, opts),
|
||||
// calcViewPointSize(bottomLeft.center, opts)
|
||||
// ],
|
||||
// element,
|
||||
// groupQueue
|
||||
// });
|
||||
}
|
||||
|
||||
export function drawElementListShadows(ctx: ViewContext2D, elements: Element<ElementType>[], opts?: Omit<RendererDrawElementOptions, 'loader'>) {
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@ import {
|
|||
getGroupQueueFromList,
|
||||
findElementsFromList,
|
||||
findElementsFromListByPositions,
|
||||
getElementPositionFromList,
|
||||
deepResizeGroupElement
|
||||
} from '@idraw/util';
|
||||
import type { ViewRectVertexes, CoreEvent, ElementPosition, ViewScaleInfo, ViewSizeInfo, ElementSizeController } from '@idraw/types';
|
||||
import type { ViewRectVertexes, CoreEventMap, ElementPosition, ViewScaleInfo, ViewSizeInfo, ElementSizeController } from '@idraw/types';
|
||||
import type {
|
||||
Point,
|
||||
PointSize,
|
||||
|
|
@ -53,6 +54,7 @@ import {
|
|||
keySelectedElementList,
|
||||
keySelectedElementListVertexes,
|
||||
keySelectedElementController,
|
||||
keyIsMoving,
|
||||
controllerSize
|
||||
// keyDebugElemCenter,
|
||||
// keyDebugEnd0,
|
||||
|
|
@ -67,7 +69,7 @@ export const middlewareEventSelect: string = '@middleware/select';
|
|||
|
||||
export const middlewareEventSelectClear: string = '@middleware/select-clear';
|
||||
|
||||
export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, CoreEvent> = (opts) => {
|
||||
export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, CoreEventMap> = (opts) => {
|
||||
const { viewer, sharer, boardContent, calculator, eventHub } = opts;
|
||||
const { helperContext } = boardContent;
|
||||
let prevPoint: Point | null = null;
|
||||
|
|
@ -154,6 +156,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
|
|||
sharer.setSharedStorage(keySelectedElementList, []);
|
||||
sharer.setSharedStorage(keySelectedElementListVertexes, null);
|
||||
sharer.setSharedStorage(keySelectedElementController, null);
|
||||
sharer.setSharedStorage(keyIsMoving, null);
|
||||
};
|
||||
|
||||
clear();
|
||||
|
|
@ -389,6 +392,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
|
|||
},
|
||||
|
||||
pointMove: (e: PointWatcherEvent) => {
|
||||
sharer.setSharedStorage(keyIsMoving, true);
|
||||
const data = sharer.getActiveStorage('data');
|
||||
const elems = getActiveElements();
|
||||
const scale = sharer.getActiveStorage('scale') || 1;
|
||||
|
|
@ -501,6 +505,8 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
|
|||
|
||||
pointEnd(e: PointWatcherEvent) {
|
||||
inBusyMode = null;
|
||||
|
||||
sharer.setSharedStorage(keyIsMoving, false);
|
||||
const data = sharer.getActiveStorage('data');
|
||||
const resizeType = sharer.getSharedStorage(keyResizeType);
|
||||
const actionType = sharer.getSharedStorage(keyActionType);
|
||||
|
|
@ -603,6 +609,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
|
|||
eventHub.trigger(middlewareEventTextEdit, {
|
||||
element: target.elements[0],
|
||||
groupQueue: sharer.getSharedStorage(keyGroupQueue) || [],
|
||||
position: getElementPositionFromList(target.elements[0]?.uuid, sharer.getActiveStorage('data')?.elements || []),
|
||||
viewScaleInfo: sharer.getActiveViewScaleInfo()
|
||||
});
|
||||
}
|
||||
|
|
@ -625,9 +632,12 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
|
|||
const areaEnd: Point | null = sharedStore[keyAreaEnd];
|
||||
const groupQueue: Element<'group'>[] = sharedStore[keyGroupQueue];
|
||||
const groupQueueVertexesList: ViewRectVertexes[] = sharedStore[keyGroupQueueVertexesList];
|
||||
const isMoving = sharedStore[keyIsMoving];
|
||||
|
||||
const drawBaseOpts = { calculator, viewScaleInfo, viewSizeInfo };
|
||||
// const selectedElementController = sharedStore[keySelectedElementController];
|
||||
// const resizeType: ResizeType | null = sharedStore[keyResizeType];
|
||||
|
||||
const selectedElementController = elem
|
||||
? calcElementSizeController(elem, {
|
||||
groupQueue,
|
||||
|
|
@ -656,7 +666,12 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
|
|||
}
|
||||
}
|
||||
if (!isLock && elem && (['select', 'drag', 'resize'] as ActionType[]).includes(actionType)) {
|
||||
drawSelectedElementControllersVertexes(helperContext, selectedElementController, { ...drawBaseOpts });
|
||||
drawSelectedElementControllersVertexes(helperContext, selectedElementController, {
|
||||
...drawBaseOpts,
|
||||
element: elem,
|
||||
groupQueue,
|
||||
hideControllers: !!isMoving && actionType === 'drag'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// in root
|
||||
|
|
@ -675,7 +690,12 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
|
|||
}
|
||||
}
|
||||
if (!isLock && elem && (['select', 'drag', 'resize'] as ActionType[]).includes(actionType)) {
|
||||
drawSelectedElementControllersVertexes(helperContext, selectedElementController, { ...drawBaseOpts });
|
||||
drawSelectedElementControllersVertexes(helperContext, selectedElementController, {
|
||||
...drawBaseOpts,
|
||||
element: elem,
|
||||
groupQueue,
|
||||
hideControllers: !!isMoving && actionType === 'drag'
|
||||
});
|
||||
} else if (actionType === 'area' && areaStart && areaEnd) {
|
||||
drawArea(helperContext, { start: areaStart, end: areaEnd });
|
||||
} else if ((['drag-list', 'drag-list-end'] as ActionType[]).includes(actionType)) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
keySelectedElementList,
|
||||
keySelectedElementListVertexes,
|
||||
keySelectedElementController,
|
||||
keyIsMoving,
|
||||
keyDebugElemCenter,
|
||||
keyDebugEnd0,
|
||||
keyDebugEndHorizontal,
|
||||
|
|
@ -95,6 +96,7 @@ export type DeepSelectorSharedStorage = {
|
|||
[keySelectedElementList]: Array<Element<ElementType>>;
|
||||
[keySelectedElementListVertexes]: ViewRectVertexes | null;
|
||||
[keySelectedElementController]: ElementSizeController | null;
|
||||
[keyIsMoving]: boolean | null;
|
||||
|
||||
[keyDebugElemCenter]: PointSize | null;
|
||||
[keyDebugEnd0]: PointSize | null;
|
||||
|
|
|
|||
|
|
@ -1,23 +1,40 @@
|
|||
import type { BoardMiddleware, CoreEvent, Element, ElementSize, ViewScaleInfo } from '@idraw/types';
|
||||
import type { BoardMiddleware, CoreEventMap, Element, ElementSize, ViewScaleInfo, ElementPosition } from '@idraw/types';
|
||||
import { limitAngle, getDefaultElementDetailConfig } from '@idraw/util';
|
||||
export const middlewareEventTextEdit = '@middleware/text-edit';
|
||||
export const middlewareEventTextChange = '@middleware/text-change';
|
||||
|
||||
type TextEditEvent = {
|
||||
element: Element<'text'>;
|
||||
position: ElementPosition;
|
||||
groupQueue: Element<'group'>[];
|
||||
viewScaleInfo: ViewScaleInfo;
|
||||
};
|
||||
|
||||
type TextChangeEvent = {
|
||||
element: {
|
||||
uuid: string;
|
||||
detail: {
|
||||
text: string;
|
||||
};
|
||||
};
|
||||
position: ElementPosition;
|
||||
};
|
||||
|
||||
type ExtendEventMap = Record<typeof middlewareEventTextEdit, TextEditEvent> & Record<typeof middlewareEventTextChange, TextChangeEvent>;
|
||||
|
||||
const defaultElementDetail = getDefaultElementDetailConfig();
|
||||
|
||||
export const MiddlewareTextEditor: BoardMiddleware<Record<string, any>, CoreEvent> = (opts) => {
|
||||
export const MiddlewareTextEditor: BoardMiddleware<ExtendEventMap, CoreEventMap & ExtendEventMap> = (opts) => {
|
||||
const { eventHub, boardContent, viewer } = opts;
|
||||
const canvas = boardContent.boardContext.canvas;
|
||||
const textarea = document.createElement('textarea');
|
||||
// const textarea = document.createElement('textarea');
|
||||
const textarea = document.createElement('div');
|
||||
textarea.setAttribute('contenteditable', 'true');
|
||||
const canvasWrapper = document.createElement('div');
|
||||
const container = opts.container || document.body;
|
||||
const mask = document.createElement('div');
|
||||
let activeElem: Element<'text'> | null = null;
|
||||
let activePosition: ElementPosition = [];
|
||||
|
||||
canvasWrapper.appendChild(textarea);
|
||||
|
||||
|
|
@ -41,6 +58,7 @@ export const MiddlewareTextEditor: BoardMiddleware<Record<string, any>, CoreEven
|
|||
const hideTextArea = () => {
|
||||
mask.style.display = 'none';
|
||||
activeElem = null;
|
||||
activePosition = [];
|
||||
};
|
||||
|
||||
const getCanvasRect = () => {
|
||||
|
|
@ -109,6 +127,24 @@ export const MiddlewareTextEditor: BoardMiddleware<Record<string, any>, CoreEven
|
|||
elemH = element.h * scale;
|
||||
}
|
||||
|
||||
let justifyContent: ElementCSSInlineStyle['style']['justifyContent'] = 'center';
|
||||
let alignItems = 'center';
|
||||
if (detail.textAlign === 'left') {
|
||||
justifyContent = 'start';
|
||||
} else if (detail.textAlign === 'right') {
|
||||
justifyContent = 'end';
|
||||
}
|
||||
|
||||
if (detail.verticalAlign === 'top') {
|
||||
alignItems = 'start';
|
||||
} else if (detail.verticalAlign === 'bottom') {
|
||||
alignItems = 'end';
|
||||
}
|
||||
|
||||
textarea.style.display = 'inline-flex';
|
||||
textarea.style.justifyContent = justifyContent;
|
||||
textarea.style.alignItems = alignItems;
|
||||
|
||||
textarea.style.position = 'absolute';
|
||||
textarea.style.left = `${elemX - 1}px`;
|
||||
textarea.style.top = `${elemY - 1}px`;
|
||||
|
|
@ -131,7 +167,8 @@ export const MiddlewareTextEditor: BoardMiddleware<Record<string, any>, CoreEven
|
|||
textarea.style.margin = '0';
|
||||
textarea.style.outline = 'none';
|
||||
|
||||
textarea.value = detail.text || '';
|
||||
// textarea.value = detail.text || '';
|
||||
textarea.innerText = detail.text || '';
|
||||
parent.appendChild(textarea);
|
||||
};
|
||||
|
||||
|
|
@ -152,19 +189,42 @@ export const MiddlewareTextEditor: BoardMiddleware<Record<string, any>, CoreEven
|
|||
textarea.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
textarea.addEventListener('input', (e) => {
|
||||
if (activeElem) {
|
||||
activeElem.detail.text = (e.target as any).value || '';
|
||||
textarea.addEventListener('input', () => {
|
||||
if (activeElem && activePosition) {
|
||||
// activeElem.detail.text = (e.target as any).value || '';
|
||||
activeElem.detail.text = textarea.innerText || '';
|
||||
eventHub.trigger(middlewareEventTextChange, {
|
||||
element: {
|
||||
uuid: activeElem.uuid,
|
||||
detail: {
|
||||
text: activeElem.detail.text
|
||||
}
|
||||
},
|
||||
position: [...(activePosition || [])]
|
||||
});
|
||||
viewer.drawFrame();
|
||||
}
|
||||
});
|
||||
textarea.addEventListener('blur', () => {
|
||||
if (activeElem && activePosition) {
|
||||
eventHub.trigger(middlewareEventTextChange, {
|
||||
element: {
|
||||
uuid: activeElem.uuid,
|
||||
detail: {
|
||||
text: activeElem.detail.text
|
||||
}
|
||||
},
|
||||
position: [...activePosition]
|
||||
});
|
||||
}
|
||||
|
||||
hideTextArea();
|
||||
});
|
||||
|
||||
const textEditCallback = (e: TextEditEvent) => {
|
||||
if (e?.element && e?.element?.type === 'text') {
|
||||
if (e?.position && e?.element && e?.element?.type === 'text') {
|
||||
activeElem = e.element;
|
||||
activePosition = e.position;
|
||||
}
|
||||
showTextArea(e);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,32 +1,49 @@
|
|||
import { middlewareEventScale, middlewareEventSelect } from '@idraw/core';
|
||||
import type { CoreEvent, Data } from '@idraw/types';
|
||||
import type { CoreEventMap, Data } from '@idraw/types';
|
||||
|
||||
export interface IDrawEventKeys {
|
||||
select: typeof middlewareEventSelect;
|
||||
scale: typeof middlewareEventScale;
|
||||
change: 'change';
|
||||
}
|
||||
import {
|
||||
middlewareEventRuler,
|
||||
middlewareEventScale,
|
||||
middlewareEventSelect,
|
||||
middlewareEventSelectClear,
|
||||
middlewareEventTextEdit,
|
||||
middlewareEventTextChange
|
||||
} from '@idraw/core';
|
||||
|
||||
export type IDrawEvent = CoreEvent & {
|
||||
const idrawEventChange = 'change';
|
||||
|
||||
export type IDrawEvent = CoreEventMap & {
|
||||
change: {
|
||||
data: Data;
|
||||
type: 'updateElement' | 'deleteElement' | 'moveElement' | 'addElement' | 'dragElement' | 'resizeElement' | 'setData' | 'undo' | 'redo' | 'other';
|
||||
};
|
||||
};
|
||||
|
||||
// TODO
|
||||
const EventKeys = {} as {
|
||||
select: typeof middlewareEventSelect;
|
||||
export interface IDrawEventKeys {
|
||||
change: typeof idrawEventChange;
|
||||
ruler: typeof middlewareEventRuler;
|
||||
scale: typeof middlewareEventScale;
|
||||
change: 'change';
|
||||
select: typeof middlewareEventSelect;
|
||||
clearSelect: typeof middlewareEventSelectClear;
|
||||
textEdit: typeof middlewareEventTextEdit;
|
||||
textChange: typeof middlewareEventTextChange;
|
||||
}
|
||||
|
||||
const innerEventKeys: IDrawEventKeys = {
|
||||
change: idrawEventChange,
|
||||
ruler: middlewareEventRuler,
|
||||
scale: middlewareEventScale,
|
||||
select: middlewareEventSelect,
|
||||
clearSelect: middlewareEventSelectClear,
|
||||
textEdit: middlewareEventTextEdit,
|
||||
textChange: middlewareEventTextChange
|
||||
};
|
||||
Object.defineProperty(EventKeys, 'select', {
|
||||
value: middlewareEventSelect,
|
||||
writable: false
|
||||
});
|
||||
Object.defineProperty(EventKeys, 'scale', {
|
||||
value: middlewareEventScale,
|
||||
writable: false
|
||||
|
||||
const eventKeys = {} as IDrawEventKeys;
|
||||
Object.keys(innerEventKeys).forEach((keyName: string) => {
|
||||
Object.defineProperty(eventKeys, keyName, {
|
||||
value: innerEventKeys[keyName as keyof IDrawEventKeys],
|
||||
writable: false
|
||||
});
|
||||
});
|
||||
|
||||
export { EventKeys };
|
||||
export { eventKeys };
|
||||
|
|
|
|||
|
|
@ -1,13 +1,4 @@
|
|||
import {
|
||||
Core,
|
||||
MiddlewareSelector,
|
||||
MiddlewareScroller,
|
||||
MiddlewareScaler,
|
||||
MiddlewareRuler,
|
||||
MiddlewareTextEditor,
|
||||
middlewareEventSelect,
|
||||
MiddlewareDragger
|
||||
} from '@idraw/core';
|
||||
import { Core, MiddlewareSelector, MiddlewareScroller, MiddlewareScaler, MiddlewareRuler, MiddlewareTextEditor, MiddlewareDragger } from '@idraw/core';
|
||||
import type {
|
||||
PointSize,
|
||||
IDrawOptions,
|
||||
|
|
@ -36,6 +27,7 @@ import {
|
|||
import { defaultSettings } from './config';
|
||||
import { exportImageFileBlobURL } from './file';
|
||||
import type { ExportImageFileBaseOptions, ExportImageFileResult } from './file';
|
||||
import { eventKeys } from './event';
|
||||
|
||||
export class iDraw {
|
||||
#core: Core<IDrawEvent>;
|
||||
|
|
@ -111,7 +103,7 @@ export class iDraw {
|
|||
setData(data: Data) {
|
||||
const core = this.#core;
|
||||
core.setData(data);
|
||||
core.trigger('change', { data, type: 'setData' });
|
||||
core.trigger(eventKeys.change, { data, type: 'setData' });
|
||||
}
|
||||
|
||||
getData(opts?: { compact?: boolean }): Data | null {
|
||||
|
|
@ -171,7 +163,7 @@ export class iDraw {
|
|||
}
|
||||
|
||||
selectElements(uuids: string[]) {
|
||||
this.trigger(middlewareEventSelect, { uuids });
|
||||
this.trigger(eventKeys.select, { uuids });
|
||||
}
|
||||
|
||||
selectElementByPosition(position: ElementPosition) {
|
||||
|
|
@ -179,11 +171,11 @@ export class iDraw {
|
|||
}
|
||||
|
||||
selectElementsByPositions(positions: ElementPosition[]) {
|
||||
this.trigger(middlewareEventSelect, { positions });
|
||||
this.trigger(eventKeys.select, { positions });
|
||||
}
|
||||
|
||||
cancelElements() {
|
||||
this.trigger(middlewareEventSelect, { uuids: [] });
|
||||
this.trigger(eventKeys.select, { uuids: [] });
|
||||
}
|
||||
|
||||
createElement<T extends ElementType>(
|
||||
|
|
@ -212,7 +204,7 @@ export class iDraw {
|
|||
updateElementInList(element.uuid, element, data.elements);
|
||||
core.setData(data);
|
||||
core.refresh();
|
||||
core.trigger('change', { data, type: 'updateElement' });
|
||||
core.trigger(eventKeys.change, { data, type: 'updateElement' });
|
||||
}
|
||||
|
||||
addElement(
|
||||
|
|
@ -231,7 +223,7 @@ export class iDraw {
|
|||
}
|
||||
core.setData(data);
|
||||
core.refresh();
|
||||
core.trigger('change', { data, type: 'addElement' });
|
||||
core.trigger(eventKeys.change, { data, type: 'addElement' });
|
||||
return data;
|
||||
}
|
||||
|
||||
|
|
@ -241,7 +233,7 @@ export class iDraw {
|
|||
deleteElementInList(uuid, data.elements);
|
||||
core.setData(data);
|
||||
core.refresh();
|
||||
core.trigger('change', { data, type: 'deleteElement' });
|
||||
core.trigger(eventKeys.change, { data, type: 'deleteElement' });
|
||||
}
|
||||
|
||||
moveElement(uuid: string, to: ElementPosition) {
|
||||
|
|
@ -252,7 +244,7 @@ export class iDraw {
|
|||
data.elements = list;
|
||||
core.setData(data);
|
||||
core.refresh();
|
||||
core.trigger('change', { data, type: 'moveElement' });
|
||||
core.trigger(eventKeys.change, { data, type: 'moveElement' });
|
||||
}
|
||||
|
||||
async getImageBlobURL(opts: ExportImageFileBaseOptions): Promise<ExportImageFileResult> {
|
||||
|
|
|
|||
|
|
@ -121,5 +121,6 @@ export {
|
|||
modifyElement
|
||||
} from '@idraw/util';
|
||||
export { iDraw } from './idraw';
|
||||
export { eventKeys } from './event';
|
||||
export type { IDrawEvent, IDrawEventKeys } from './event';
|
||||
export type { ExportImageFileResult, ExportImageFileBaseOptions } from './file';
|
||||
|
|
|
|||
|
|
@ -314,6 +314,12 @@ export function drawBoxShadow(
|
|||
renderContent();
|
||||
ctx.restore();
|
||||
} else {
|
||||
ctx.save();
|
||||
ctx.shadowColor = 'transparent';
|
||||
ctx.shadowOffsetX = 0;
|
||||
ctx.shadowOffsetY = 0;
|
||||
ctx.shadowBlur = 0;
|
||||
renderContent();
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { drawBoxShadow, getOpacity } from './box';
|
|||
|
||||
export function drawCircle(ctx: ViewContext2D, elem: Element<'circle'>, opts: RendererDrawElementOptions) {
|
||||
const { detail, angle } = elem;
|
||||
const { calculator, viewScaleInfo, viewSizeInfo, parentOpacity } = opts;
|
||||
const { background = '#000000', borderColor = '#000000', boxSizing, borderWidth = 0 } = detail;
|
||||
let bw: number = 0;
|
||||
if (typeof borderWidth === 'number' && borderWidth > 0) {
|
||||
|
|
@ -12,7 +13,8 @@ export function drawCircle(ctx: ViewContext2D, elem: Element<'circle'>, opts: Re
|
|||
} else if (Array.isArray(borderWidth) && typeof borderWidth[0] === 'number' && borderWidth[0] > 0) {
|
||||
bw = borderWidth[0] as number;
|
||||
}
|
||||
const { calculator, viewScaleInfo, viewSizeInfo, parentOpacity } = opts;
|
||||
bw = bw * viewScaleInfo.scale;
|
||||
|
||||
// const { scale, offsetTop, offsetBottom, offsetLeft, offsetRight } = viewScaleInfo;
|
||||
const { x, y, w, h } = calculator?.elementSize({ x: elem.x, y: elem.y, w: elem.w, h: elem.h }, viewScaleInfo, viewSizeInfo) || elem;
|
||||
const viewElem = { ...elem, ...{ x, y, w, h, angle } };
|
||||
|
|
@ -46,12 +48,12 @@ export function drawCircle(ctx: ViewContext2D, elem: Element<'circle'>, opts: Re
|
|||
ctx.globalAlpha = opacity;
|
||||
|
||||
// draw border
|
||||
if (typeof borderWidth === 'number' && borderWidth > 0) {
|
||||
const ba = borderWidth / 2 + a;
|
||||
const bb = borderWidth / 2 + b;
|
||||
if (typeof bw === 'number' && bw > 0) {
|
||||
const ba = bw / 2 + a;
|
||||
const bb = bw / 2 + b;
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = borderColor;
|
||||
ctx.lineWidth = borderWidth;
|
||||
ctx.lineWidth = bw;
|
||||
ctx.circle(centerX, centerY, ba, bb, 0, 0, 2 * Math.PI);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
|
|
|
|||
|
|
@ -32,8 +32,10 @@ export class Renderer extends EventEmitter<RendererEventMap> implements BoardRen
|
|||
loader.on('load', (e) => {
|
||||
this.trigger('load', e);
|
||||
});
|
||||
loader.on('error', () => {
|
||||
loader.on('error', (e) => {
|
||||
// TODO
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -138,6 +138,9 @@ export class Loader extends EventEmitter<LoaderEventMap> implements RendererLoad
|
|||
#loadResource(element: Element<LoadElementType>, assets: ElementAssets) {
|
||||
const item = this.#createLoadItem(element);
|
||||
const assetId = getAssetIdFromElement(element);
|
||||
if (this.#currentLoadItemMap[assetId]) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#currentLoadItemMap[assetId] = item;
|
||||
const loadFunc = this.#loadFuncMap[element.type];
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ import type { ActiveStore, StoreSharer } from './store';
|
|||
import type { RendererEventMap, RendererOptions, RendererDrawOptions, RendererLoader } from './renderer';
|
||||
import type { Data } from './data';
|
||||
|
||||
export type BoardBaseEventMap = {
|
||||
loadSource: void;
|
||||
};
|
||||
|
||||
export type BoardExtendEventMap = BoardBaseEventMap & Record<string, any>;
|
||||
|
||||
export interface BoardWatcherPointEvent {
|
||||
point: Point;
|
||||
}
|
||||
|
|
@ -81,7 +87,7 @@ export interface BoardMiddlewareObject<S extends Record<any | symbol, any> = any
|
|||
clear?(e: BoardWatcherEventMap<S>['clear']): void | boolean;
|
||||
}
|
||||
|
||||
export interface BoardMiddlewareOptions<S extends Record<any | symbol, any> = Record<any | symbol, any>, E extends BoardExtendEvent = Record<string, any>> {
|
||||
export interface BoardMiddlewareOptions<S extends Record<any | symbol, any> = Record<any | symbol, any>, E extends BoardExtendEventMap = BoardExtendEventMap> {
|
||||
boardContent: BoardContent;
|
||||
sharer: StoreSharer<S>;
|
||||
viewer: BoardViewer;
|
||||
|
|
@ -91,7 +97,7 @@ export interface BoardMiddlewareOptions<S extends Record<any | symbol, any> = Re
|
|||
canvas?: HTMLCanvasElement;
|
||||
}
|
||||
|
||||
export type BoardMiddleware<S extends Record<any | symbol, any> = any, E extends BoardExtendEvent = Record<string, any>> = (
|
||||
export type BoardMiddleware<S extends Record<any | symbol, any> = any, E extends BoardExtendEventMap = BoardExtendEventMap> = (
|
||||
opts: BoardMiddlewareOptions<S, E>
|
||||
) => BoardMiddlewareObject<S>;
|
||||
|
||||
|
|
@ -147,5 +153,3 @@ export interface BoardWatcherStore {
|
|||
hasPointDown: boolean;
|
||||
prevClickPoint: Point | null;
|
||||
}
|
||||
|
||||
export type BoardExtendEvent = Record<string, any>;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ export interface ViewContext2D {
|
|||
// extend API
|
||||
$getContext(): CanvasRenderingContext2D;
|
||||
$setContext(ctx: CanvasRenderingContext2D): void;
|
||||
$resetFont(): void;
|
||||
$setFont(opts: { fontSize: number; fontFamily?: string; fontWeight?: string | number }): void;
|
||||
$resize(opts: { width: number; height: number; devicePixelRatio: number }): void;
|
||||
$getSize(): { width: number; height: number; devicePixelRatio: number };
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import type { Element, ElementType } from './element';
|
||||
import type { Data } from './data';
|
||||
import type { ViewContext2D } from './context2d';
|
||||
import type { BoardBaseEventMap } from './board';
|
||||
|
||||
export interface CoreOptions {
|
||||
width: number;
|
||||
|
|
@ -40,7 +41,7 @@ export interface CoreEventScale {
|
|||
scale: number;
|
||||
}
|
||||
|
||||
export type CoreEvent = {
|
||||
export type CoreEventMap = BoardBaseEventMap & {
|
||||
cursor: CoreEventCursor;
|
||||
change: CoreEventChange;
|
||||
[key: string]: any;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import type { BoardMiddlewareObject, BoardMiddleware, BoardMode } from './board';
|
||||
import type { BoardMiddlewareObject, BoardMiddleware } from './board';
|
||||
|
||||
export type Middleware = BoardMiddleware;
|
||||
|
||||
export type MiddlewareObject = BoardMiddlewareObject;
|
||||
|
||||
export type MiddlewareMode = BoardMode;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
import { Data } from './data';
|
||||
import { ViewScaleInfo, ViewSizeInfo } from './view';
|
||||
import {
|
||||
// ViewRectVertexes,
|
||||
ViewScaleInfo,
|
||||
ViewSizeInfo
|
||||
} from './view';
|
||||
|
||||
export type ActiveStore = ViewSizeInfo &
|
||||
ViewScaleInfo & {
|
||||
data: Data | null;
|
||||
// selectedViewRectVertexes: ViewRectVertexes | null;
|
||||
};
|
||||
|
||||
export interface StoreSharer<S extends Record<any, any> = any> {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import type { ViewContext2D, ViewContext2DOptions } from '@idraw/types';
|
||||
|
||||
const defaultFontSize = 12;
|
||||
const defaultFontWeight = '400';
|
||||
const defaultFontFamily = `-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'`;
|
||||
|
||||
export class Context2D implements ViewContext2D {
|
||||
#ctx: CanvasRenderingContext2D;
|
||||
#opts: Required<ViewContext2DOptions>;
|
||||
|
|
@ -12,6 +16,7 @@ export class Context2D implements ViewContext2D {
|
|||
this.#opts = { ...{ devicePixelRatio: 1, offscreenCanvas: null }, ...opts };
|
||||
// this._width = ctx.canvas.width / devicePixelRatio;
|
||||
// this._height = ctx.canvas.height / devicePixelRatio;
|
||||
this.$resetFont();
|
||||
}
|
||||
|
||||
$undoPixelRatio(num: number) {
|
||||
|
|
@ -39,6 +44,14 @@ export class Context2D implements ViewContext2D {
|
|||
this.#ctx.font = `${strList.join(' ')}`;
|
||||
}
|
||||
|
||||
$resetFont() {
|
||||
this.$setFont({
|
||||
fontSize: defaultFontSize,
|
||||
fontFamily: defaultFontFamily,
|
||||
fontWeight: defaultFontWeight
|
||||
});
|
||||
}
|
||||
|
||||
$getOffscreenCanvas(): OffscreenCanvas | null {
|
||||
return this.#opts.offscreenCanvas;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue