From 23122514603a82bb595c8f673dc0dbc092bc3f44 Mon Sep 17 00:00:00 2001 From: chenshenhai Date: Sat, 22 Apr 2023 18:13:32 +0800 Subject: [PATCH] feat: improve renderer for elements in view --- packages/board/src/lib/calculator.ts | 64 ++++--------------- packages/board/src/lib/viewer.ts | 21 ++++-- .../src/middleware/select/draw-wrapper.ts | 10 ++- packages/core/src/middleware/select/index.ts | 38 +++++++---- packages/idraw/dev/data.ts | 16 +++++ packages/idraw/dev/main.ts | 22 +++---- packages/renderer/src/draw/circle.ts | 3 +- packages/renderer/src/draw/elements.ts | 2 +- packages/renderer/src/draw/image.ts | 4 +- packages/renderer/src/draw/rect.ts | 9 +-- packages/renderer/src/index.ts | 23 +++++-- packages/types/src/lib/renderer.ts | 7 +- packages/types/src/lib/view.ts | 13 ++-- 13 files changed, 120 insertions(+), 112 deletions(-) diff --git a/packages/board/src/lib/calculator.ts b/packages/board/src/lib/calculator.ts index 2669280..cfa3370 100644 --- a/packages/board/src/lib/calculator.ts +++ b/packages/board/src/lib/calculator.ts @@ -1,16 +1,4 @@ -import type { - Data, - PointSize, - Point, - Element, - ElementType, - ViewCalculator, - ViewCalculatorOptions, - ViewScaleInfo, - ElementSize, - ViewSizeInfo, - ViewContext2D -} from '@idraw/types'; +import type { Data, Point, Element, ElementType, ViewCalculator, ViewCalculatorOptions, ViewScaleInfo, ElementSize, ViewSizeInfo } from '@idraw/types'; import { rotateElementVertexes } from '@idraw/util'; export class Calculator implements ViewCalculator { @@ -20,13 +8,6 @@ export class Calculator implements ViewCalculator { this._opts = opts; } - private _getBoardSize(): { width: number; height: number } { - return { - width: this._opts.viewContent.boardContext.canvas.width, - height: this._opts.viewContent.boardContext.canvas.height - }; - } - viewScale(num: number, prevScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): ViewScaleInfo { const scale = num; let offsetLeft = 0; @@ -129,41 +110,22 @@ export class Calculator implements ViewCalculator { }; } - isElementInView(elem: Element, scaleInfo: ViewScaleInfo): boolean { - // TODO - const { width, height } = this._getBoardSize(); - const { scale = 1, offsetTop = 0, offsetLeft = 0 } = scaleInfo; - - const { angle = 0 } = elem; + isElementInView(elem: ElementSize, scaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): boolean { + const { width, height } = viewSizeInfo; + const { angle } = elem; const { x, y, w, h } = this.elementSize(elem, scaleInfo); const vertexes = rotateElementVertexes({ x, y, w, h, angle }); - - // Virtual View Point - const vvpStart: PointSize = { - x: Math.min(vertexes[0].x, vertexes[1].x, vertexes[2].x, vertexes[3].x), - y: Math.min(vertexes[0].y, vertexes[1].y, vertexes[2].y, vertexes[3].y) - }; - const vvpEnd: PointSize = { - x: Math.max(vertexes[0].x, vertexes[1].x, vertexes[2].x, vertexes[3].x), - y: Math.max(vertexes[0].y, vertexes[1].y, vertexes[2].y, vertexes[3].y) - }; - - // Virtual Element Point - const vep0: PointSize = { x: elem.x * scale, y: elem.y * scale }; - const vep1: PointSize = { x: (elem.x + elem.w) * scale, y: elem.y * scale }; - const vep2: PointSize = { x: (elem.x + elem.w) * scale, y: (elem.y + elem.h) * scale }; - const vep3: PointSize = { x: elem.x * scale, y: (elem.y + elem.h) * scale }; - - const isPointInRect = (p: PointSize) => { - return p.x >= vvpStart.x && p.x <= vvpEnd.x && p.y >= vvpStart.y && p.y <= vvpEnd.y; - }; - if (isPointInRect(vep0) || isPointInRect(vep1) || isPointInRect(vep2) || isPointInRect(vep3)) { - return true; + for (let i = 0; i < vertexes.length; i++) { + const v = vertexes[i]; + if (v.x >= 0 && v.x <= width && v.y >= 0 && v.y <= height) { + return true; + } } return false; } - isPointInElement(ctx: ViewContext2D, p: Point, elem: Element, scaleInfo: ViewScaleInfo): boolean { + isPointInElement(p: Point, elem: Element, scaleInfo: ViewScaleInfo): boolean { + const ctx = this._opts.viewContent.boardContext; const { angle = 0 } = elem; const { x, y, w, h } = this.elementSize(elem, scaleInfo); const vertexes = rotateElementVertexes({ x, y, w, h, angle }); @@ -181,14 +143,14 @@ export class Calculator implements ViewCalculator { return false; } - getPointElement(ctx: ViewContext2D, p: Point, data: Data, scaleInfo: ViewScaleInfo): { index: number; element: null | Element } { + getPointElement(p: Point, data: Data, scaleInfo: ViewScaleInfo): { index: number; element: null | Element } { const result: { index: number; element: null | Element } = { index: -1, element: null }; for (let i = 0; i < data.elements.length; i++) { const elem = data.elements[i]; - if (this.isPointInElement(ctx, p, elem, scaleInfo)) { + if (this.isPointInElement(p, elem, scaleInfo)) { result.index = i; result.element = elem; break; diff --git a/packages/board/src/lib/viewer.ts b/packages/board/src/lib/viewer.ts index 87c014b..8344f28 100644 --- a/packages/board/src/lib/viewer.ts +++ b/packages/board/src/lib/viewer.ts @@ -34,13 +34,24 @@ export class Viewer extends EventEmitter implements BoardVi if (snapshot) { const { viewContext, helperContext, boardContext } = viewContent; + if (snapshot?.activeStore.data) { + const { scale, offsetTop, offsetBottom, offsetLeft, offsetRight, width, height, contextHeight, contextWidth, devicePixelRatio } = snapshot.activeStore; renderer.drawData(snapshot.activeStore.data, { - scale: snapshot?.activeStore.scale, - offsetTop: snapshot?.activeStore.offsetTop, - offsetBottom: snapshot?.activeStore.offsetBottom, - offsetLeft: snapshot?.activeStore.offsetLeft, - offsetRight: snapshot?.activeStore.offsetRight + scaleInfo: { + scale, + offsetTop, + offsetBottom, + offsetLeft, + offsetRight + }, + viewSize: { + width, + height, + contextHeight, + contextWidth, + devicePixelRatio + } }); } beforeDrawFrame({ snapshot }); diff --git a/packages/core/src/middleware/select/draw-wrapper.ts b/packages/core/src/middleware/select/draw-wrapper.ts index f6a16d1..dae8d43 100644 --- a/packages/core/src/middleware/select/draw-wrapper.ts +++ b/packages/core/src/middleware/select/draw-wrapper.ts @@ -12,7 +12,7 @@ export function drawPointWrapper(ctx: CanvasRenderingContext2D | ViewContext2D, if (opts?.calculator) { const { calculator } = opts; - const size = calculator.elementSize({ x, y, w, h }, opts); + const size = calculator.elementSize({ x, y, w, h }, opts.scaleInfo); x = size.x; y = size.y; w = size.w; @@ -41,7 +41,7 @@ export function drawHoverWrapper(ctx: CanvasRenderingContext2D | ViewContext2D, const { angle = 0 } = elem; if (opts?.calculator) { const { calculator } = opts; - const size = calculator.elementSize({ x, y, w, h }, opts); + const size = calculator.elementSize({ x, y, w, h }, opts.scaleInfo); x = size.x; y = size.y; w = size.w; @@ -93,7 +93,7 @@ export function drawElementControllers( if (opts?.calculator) { const { calculator } = opts; - const size = calculator.elementSize({ x, y, w, h }, opts); + const size = calculator.elementSize({ x, y, w, h }, opts.scaleInfo); x = size.x; y = size.y; w = size.w; @@ -133,15 +133,13 @@ export function drawElementListShadows( const { angle = 0 } = elem; if (opts?.calculator) { const { calculator } = opts; - const size = calculator.elementSize({ x, y, w, h }, opts); + const size = calculator.elementSize({ x, y, w, h }, opts.scaleInfo); x = size.x; y = size.y; w = size.w; h = size.h; } - const vertexes = rotateElementVertexes({ x, y, w, h, angle }); - if (vertexes.length >= 2) { ctx.setLineDash([]); ctx.lineWidth = 1; diff --git a/packages/core/src/middleware/select/index.ts b/packages/core/src/middleware/select/index.ts index 0cfe937..9ca8e00 100644 --- a/packages/core/src/middleware/select/index.ts +++ b/packages/core/src/middleware/select/index.ts @@ -1,5 +1,5 @@ import type { Point, PointWatcherEvent, BoardMiddleware, ElementSize } from '@idraw/types'; -import { drawPointWrapper, drawHoverWrapper, drawElementControllers, drawElementListShadows } from './draw-wrapper'; +import { drawPointWrapper, drawHoverWrapper, drawElementControllers } from './draw-wrapper'; export const MiddlewareSelector: BoardMiddleware = (opts) => { const { viewer, sharer, viewContent, calculator } = opts; @@ -43,10 +43,9 @@ export const MiddlewareSelector: BoardMiddleware = (opts) => { if (sharer.getSharedStorage(keyActionType) === 'drag') { sharer.setSharedStorage(keyHoverElementSize, null); } else if (data) { - const result = calculator.getPointElement(helperContext, e.point, data, getScaleInfo()); + const result = calculator.getPointElement(e.point, data, getScaleInfo()); if (result.element) { const { x, y, w, h, angle } = result.element; - // const { x, y, w, h, angle } = calculator.elementSize(result.element, getScaleInfo()); sharer.setSharedStorage(keyHoverElementSize, { x, y, w, h, angle }); viewer.drawFrame(); return; @@ -60,16 +59,23 @@ export const MiddlewareSelector: BoardMiddleware = (opts) => { pointStart: (e: PointWatcherEvent) => { const data = sharer.getActiveStorage('data'); if (data) { - const result = calculator.getPointElement(helperContext, e.point, data, getScaleInfo()); + const result = calculator.getPointElement(e.point, data, getScaleInfo()); sharer.setActiveStorage('selectedIndexs', result.index >= 0 ? [result.index] : []); } + const _actionType = sharer.getSharedStorage(keyActionType); + const _hoverElementSize = sharer.getSharedStorage(keyHoverElementSize); if (getIndex() >= 0) { sharer.setSharedStorage(keyActionType, 'drag'); prevPoint = e.point; - } else if (sharer.getSharedStorage(keyActionType) !== null || sharer.getSharedStorage(keyHoverElementSize) !== null) { - sharer.setSharedStorage(keyActionType, null); - sharer.setSharedStorage(keyHoverElementSize, null); - viewer.drawFrame(); + } else if (_actionType !== null || _hoverElementSize !== null) { + if (_actionType === 'click') { + sharer.setSharedStorage(keyActionType, null); + sharer.setSharedStorage(keyHoverElementSize, null); + } else { + sharer.setSharedStorage(keyActionType, null); + sharer.setSharedStorage(keyHoverElementSize, null); + viewer.drawFrame(); + } } }, pointMove: (e: PointWatcherEvent) => { @@ -95,15 +101,25 @@ export const MiddlewareSelector: BoardMiddleware = (opts) => { }, pointEnd(e: PointWatcherEvent) { sharer.setSharedStorage(keyActionType, 'click'); - viewer.drawFrame(); + const data = sharer.getActiveStorage('data'); + if (data) { + const result = calculator.getPointElement(e.point, data, sharer.getActiveScaleInfo()); + if (result.element) { + viewer.drawFrame(); + } + } }, beforeDrawFrame({ snapshot }) { const { activeStore, sharedStore } = snapshot; - const { data, selectedIndexs, scale, offsetLeft, offsetTop, offsetRight, offsetBottom } = activeStore; + const { data, selectedIndexs, scale, offsetLeft, offsetTop, offsetRight, offsetBottom, width, height, contextHeight, contextWidth, devicePixelRatio } = + activeStore; + + const scaleInfo = { scale, offsetLeft, offsetTop, offsetRight, offsetBottom }; + const viewSize = { width, height, contextHeight, contextWidth, devicePixelRatio }; const hoverElement: ElementSize = sharedStore[keyHoverElementSize]; - const drawOpts = { calculator, scale, offsetLeft, offsetTop, offsetRight, offsetBottom }; + const drawOpts = { calculator, scaleInfo, viewSize }; if (hoverElement) { drawHoverWrapper(helperContext, hoverElement, drawOpts); } diff --git a/packages/idraw/dev/data.ts b/packages/idraw/dev/data.ts index f92d2bb..8d3503d 100644 --- a/packages/idraw/dev/data.ts +++ b/packages/idraw/dev/data.ts @@ -207,6 +207,22 @@ const data: Data = { ] }; +// const data: Data = { +// elements: [ +// { +// uuid: 'xxxx-0005', +// x: 300, +// y: 300, +// w: 100, +// h: 100, +// type: 'circle', +// desc: { +// bgColor: '#009688' +// } +// } +// ] +// }; + export function getData() { return deepClone(data); } diff --git a/packages/idraw/dev/main.ts b/packages/idraw/dev/main.ts index 52d07ec..5049def 100644 --- a/packages/idraw/dev/main.ts +++ b/packages/idraw/dev/main.ts @@ -21,20 +21,20 @@ const idraw = new iDraw( ); idraw.setData(data); // idraw.scale(0.5); -idraw.scale(1.5); +idraw.scale(2); idraw.scrollX(-80); idraw.scrollY(-80); -const mount2 = document.querySelector('#mount') as HTMLDivElement; -const data2 = getData(); -const idraw2 = new iDraw( - mount2, - Object.assign({}, opts, { - // contextWidth: 500, - // contextHeight: 400 - }) -); -idraw2.setData(data2); +// const mount2 = document.querySelector('#mount') as HTMLDivElement; +// const data2 = getData(); +// const idraw2 = new iDraw( +// mount2, +// Object.assign({}, opts, { +// // contextWidth: 500, +// // contextHeight: 400 +// }) +// ); +// idraw2.setData(data2); // const parseData = idraw.getData(); diff --git a/packages/renderer/src/draw/circle.ts b/packages/renderer/src/draw/circle.ts index c86c781..29d33ac 100644 --- a/packages/renderer/src/draw/circle.ts +++ b/packages/renderer/src/draw/circle.ts @@ -4,7 +4,8 @@ import { rotateElement } from '@idraw/util'; export function drawCircle(ctx: ViewContext2D, elem: Element<'circle'>, opts: RendererDrawElementOptions) { const { desc, angle } = elem; const { bgColor = '#000000', borderColor = '#000000', borderWidth = 0 } = desc; - const { calculator, scale, offsetTop, offsetBottom, offsetLeft, offsetRight } = opts; + const { calculator, scaleInfo } = opts; + const { scale, offsetTop, offsetBottom, offsetLeft, offsetRight } = scaleInfo; const { x, y, w, h } = calculator.elementSize({ x: elem.x, y: elem.y, w: elem.w, h: elem.h }, { scale, offsetTop, offsetBottom, offsetLeft, offsetRight }); rotateElement(ctx, { x, y, w, h, angle }, () => { const a = w / 2; diff --git a/packages/renderer/src/draw/elements.ts b/packages/renderer/src/draw/elements.ts index 85b83c4..76bd1f6 100644 --- a/packages/renderer/src/draw/elements.ts +++ b/packages/renderer/src/draw/elements.ts @@ -30,7 +30,7 @@ export function drawElement(ctx: ViewContext2D, elem: Element, opts export function drawElementList(ctx: ViewContext2D, elements: Data['elements'], opts: RendererDrawElementOptions) { for (let i = elements.length - 1; i >= 0; i--) { const elem = elements[i]; - if (!opts.calculator.isElementInView(elem, opts)) { + if (!opts.calculator.isElementInView(elem, opts.scaleInfo, opts.viewSize)) { continue; } try { diff --git a/packages/renderer/src/draw/image.ts b/packages/renderer/src/draw/image.ts index b7cbd8b..73671bd 100644 --- a/packages/renderer/src/draw/image.ts +++ b/packages/renderer/src/draw/image.ts @@ -3,8 +3,8 @@ import { rotateElement } from '@idraw/util'; export function drawImage(ctx: ViewContext2D, elem: Element<'image'>, opts: RendererDrawElementOptions) { const content = opts.loader.getContent(elem.uuid); - const { calculator, scale, offsetTop, offsetBottom, offsetLeft, offsetRight } = opts; - const { x, y, w, h, angle } = calculator.elementSize(elem, { scale, offsetTop, offsetBottom, offsetLeft, offsetRight }); + const { calculator, scaleInfo } = opts; + const { x, y, w, h, angle } = calculator.elementSize(elem, scaleInfo); rotateElement(ctx, { x, y, w, h, angle }, () => { if (!content) { opts.loader.load(elem as Element<'image'>); diff --git a/packages/renderer/src/draw/rect.ts b/packages/renderer/src/draw/rect.ts index cc491dd..950e326 100644 --- a/packages/renderer/src/draw/rect.ts +++ b/packages/renderer/src/draw/rect.ts @@ -3,14 +3,11 @@ import { rotateElement } from '@idraw/util'; export function drawRect(ctx: ViewContext2D, elem: Element<'rect'>, opts: RendererDrawElementOptions) { // const { desc } = elem; - const { calculator, scale, offsetTop, offsetBottom, offsetLeft, offsetRight } = opts; + const { calculator, scaleInfo } = opts; - const { x, y, w, h, angle } = calculator.elementSize( - { x: elem.x, y: elem.y, w: elem.w, h: elem.h }, - { scale, offsetTop, offsetBottom, offsetLeft, offsetRight } - ); + const { x, y, w, h, angle } = calculator.elementSize({ x: elem.x, y: elem.y, w: elem.w, h: elem.h }, scaleInfo); rotateElement(ctx, { x, y, w, h, angle }, () => { - let r: number = (elem.desc.borderRadius || 0) * scale; + let r: number = (elem.desc.borderRadius || 0) * scaleInfo.scale; r = Math.min(r, w / 2, h / 2); if (w < r * 2 || h < r * 2) { r = 0; diff --git a/packages/renderer/src/index.ts b/packages/renderer/src/index.ts index 2367fb4..72a8ee6 100644 --- a/packages/renderer/src/index.ts +++ b/packages/renderer/src/index.ts @@ -45,15 +45,24 @@ export class Renderer extends EventEmitter implements BoardRen scale(num: number) { const { sharer } = this._opts; - const { data, offsetTop, offsetBottom, offsetLeft, offsetRight } = sharer.getActiveStoreSnapshot(); - // TODO calc offset data + const { data, offsetTop, offsetBottom, offsetLeft, offsetRight, width, height, contextHeight, contextWidth, devicePixelRatio } = + sharer.getActiveStoreSnapshot(); if (data) { this.drawData(data, { - scale: num, - offsetTop, - offsetBottom, - offsetLeft, - offsetRight + scaleInfo: { + scale: num, + offsetTop, + offsetBottom, + offsetLeft, + offsetRight + }, + viewSize: { + width, + height, + contextHeight, + contextWidth, + devicePixelRatio + } }); } } diff --git a/packages/types/src/lib/renderer.ts b/packages/types/src/lib/renderer.ts index 965e7e9..e3a149e 100644 --- a/packages/types/src/lib/renderer.ts +++ b/packages/types/src/lib/renderer.ts @@ -1,4 +1,4 @@ -import type { ViewContent, ViewScaleInfo, ViewCalculator } from './view'; +import type { ViewContent, ViewScaleInfo, ViewCalculator, ViewSizeInfo } from './view'; import type { Element } from './element'; import type { LoaderEventMap, LoadElementType, LoadContent } from './loader'; import type { UtilEventEmitter } from './util'; @@ -23,7 +23,10 @@ export interface RendererLoader extends UtilEventEmitter { getContent(uuid: string): LoadContent | null; } -export type RendererDrawOptions = ViewScaleInfo; +export interface RendererDrawOptions { + viewSize: ViewSizeInfo; + scaleInfo: ViewScaleInfo; +} export interface RendererDrawElementOptions extends RendererDrawOptions { loader: RendererLoader; diff --git a/packages/types/src/lib/view.ts b/packages/types/src/lib/view.ts index a8bb61e..9dc644c 100644 --- a/packages/types/src/lib/view.ts +++ b/packages/types/src/lib/view.ts @@ -1,5 +1,5 @@ import type { Element, ElementType, ElementSize } from './element'; -import type { Point, PointSize } from './point'; +import type { Point } from './point'; import type { Data } from './data'; import type { ViewContext2D } from './context2d'; @@ -31,16 +31,11 @@ export interface ViewCalculatorOptions { export interface ViewCalculator { viewScale(num: number, prevScaleInfo: ViewScaleInfo, viewSize: ViewSizeInfo): ViewScaleInfo; - isElementInView(elem: Element, scaleInfo: ViewScaleInfo): boolean; - isPointInElement(ctx: ViewContext2D | CanvasRenderingContext2D | ViewContext2D, p: Point, elem: Element, scaleInfo: ViewScaleInfo): boolean; + isElementInView(elem: Element, scaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): boolean; + isPointInElement(p: Point, elem: Element, scaleInfo: ViewScaleInfo): boolean; elementSize(size: ElementSize, scaleInfo: ViewScaleInfo): ElementSize; viewScroll(opts: { moveX?: number; moveY?: number }, scaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): ViewScaleInfo; - getPointElement( - ctx: ViewContext2D | CanvasRenderingContext2D | ViewContext2D, - p: Point, - data: Data, - scaleInfo: ViewScaleInfo - ): { index: number; element: null | Element }; + getPointElement(p: Point, data: Data, scaleInfo: ViewScaleInfo): { index: number; element: null | Element }; // rotateElementSize(elemSize: ElementSize): PointSize[]; // pointToViewPoint( p: Point): Point; // TODO