From db9e8ebead25471fbc9d79627cbde17fb1e308db Mon Sep 17 00:00:00 2001 From: wxm <157215725@qq.com> Date: Tue, 21 Jan 2025 18:05:38 +0800 Subject: [PATCH] feat: TransformRect2D --- src/editor/CanvasItemEditor.ts | 96 +++++++++++++++++---------------- src/scene/2d/Node2D.ts | 6 +-- src/scene/2d/TransformRect2D.ts | 58 ++++++++++++++++++++ src/scene/2d/index.ts | 1 + src/scene/main/CanvasContext.ts | 37 +++++++------ 5 files changed, 133 insertions(+), 65 deletions(-) create mode 100644 src/scene/2d/TransformRect2D.ts diff --git a/src/editor/CanvasItemEditor.ts b/src/editor/CanvasItemEditor.ts index e1a1901..00c8c5f 100644 --- a/src/editor/CanvasItemEditor.ts +++ b/src/editor/CanvasItemEditor.ts @@ -1,8 +1,14 @@ import type { InputEvent, InputEventKey, PointerInputEvent, PropertyDeclaration } from '../core' import type { CanvasItem, CanvasItemStyle } from '../scene' -import { Control, Node2D, Ruler, Scaler, XScrollBar, YScrollBar } from '../scene' +import { Control, Node2D, Ruler, Scaler, TransformRect2D, XScrollBar, YScrollBar } from '../scene' export class CanvasItemEditor extends Control { + protected _pointerStart?: CanvasItemStyle + protected _pointerOffset?: { x: number, y: number } + selected?: CanvasItem + dragging?: CanvasItem + hovered?: CanvasItem + hover = new Node2D({ name: 'hover', internalMode: 'back', @@ -15,30 +21,21 @@ export class CanvasItemEditor extends Control { }, }) - selectionRect = new Node2D({ - name: 'selectionRect', + transformRect = new TransformRect2D({ + name: 'transformRect', internalMode: 'back', style: { visibility: 'hidden', - width: 1, - height: 1, - backgroundColor: 0x00FF000F, - outlineStyle: 'solid', - outlineColor: 0x00FF00FF, - outlineWidth: 2, pointerEvents: 'none', }, }) - selector = new Node2D({ - name: 'selector', - }) - scaler = new Scaler({ internalMode: 'back', }).on('updateScale', (scale) => { this.ruler.scale = scale this._updateScrollbars() + this._updateSelectionRect() }) xScrollBar = new XScrollBar({ @@ -76,15 +73,11 @@ export class CanvasItemEditor extends Control { }, [ this.drawboard, this.hover, - this.selectionRect, + this.transformRect, this.xScrollBar, this.yScrollBar, ]) - protected _pointerStart?: CanvasItemStyle - protected _pointerOffset?: { x: number, y: number } - selected?: CanvasItem - constructor() { super() this._onPointerdown = this._onPointerdown.bind(this) @@ -127,53 +120,51 @@ export class CanvasItemEditor extends Control { protected _onPointerdown(e: PointerInputEvent): void { const target = e.target this._pointerOffset = { x: e.offsetX, y: e.offsetY } + this.selected = target + this.dragging = target if (target instanceof Node2D) { - this.selected = target this._pointerStart = target.style.clone() } else { - this.selected = undefined this._pointerStart = undefined - this.selectionRect.style.visibility = 'visible' - this.selectionRect.style.left = e.screen.x - this.selectionRect.style.top = e.screen.y - this.selectionRect.style.width = 1 - this.selectionRect.style.height = 1 } - this._onHover() + this._updateHover() + this._updateSelectionRect() } protected _onPointermove(e: PointerInputEvent): void { - const { selected, _pointerStart, _pointerOffset } = this + const target = e.target + const { selected, dragging, _pointerStart, _pointerOffset } = this + if (selected && target?.is(selected)) { + this.hovered = undefined + } + else { + this.hovered = target + } const offset = _pointerOffset ? { x: (e.offsetX - _pointerOffset.x), y: (e.offsetY - _pointerOffset.y) } : { x: 0, y: 0 } - if (selected && _pointerStart) { - selected.style.left = _pointerStart.left + offset.x - selected.style.top = _pointerStart.top + offset.y - } - else { - if (this.selectionRect.isVisibleInTree()) { - this.selectionRect.style.width = offset.x - this.selectionRect.style.height = offset.y - } + if (dragging && _pointerStart) { + dragging.style.left = _pointerStart.left + offset.x + dragging.style.top = _pointerStart.top + offset.y } - this._onHover() + this._updateHover() + this._updateSelectionRect() } protected _onPointerup(): void { - this.selected = undefined - this.selectionRect.style.visibility = 'hidden' - this._onHover() + this.dragging = undefined + this._updateHover() + this._updateSelectionRect() } - protected _onHover(): void { - const selected = this.selected - if (selected instanceof Node2D) { + protected _updateHover(): void { + const hovered = this.hovered + if (hovered instanceof Node2D) { this.hover.style.visibility = 'visible' - this.hover.style.width = selected.style.width - this.hover.style.height = selected.style.height - this.hover.transform.set(selected.transform) + this.hover.style.width = hovered.style.width + this.hover.style.height = hovered.style.height + this.hover.transform.set(hovered.transform) this.hover.requestRedraw() } else { @@ -181,6 +172,19 @@ export class CanvasItemEditor extends Control { } } + protected _updateSelectionRect(): void { + if (this.selected) { + this.transformRect.style.visibility = 'visible' + this.transformRect.style.width = this.selected.style.width + this.transformRect.style.height = this.selected.style.height + this.transformRect.transform.set((this.selected as Node2D).transform) + this.transformRect.requestReflow() + } + else { + this.transformRect.style.visibility = 'hidden' + } + } + protected _updateScrollbars(): void { const scale = this.ruler.scale const scrollHeight = this.drawboard.style.height * scale diff --git a/src/scene/2d/Node2D.ts b/src/scene/2d/Node2D.ts index 924260a..74507d8 100644 --- a/src/scene/2d/Node2D.ts +++ b/src/scene/2d/Node2D.ts @@ -1,4 +1,4 @@ -import type { InputEvent, InputEventKey, PointerInputEvent } from '../../core' +import type { InputEvent, InputEventKey, PointerInputEvent, PropertyDeclaration } from '../../core' import type { CanvasBatchable, CanvasItemProperties, Node } from '../main' import { customNode, Rect2, Transform2D } from '../../core' import { CanvasItem } from '../main' @@ -20,8 +20,8 @@ export class Node2D extends CanvasItem { .append(children) } - protected override _updateStyleProperty(key: PropertyKey, value: any, oldValue: any): void { - super._updateStyleProperty(key, value, oldValue) + protected _updateStyleProperty(key: PropertyKey, value: any, oldValue: any, declaration?: PropertyDeclaration): void { + super._updateStyleProperty(key, value, oldValue, declaration) switch (key) { case 'width': diff --git a/src/scene/2d/TransformRect2D.ts b/src/scene/2d/TransformRect2D.ts new file mode 100644 index 0000000..202d180 --- /dev/null +++ b/src/scene/2d/TransformRect2D.ts @@ -0,0 +1,58 @@ +import type { PropertyDeclaration } from '../../core' +import type { Rectangulable } from '../main/interfaces' +import type { Node2DProperties } from './Node2D' +import { property } from '../../core' +import { Texture2D } from '../resources' +import { Node2D } from './Node2D' + +export interface TransformRect2DProperties extends Node2DProperties { + // +} + +export class TransformRect2D extends Node2D implements Rectangulable { + @property({ default: 6 }) declare size: number + + protected _updateStyleProperty(key: PropertyKey, value: any, oldValue: any, declaration?: PropertyDeclaration): void { + super._updateStyleProperty(key, value, oldValue, declaration) + + switch (key) { + case 'width': + case 'height': + this.requestRedraw() + break + } + } + + protected _drawCircle(x: number, y: number): void { + this.context.arc(x, y, this.size, 0, Math.PI * 2, true) + this.context.fillStyle = Texture2D.WHITE + this.context.fill() + + this.context.arc(x, y, this.size, 0, Math.PI * 2, true) + this.context.strokeStyle = 'rgba(0, 0, 0, 0.2)' + this.context.stroke() + } + + protected _drawEllipse(x: number, y: number): void { + this.context.roundRect(x - this.size, y - this.size * 2, this.size * 2, this.size * 4, this.size) + this.context.fillStyle = Texture2D.WHITE + this.context.fill() + + this.context.roundRect(x - this.size, y - this.size * 2, this.size * 2, this.size * 4, this.size) + this.context.strokeStyle = 'rgba(0, 0, 0, 0.2)' + this.context.stroke() + } + + protected override _draw(): void { + const { width, height } = this.style + this.context.rect(0, 0, width, height) + this.context.strokeStyle = '#00FF00' + this.context.stroke() + this._drawCircle(0, 0) + this._drawCircle(width, height) + this._drawCircle(0, height) + this._drawEllipse(0, height / 2) + this._drawCircle(width, 0) + this._drawEllipse(width, height / 2) + } +} diff --git a/src/scene/2d/index.ts b/src/scene/2d/index.ts index 89905d8..e03da76 100644 --- a/src/scene/2d/index.ts +++ b/src/scene/2d/index.ts @@ -4,4 +4,5 @@ export * from './Lottie2D' export * from './Node2D' export * from './Text2D' export * from './TextureRect2D' +export * from './TransformRect2D' export * from './Video2D' diff --git a/src/scene/main/CanvasContext.ts b/src/scene/main/CanvasContext.ts index 62f89af..67e9151 100644 --- a/src/scene/main/CanvasContext.ts +++ b/src/scene/main/CanvasContext.ts @@ -32,8 +32,8 @@ export class CanvasContext extends Path2D { miterLimit?: number _defaultStyle = Texture2D.EMPTY - _stroke: StrokedGraphics[] = [] - _fille: FilledGraphics[] = [] + _strokes: StrokedGraphics[] = [] + _fills: FilledGraphics[] = [] stroke(): void { let texture: Texture2D = this._defaultStyle @@ -47,7 +47,7 @@ export class CanvasContext extends Path2D { } if (this.curves.length) { - this._stroke.push({ + this._strokes.push({ path: new Path2D(this), texture, textureTransform: this.textureTransform, @@ -86,7 +86,7 @@ export class CanvasContext extends Path2D { texture = new ColorTexture(this.fillStyle) } } - this._fille.push({ + this._fills.push({ path: new Path2D(this), texture, textureTransform: this.textureTransform, @@ -104,8 +104,8 @@ export class CanvasContext extends Path2D { this.lineJoin = source.lineJoin this.lineWidth = source.lineWidth this.miterLimit = source.miterLimit - this._stroke = source._stroke.slice() - this._fille = source._fille.slice() + this._strokes = source._strokes.slice() + this._fills = source._fills.slice() return this } @@ -118,8 +118,8 @@ export class CanvasContext extends Path2D { this.lineJoin = undefined this.lineWidth = undefined this.miterLimit = undefined - this._stroke.length = 0 - this._fille.length = 0 + this._strokes.length = 0 + this._fills.length = 0 return this } @@ -154,6 +154,7 @@ export class CanvasContext extends Path2D { let uvs: number[] = [] let startUv = 0 let texture: Texture2D | undefined + let verticesLen = vertices.length const push = (type: CanvasBatchable['type']): void => { batchables.push({ @@ -167,14 +168,15 @@ export class CanvasContext extends Path2D { indices = [] uvs = [] texture = undefined + verticesLen = vertices.length } - let verticesLen = vertices.length - - for (let len = this._fille.length, i = 0; i < len; i++) { - const graphics = this._fille[i] - texture ??= graphics.texture - if (texture !== graphics.texture) { + for (let len = this._fills.length, i = 0; i < len; i++) { + const graphics = this._fills[i] + if (!texture) { + texture = graphics.texture + } + else if (!texture.is(graphics.texture)) { push('fill') } startUv = vertices.length @@ -183,6 +185,9 @@ export class CanvasContext extends Path2D { indices, }) this.buildUvs(startUv, vertices, uvs, graphics.texture, graphics.textureTransform) + if (!texture) { + texture = graphics.texture + } } if (vertices.length - verticesLen > 0) { @@ -191,9 +196,9 @@ export class CanvasContext extends Path2D { verticesLen = vertices.length - for (let len = this._stroke.length, i = 0; i < len; i++) { + for (let len = this._strokes.length, i = 0; i < len; i++) { startUv = vertices.length - const graphics = this._stroke[i] + const graphics = this._strokes[i] texture ??= graphics.texture graphics.path.strokeTriangulate({ vertices,