From 25e7616e591fb0cb4a9da60f11a9be39d8eee13a Mon Sep 17 00:00:00 2001 From: zhouxinyu Date: Tue, 11 Jun 2024 20:00:05 +0800 Subject: [PATCH 1/4] feat: support rect angle --- .../vstory/demo/src/demos/GraphicEdit.tsx | 38 ++++++++++ .../src/edit/edit-component/base-selection.ts | 15 +++- .../edit-control/transform-control.ts | 6 +- .../vstory/src/edit/edit-component/index.ts | 2 + .../src/edit/edit-component/rect-selection.ts | 73 +++++++++++++++++++ .../vstory/src/story/character/base/base.ts | 26 ++++--- .../story/character/component/character.ts | 4 + .../component/characters/character-rect.ts | 6 ++ .../component/graphic/graphic-text.ts | 21 +++--- .../character/component/graphic/graphic.ts | 10 ++- .../story/character/component/graphic/rect.ts | 7 ++ .../src/story/character/runtime-interface.ts | 2 + 12 files changed, 185 insertions(+), 25 deletions(-) create mode 100644 packages/vstory/src/edit/edit-component/rect-selection.ts diff --git a/packages/vstory/demo/src/demos/GraphicEdit.tsx b/packages/vstory/demo/src/demos/GraphicEdit.tsx index 72a51084..89c1025f 100644 --- a/packages/vstory/demo/src/demos/GraphicEdit.tsx +++ b/packages/vstory/demo/src/demos/GraphicEdit.tsx @@ -41,6 +41,29 @@ export const GraphicEdit = () => { ] } } + }, + { + type: 'RectComponent', + id: 'rect', + zIndex: 0, + position: { + top: 40, + left: 250, + width: 200, + height: 100 + }, + options: { + graphic: { + fill: 'red', + visible: false + }, + text: { + text: 'title2', + fill: 'black' + }, + angle: 0, + shapePoints: [] + } } ], acts: [ @@ -68,6 +91,21 @@ export const GraphicEdit = () => { } } ] + }, + { + characterId: 'rect', + characterActions: [ + { + startTime: 1, + duration: 800, + action: 'appear', + payload: { + animation: { + duration: 700 + } + } + } + ] } ] } diff --git a/packages/vstory/src/edit/edit-component/base-selection.ts b/packages/vstory/src/edit/edit-component/base-selection.ts index ed376ff2..599099a5 100644 --- a/packages/vstory/src/edit/edit-component/base-selection.ts +++ b/packages/vstory/src/edit/edit-component/base-selection.ts @@ -3,9 +3,12 @@ import { EditActionEnum, type IEditActionInfo, type IEditComponent } from '../in import { StoryEvent } from '../../story/interface/runtime-interface'; import type { Edit } from '../edit'; import type { AbstractComponent } from '@visactor/vrender-components'; -import type { ITransformControl } from './edit-control/transform-control'; +import type { ITransformControl, IUpdateParams } from './edit-control/transform-control'; import { TransformControl, type TransformAttributes } from './edit-control/transform-control'; import { throwError } from '../../util/common'; +import type { VRenderPointerEvent } from '../../interface/type'; +import { IGraphic } from '@visactor/vrender-core'; +import type { ICharacter } from '../../story/character'; export abstract class BaseSelection implements IEditComponent { declare readonly level: number; @@ -14,11 +17,13 @@ export abstract class BaseSelection implements IEditComponent { protected _isSelection = false; isEditing: boolean = false; protected _layoutComponent?: ITransformControl; + protected _activeCharacter?: ICharacter | null; constructor(public readonly edit: Edit) {} editEnd(): void { this.isEditing = false; this._actionInfo = null; + this._activeCharacter = null; this.inActiveLayoutComponent(); } abstract checkAction(actionInfo: IEditSelectionInfo): boolean; @@ -26,6 +31,9 @@ export abstract class BaseSelection implements IEditComponent { startEdit(actionInfo: IEditActionInfo) { this.isEditing = true; this._actionInfo = actionInfo; + if (actionInfo && (actionInfo as IEditSelectionInfo).character) { + this._activeCharacter = (actionInfo as IEditSelectionInfo).character; + } this.activeLayoutComponent(); } @@ -47,9 +55,14 @@ export abstract class BaseSelection implements IEditComponent { } }); + component.onUpdate(this.handlerTransformChange.bind(this)); return component; } + protected handlerTransformChange(data: IUpdateParams, event?: VRenderPointerEvent) { + return; + } + protected _createLayoutComponent(attributes: Partial): ITransformControl | undefined { return new TransformControl(attributes); } diff --git a/packages/vstory/src/edit/edit-component/edit-control/transform-control.ts b/packages/vstory/src/edit/edit-component/edit-control/transform-control.ts index 92b71e0f..65c8332d 100644 --- a/packages/vstory/src/edit/edit-component/edit-control/transform-control.ts +++ b/packages/vstory/src/edit/edit-component/edit-control/transform-control.ts @@ -28,7 +28,7 @@ import { Edit } from '../../edit'; type AnchorDirection = 'top' | 'bottom' | 'left-top' | 'left-bottom' | 'right' | 'left' | 'right-top' | 'right-bottom'; const fixedAngles = [0, Math.PI / 2, Math.PI, (Math.PI * 3) / 2, Math.PI * 2]; -const maxAngleDifference = (10 / 180) * Math.PI; // 10 degrees +const maxAngleDifference = (3 / 180) * Math.PI; // 10 degrees export type TransformAttributes = { padding?: number | [number, number, number, number]; @@ -96,6 +96,10 @@ export interface ITransformControl extends IGroup { updateSubBounds: (b: IAABBBoundsLike) => void; onActive: () => void; onInActive: () => void; + onUpdate: (cb: (data: IUpdateParams, event?: VRenderPointerEvent) => Partial | false) => void; + onEditorEnd: (cb: (event?: VRenderPointerEvent) => void) => void; + onEditorStart: (cb: (event?: VRenderPointerEvent) => void) => void; + onUnTransStart: (cb: (event: PointerEvent) => void) => void; } export class TransformControl extends AbstractComponent> implements ITransformControl { diff --git a/packages/vstory/src/edit/edit-component/index.ts b/packages/vstory/src/edit/edit-component/index.ts index 994bbac9..c976e2bc 100644 --- a/packages/vstory/src/edit/edit-component/index.ts +++ b/packages/vstory/src/edit/edit-component/index.ts @@ -2,11 +2,13 @@ import { CommonEditComponent } from './common'; import { BoxSelection } from './box-selection'; import { TextSelection } from './text-selection'; import { RichTextSelection } from './richtext-selection'; +import { RectSelection } from './rect-selection'; import { Edit } from '../edit'; export function loadAllSelection() { Edit.registerEditComponent('common', CommonEditComponent); Edit.registerEditComponent('text', TextSelection); Edit.registerEditComponent('richtext', RichTextSelection); + Edit.registerEditComponent('rect', RectSelection); Edit.registerEditComponent('box-selection', BoxSelection); } diff --git a/packages/vstory/src/edit/edit-component/rect-selection.ts b/packages/vstory/src/edit/edit-component/rect-selection.ts new file mode 100644 index 00000000..7eb73d98 --- /dev/null +++ b/packages/vstory/src/edit/edit-component/rect-selection.ts @@ -0,0 +1,73 @@ +import type { IEditSelectionInfo } from '../interface'; +import { EditActionEnum, type IEditActionInfo, type IEditComponent } from '../interface'; +import { StoryEvent } from '../../story/interface/runtime-interface'; +import type { Edit } from '../edit'; +import { BaseSelection } from './base-selection'; +import type { TransformAttributes, ITransformControl, IUpdateParams } from './edit-control/transform-control'; +import { TransformControl } from './edit-control/transform-control'; +import type { VRenderPointerEvent } from '../../interface/type'; + +export class RectSelection extends BaseSelection implements IEditComponent { + readonly level = 3; + readonly type: string = 'rect'; + + constructor(public readonly edit: Edit) { + super(edit); + } + + protected _createLayoutComponent(attributes: Partial): ITransformControl { + return new TransformControl(attributes); + } + + editEnd(): void { + super.editEnd(); + return; + } + checkAction(actionInfo: IEditSelectionInfo): boolean { + if (this.isEditing) { + return this.checkActionWhileEditing(actionInfo); + } + return this.checkActionWhileNoEditing(actionInfo); + } + + checkActionWhileEditing(actionInfo: IEditSelectionInfo): boolean { + // 点到其他内容了,return false + if (actionInfo.type === EditActionEnum.singleSelection && actionInfo.detail.graphicType !== this.type) { + return false; + } + + if (actionInfo.event.type === 'pointerdown') { + if (!actionInfo.event.target || (actionInfo.event.target as any).parent !== this._layoutComponent) { + return false; + } + } + return true; + } + + protected handlerTransformChange(data: IUpdateParams, event?: VRenderPointerEvent): void { + if (this._activeCharacter) { + this._activeCharacter.setAttributes(data); + } + } + + checkActionWhileNoEditing(actionInfo: IEditSelectionInfo): boolean { + if (actionInfo.type === EditActionEnum.singleSelection && actionInfo.detail.graphicType === this.type) { + this.startEdit(actionInfo); + // graphic + return true; + } + + return false; + } + + startEdit(actionInfo: IEditActionInfo) { + super.startEdit(actionInfo); + this.edit.startEdit({ + type: 'boxSelection', + actionInfo: this._actionInfo, + updateCharacter: (params: any) => { + // nothing 不支持任何修改 + } + }); + } +} diff --git a/packages/vstory/src/story/character/base/base.ts b/packages/vstory/src/story/character/base/base.ts index 0f5e5162..d30cf6b5 100644 --- a/packages/vstory/src/story/character/base/base.ts +++ b/packages/vstory/src/story/character/base/base.ts @@ -1,8 +1,8 @@ import { isValid, merge } from '@visactor/vutils'; -import { ICharacterInitOption, ICharacterPickInfo } from '../runtime-interface'; -import { ICharacter, ICharacterSpec } from '..'; -import { IGroup } from '@visactor/vrender-core'; -import { StoryEvent } from '../../interface'; +import type { ICharacterInitOption, ICharacterPickInfo } from '../runtime-interface'; +import type { ICharacter, ICharacterSpec } from '..'; +import type { IGroup } from '@visactor/vrender-core'; +import type { StoryEvent } from '../../interface'; export abstract class CharacterBase implements ICharacter { readonly id: string; @@ -33,17 +33,21 @@ export abstract class CharacterBase implements ICharacter { } } + setAttributes(attr: Record) { + throw new Error('请重载setAttributes函数'); + } + tickTo(t: number): void { throw new Error('Method not implemented.'); } - public init() { + init() { this._initRuntime(); this._parserSpec(); this._initGraphics(); } - public reset() { + reset() { this.clearCharacter(); this.init(); } @@ -55,11 +59,13 @@ export abstract class CharacterBase implements ICharacter { abstract show(): void; abstract hide(): void; - public getPositionData() {} + getPositionData() { + return; + } - public abstract getGraphicParent(): IGroup; + abstract getGraphicParent(): IGroup; - public abstract clearCharacter(): void; + abstract clearCharacter(): void; - public abstract checkEvent(event: StoryEvent): false | ICharacterPickInfo; + abstract checkEvent(event: StoryEvent): false | ICharacterPickInfo; } diff --git a/packages/vstory/src/story/character/component/character.ts b/packages/vstory/src/story/character/component/character.ts index 4484edce..ca65410f 100644 --- a/packages/vstory/src/story/character/component/character.ts +++ b/packages/vstory/src/story/character/component/character.ts @@ -54,6 +54,10 @@ export abstract class CharacterComponent extends CharacterBase { this.hide(); } + setAttributes(attr: Record) { + this._graphic.setAttributes(attr); + } + protected abstract _createGraphic(): Graphic; protected _initRuntime(): void { diff --git a/packages/vstory/src/story/character/component/characters/character-rect.ts b/packages/vstory/src/story/character/component/characters/character-rect.ts index 377bf5ea..f177460a 100644 --- a/packages/vstory/src/story/character/component/characters/character-rect.ts +++ b/packages/vstory/src/story/character/component/characters/character-rect.ts @@ -8,4 +8,10 @@ export class CharacterComponentRect extends CharacterComponent { protected _createGraphic(): Graphic { return new GraphicRect(StoryGraphicType.RECT, this); } + + setAttributes(attr: Record): void { + this.group.setAttributes(attr); + this._graphic.setAttributes({ ...attr, x: 0, y: 0, angle: 0 }); + this._text.updateAttribute({}); + } } diff --git a/packages/vstory/src/story/character/component/graphic/graphic-text.ts b/packages/vstory/src/story/character/component/graphic/graphic-text.ts index b8ea6d0d..e8186218 100644 --- a/packages/vstory/src/story/character/component/graphic/graphic-text.ts +++ b/packages/vstory/src/story/character/component/graphic/graphic-text.ts @@ -1,7 +1,8 @@ import { getLayoutFromWidget } from '../../../utils/layout'; -import { IWidgetData } from '../../dsl-interface'; -import { CharacterGraphics } from '../character'; -import { IText, createText, ITextGraphicAttribute, TextAlignType, TextBaselineType } from '@visactor/vrender-core'; +import type { IWidgetData } from '../../dsl-interface'; +import type { CharacterGraphics } from '../character'; +import type { IText, ITextGraphicAttribute, TextAlignType, TextBaselineType } from '@visactor/vrender-core'; +import { createText } from '@visactor/vrender-core'; export const MAX_LAYOUT_SIZE = 999999; @@ -124,13 +125,13 @@ export class GraphicText { private _updateGraphicCharacterSize() { if (!this._character.graphic.getGraphicAttribute().isResized) { - const layoutRatio = this._character.graphic.getTextLayoutRatio(); - const textHeight = this._graphic.AABBBounds.height(); - const minGraphicHeight = this._character.graphic.getInitialAttributes().height; - const graphicHeight = Math.max(textHeight / (layoutRatio.bottom - layoutRatio.top), minGraphicHeight); - this._character.graphic.applyLayoutData({ - height: graphicHeight - }); + // const layoutRatio = this._character.graphic.getTextLayoutRatio(); + // const textHeight = this._graphic.AABBBounds.height(); + // const minGraphicHeight = this._character.graphic.getInitialAttributes().height; + // const graphicHeight = Math.max(textHeight / (layoutRatio.bottom - layoutRatio.top), minGraphicHeight); + // this._character.graphic.applyLayoutData({ + // height: graphicHeight + // }); this.applyLayoutData(this._character.graphic.getPositionData()); } } diff --git a/packages/vstory/src/story/character/component/graphic/graphic.ts b/packages/vstory/src/story/character/component/graphic/graphic.ts index e97e226a..38272571 100644 --- a/packages/vstory/src/story/character/component/graphic/graphic.ts +++ b/packages/vstory/src/story/character/component/graphic/graphic.ts @@ -1,8 +1,8 @@ import type { IGraphic } from '@visactor/vrender-core'; import type { IPointLike } from '@visactor/vutils'; import { isValid } from '@visactor/vutils'; -import { CharacterComponent } from '../character'; -import { IComponentCharacterSpec, IWidgetData } from '../..'; +import type { CharacterComponent } from '../character'; +import type { IComponentCharacterSpec, IWidgetData } from '../..'; import { getLayoutFromWidget } from '../../../utils/layout'; export interface IGraphicConstructor { @@ -18,7 +18,7 @@ export abstract class Graphic { get graphic() { return this._graphic; } - protected _group: IGraphic; + // protected _group: IGraphic; constructor(type: string, character: CharacterComponent) { this.type = type; @@ -52,6 +52,10 @@ export abstract class Graphic { }; } + setAttributes(attr: Record) { + throw new Error('请重载setAttributes函数'); + } + show(): void { this._graphic.setAttributes({ visible: true, diff --git a/packages/vstory/src/story/character/component/graphic/rect.ts b/packages/vstory/src/story/character/component/graphic/rect.ts index d76687cf..e9f7fefd 100644 --- a/packages/vstory/src/story/character/component/graphic/rect.ts +++ b/packages/vstory/src/story/character/component/graphic/rect.ts @@ -20,6 +20,13 @@ export class GraphicRect extends Graphic { }; } + setAttributes(attr: Record): void { + if (!this._graphic) { + return; + } + this._graphic.setAttributes(attr); + } + init() { if (!this._graphic) { this._graphic = createRect( diff --git a/packages/vstory/src/story/character/runtime-interface.ts b/packages/vstory/src/story/character/runtime-interface.ts index 15a20a89..d8fff0b1 100644 --- a/packages/vstory/src/story/character/runtime-interface.ts +++ b/packages/vstory/src/story/character/runtime-interface.ts @@ -22,6 +22,8 @@ export interface ICharacter { checkEvent: (event: StoryEvent) => false | (ICharacterPickInfo & any); updateSpec: (spec: Omit, 'id' | 'type'>) => void; + + setAttributes: (attr: Record) => void; } export interface ICharacterInitOption { From 3c9273a7acf3b399e355e298a8051c8dd80bb74f Mon Sep 17 00:00:00 2001 From: zhouxinyu Date: Tue, 11 Jun 2024 20:36:29 +0800 Subject: [PATCH 2/4] feat: support translate --- .../edit-control/transform-control.ts | 58 +++++++++++++-- .../edit-control/transform-drag.ts | 73 +++++++++++++++++++ 2 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 packages/vstory/src/edit/edit-component/edit-control/transform-drag.ts diff --git a/packages/vstory/src/edit/edit-component/edit-control/transform-control.ts b/packages/vstory/src/edit/edit-component/edit-control/transform-control.ts index 65c8332d..8a35631e 100644 --- a/packages/vstory/src/edit/edit-component/edit-control/transform-control.ts +++ b/packages/vstory/src/edit/edit-component/edit-control/transform-control.ts @@ -23,6 +23,7 @@ import { DRAG_ANCHOR_COLOR, SHAPE_SELECT_COLOR } from './constants'; import type { VRenderPointerEvent } from '../../../interface/type'; import type { IEditComponent } from '../../interface'; import { Edit } from '../../edit'; +import { DragComponent } from './transform-drag'; // import { EditorActionMode } from './enum'; type AnchorDirection = 'top' | 'bottom' | 'left-top' | 'left-bottom' | 'right' | 'left' | 'right-top' | 'right-bottom'; @@ -146,6 +147,10 @@ export class TransformControl extends AbstractComponent void = null; + // drag + _dragger: DragComponent; + private _lastBoxInDrag: IRect; + static defaultAttributes: Partial = { // 去掉padding // padding: 2, @@ -230,6 +235,47 @@ export class TransformControl extends AbstractComponent { + this._lastBoxInDrag.setAttribute('visible', true); + const scale = this.stage.defaultLayer?.globalTransMatrix.a ?? 1; + this.moveBy(moveX / scale, moveY / scale); + }; + private _dragEnd = () => { + this._editorEnd(); + }; + private _unDragEnd = () => { + this._editorEnd(); + }; + + protected _editorEnd = () => { + // this._endHandler(this._editorBox.getTransformAttribute()); + // this._editorBox.isEditor = false; + // this._snapLineX.setAttributes({ visible: false }); + // this._snapLineY.setAttributes({ visible: false }); + + // this._snapTargetBoxX.setAttributes({ visible: false }); + // this._snapTargetBoxY.setAttributes({ visible: false }); + + this._lastBoxInDrag.setAttribute('visible', false); + // this._opt.editorEvent.setCursorSyncToTriggerLayer(); + }; + updateSubBounds(bounds: IAABBBoundsLike) { // set bounds this.rect.setAttributes({ @@ -260,6 +306,7 @@ export class TransformControl extends AbstractComponent cb(e)); + this._dragger.startDrag(e); } }; @@ -848,9 +896,9 @@ export class TransformControl extends AbstractComponent void; + protected _dragEndHandler: () => void; + protected _unDragEndHandler: () => void; + + pointerMove = (event: PointerEvent) => { + if (!(this._state === 'startDrag' || this._state === 'dragging')) { + return; + } + if (this._state !== 'dragging') { + this._state = 'dragging'; + } + this._dragHandler?.(event.clientX - this._lastPosX, event.clientY - this._lastPosY); + this._lastPosX = event.clientX; + this._lastPosY = event.clientY; + }; + + dragHandler(handler: (moveX: number, moveY: number) => void) { + this._dragHandler = handler; + } + dragEndHandler(handler: () => void) { + this._dragEndHandler = handler; + } + unDragEndHandler(handler: () => void) { + this._unDragEndHandler = handler; + } + + startDrag(event: PointerEvent) { + this._state = 'startDrag'; + this._lastPosX = event.clientX; + this._lastPosY = event.clientY; + } + + stopDrag = (event: PointerEvent) => { + if (this._state !== 'dragging' && this._state !== 'startDrag') { + this._unDragEndHandler?.(); + return; + } + // const lastState = this._state; + this._state = 'stopDrag'; + // if (lastState !== 'dragging') { + // return; + // } + this._state = 'stopDrag'; + this._dragEndHandler?.(); + }; + + release() { + window.removeEventListener('pointermove', this.pointerMove, true); + window.removeEventListener('pointerup', this.stopDrag, true); + this._dragHandler = null; + this._dragEndHandler = null; + } +} From 75662b29831f49c60341daf79be00a66fb44697c Mon Sep 17 00:00:00 2001 From: zhouxinyu Date: Wed, 12 Jun 2024 16:04:17 +0800 Subject: [PATCH 3/4] feat: support image edit --- .../vstory/demo/src/demos/GraphicEdit.tsx | 33 ++++++++++++++++++- .../src/edit/edit-component/base-selection.ts | 10 +++--- .../src/edit/edit-component/box-selection.ts | 20 +++++------ .../edit/edit-component/image-selection.ts | 7 ++++ .../vstory/src/edit/edit-component/index.ts | 2 ++ .../src/edit/edit-component/rect-selection.ts | 11 ------- packages/vstory/src/edit/edit.ts | 3 +- .../story/character/component/character.ts | 6 ++-- .../component/characters/character-rect.ts | 6 ---- .../character/component/graphic/graphic.ts | 11 ++++--- .../story/character/component/graphic/rect.ts | 7 ---- 11 files changed, 69 insertions(+), 47 deletions(-) create mode 100644 packages/vstory/src/edit/edit-component/image-selection.ts diff --git a/packages/vstory/demo/src/demos/GraphicEdit.tsx b/packages/vstory/demo/src/demos/GraphicEdit.tsx index 89c1025f..98bf8e94 100644 --- a/packages/vstory/demo/src/demos/GraphicEdit.tsx +++ b/packages/vstory/demo/src/demos/GraphicEdit.tsx @@ -5,6 +5,7 @@ import { Story } from '../../../src/story/story'; import { Edit } from '../../../src/edit/edit'; import '../../../src/story/index'; import { loadAllSelection } from '../../../src/edit/edit-component'; +import img from '../assets/scene3/chart-3.png'; loadAllSelection(); @@ -64,6 +65,22 @@ export const GraphicEdit = () => { angle: 0, shapePoints: [] } + }, + { + type: 'ImageComponent', + id: `image`, + zIndex: 1, + position: { + top: 225, + left: 260, + width: 200, + height: 160 + }, + options: { + graphic: { + image: img + } + } } ], acts: [ @@ -83,7 +100,6 @@ export const GraphicEdit = () => { payload: { animation: { duration: 700, - easing: 'easeInOutQuad', move: { from: 'right' } @@ -106,6 +122,21 @@ export const GraphicEdit = () => { } } ] + }, + { + characterId: 'image', + characterActions: [ + { + startTime: 1, + duration: 800, + action: 'appear', + payload: { + animation: { + duration: 700 + } + } + } + ] } ] } diff --git a/packages/vstory/src/edit/edit-component/base-selection.ts b/packages/vstory/src/edit/edit-component/base-selection.ts index 599099a5..ba54bcaa 100644 --- a/packages/vstory/src/edit/edit-component/base-selection.ts +++ b/packages/vstory/src/edit/edit-component/base-selection.ts @@ -80,15 +80,15 @@ export abstract class BaseSelection implements IEditComponent { } inActiveLayoutComponent() { - if (!this._layoutComponent) { - this._layoutComponent = this.createLayoutComponent(); - } if (!this._layoutComponent) { return; } - this._layoutComponent.onInActive(); - this.detachComponent(this._layoutComponent); + // this._layoutComponent.onInActive(); + // this.detachComponent(this._layoutComponent); + // TODO 直接release + this._layoutComponent.release(); + this._layoutComponent = null; } updateComponent() { diff --git a/packages/vstory/src/edit/edit-component/box-selection.ts b/packages/vstory/src/edit/edit-component/box-selection.ts index f4fbb8df..c31ea9c3 100644 --- a/packages/vstory/src/edit/edit-component/box-selection.ts +++ b/packages/vstory/src/edit/edit-component/box-selection.ts @@ -36,14 +36,14 @@ export class BoxSelection extends BaseSelection implements IEditComponent { return false; } - startEdit(actionInfo: IEditActionInfo) { - super.startEdit(actionInfo); - this.edit.startEdit({ - type: 'boxSelection', - actionInfo: this._actionInfo, - updateCharacter: (params: any) => { - // nothing 不支持任何修改 - } - }); - } + // startEdit(actionInfo: IEditActionInfo) { + // super.startEdit(actionInfo); + // // this.edit.startEdit({ + // // type: 'boxSelection', + // // actionInfo: this._actionInfo, + // // updateCharacter: (params: any) => { + // // // nothing 不支持任何修改 + // // } + // // }); + // } } diff --git a/packages/vstory/src/edit/edit-component/image-selection.ts b/packages/vstory/src/edit/edit-component/image-selection.ts new file mode 100644 index 00000000..4360589d --- /dev/null +++ b/packages/vstory/src/edit/edit-component/image-selection.ts @@ -0,0 +1,7 @@ +import { type IEditActionInfo, type IEditComponent } from '../interface'; +import { RectSelection } from './rect-selection'; + +export class ImageSelection extends RectSelection implements IEditComponent { + readonly level = 3; + readonly type: string = 'image'; +} diff --git a/packages/vstory/src/edit/edit-component/index.ts b/packages/vstory/src/edit/edit-component/index.ts index c976e2bc..9d86fa6a 100644 --- a/packages/vstory/src/edit/edit-component/index.ts +++ b/packages/vstory/src/edit/edit-component/index.ts @@ -1,5 +1,6 @@ import { CommonEditComponent } from './common'; import { BoxSelection } from './box-selection'; +import { ImageSelection } from './image-selection'; import { TextSelection } from './text-selection'; import { RichTextSelection } from './richtext-selection'; import { RectSelection } from './rect-selection'; @@ -11,4 +12,5 @@ export function loadAllSelection() { Edit.registerEditComponent('richtext', RichTextSelection); Edit.registerEditComponent('rect', RectSelection); Edit.registerEditComponent('box-selection', BoxSelection); + Edit.registerEditComponent('image-selection', ImageSelection); } diff --git a/packages/vstory/src/edit/edit-component/rect-selection.ts b/packages/vstory/src/edit/edit-component/rect-selection.ts index 7eb73d98..cb7866f8 100644 --- a/packages/vstory/src/edit/edit-component/rect-selection.ts +++ b/packages/vstory/src/edit/edit-component/rect-selection.ts @@ -59,15 +59,4 @@ export class RectSelection extends BaseSelection implements IEditComponent { return false; } - - startEdit(actionInfo: IEditActionInfo) { - super.startEdit(actionInfo); - this.edit.startEdit({ - type: 'boxSelection', - actionInfo: this._actionInfo, - updateCharacter: (params: any) => { - // nothing 不支持任何修改 - } - }); - } } diff --git a/packages/vstory/src/edit/edit.ts b/packages/vstory/src/edit/edit.ts index 334c4bb0..806c0fac 100644 --- a/packages/vstory/src/edit/edit.ts +++ b/packages/vstory/src/edit/edit.ts @@ -34,7 +34,8 @@ export class Edit { _initEditGroup() { this._editGroup = createGroup({}); - this.story.canvas.getStage().defaultLayer.add(this._editGroup); + const editLayer = this.story.canvas.getStage().createLayer(); + editLayer.add(this._editGroup); } getEditGroup() { diff --git a/packages/vstory/src/story/character/component/character.ts b/packages/vstory/src/story/character/component/character.ts index ca65410f..14b30d66 100644 --- a/packages/vstory/src/story/character/component/character.ts +++ b/packages/vstory/src/story/character/component/character.ts @@ -54,8 +54,10 @@ export abstract class CharacterComponent extends CharacterBase { this.hide(); } - setAttributes(attr: Record) { - this._graphic.setAttributes(attr); + setAttributes(attr: Record): void { + this.group.setAttributes(attr); + this._graphic.setAttributes({ ...attr, x: 0, y: 0, angle: 0 }); + this._text.updateAttribute({}); } protected abstract _createGraphic(): Graphic; diff --git a/packages/vstory/src/story/character/component/characters/character-rect.ts b/packages/vstory/src/story/character/component/characters/character-rect.ts index f177460a..377bf5ea 100644 --- a/packages/vstory/src/story/character/component/characters/character-rect.ts +++ b/packages/vstory/src/story/character/component/characters/character-rect.ts @@ -8,10 +8,4 @@ export class CharacterComponentRect extends CharacterComponent { protected _createGraphic(): Graphic { return new GraphicRect(StoryGraphicType.RECT, this); } - - setAttributes(attr: Record): void { - this.group.setAttributes(attr); - this._graphic.setAttributes({ ...attr, x: 0, y: 0, angle: 0 }); - this._text.updateAttribute({}); - } } diff --git a/packages/vstory/src/story/character/component/graphic/graphic.ts b/packages/vstory/src/story/character/component/graphic/graphic.ts index 38272571..431d20c7 100644 --- a/packages/vstory/src/story/character/component/graphic/graphic.ts +++ b/packages/vstory/src/story/character/component/graphic/graphic.ts @@ -1,5 +1,5 @@ import type { IGraphic } from '@visactor/vrender-core'; -import type { IPointLike } from '@visactor/vutils'; +import type { IAABBBounds, IPointLike } from '@visactor/vutils'; import { isValid } from '@visactor/vutils'; import type { CharacterComponent } from '../character'; import type { IComponentCharacterSpec, IWidgetData } from '../..'; @@ -27,7 +27,7 @@ export abstract class Graphic { abstract init(): void; - getBounds() { + getBounds(): IAABBBounds { return this._graphic.AABBBounds; } @@ -52,8 +52,11 @@ export abstract class Graphic { }; } - setAttributes(attr: Record) { - throw new Error('请重载setAttributes函数'); + setAttributes(attr: Record): void { + if (!this._graphic) { + return; + } + this._graphic.setAttributes(attr); } show(): void { diff --git a/packages/vstory/src/story/character/component/graphic/rect.ts b/packages/vstory/src/story/character/component/graphic/rect.ts index e9f7fefd..d76687cf 100644 --- a/packages/vstory/src/story/character/component/graphic/rect.ts +++ b/packages/vstory/src/story/character/component/graphic/rect.ts @@ -20,13 +20,6 @@ export class GraphicRect extends Graphic { }; } - setAttributes(attr: Record): void { - if (!this._graphic) { - return; - } - this._graphic.setAttributes(attr); - } - init() { if (!this._graphic) { this._graphic = createRect( From b5264ca1ce35aba1f714770ade1ac09ef9b95d3a Mon Sep 17 00:00:00 2001 From: zhouxinyu Date: Wed, 12 Jun 2024 19:54:04 +0800 Subject: [PATCH 4/4] feat: support text edit --- .../src/edit/edit-component/base-selection.ts | 6 ++- .../richtext-transform-control.ts | 40 ++++++++++++++++--- .../edit-control/transform-control.ts | 12 +++--- .../edit/edit-component/image-selection.ts | 6 +++ .../src/edit/edit-component/rect-selection.ts | 4 +- .../edit/edit-component/richtext-selection.ts | 2 +- packages/vstory/src/edit/interface.ts | 2 + 7 files changed, 56 insertions(+), 16 deletions(-) diff --git a/packages/vstory/src/edit/edit-component/base-selection.ts b/packages/vstory/src/edit/edit-component/base-selection.ts index ba54bcaa..adec344c 100644 --- a/packages/vstory/src/edit/edit-component/base-selection.ts +++ b/packages/vstory/src/edit/edit-component/base-selection.ts @@ -28,6 +28,10 @@ export abstract class BaseSelection implements IEditComponent { } abstract checkAction(actionInfo: IEditSelectionInfo): boolean; + getActiveCharacter() { + return this._activeCharacter; + } + startEdit(actionInfo: IEditActionInfo) { this.isEditing = true; this._actionInfo = actionInfo; @@ -64,7 +68,7 @@ export abstract class BaseSelection implements IEditComponent { } protected _createLayoutComponent(attributes: Partial): ITransformControl | undefined { - return new TransformControl(attributes); + return new TransformControl(this, attributes); } activeLayoutComponent() { diff --git a/packages/vstory/src/edit/edit-component/edit-control/richtext-transform-control.ts b/packages/vstory/src/edit/edit-component/edit-control/richtext-transform-control.ts index e26bdcea..aae74f07 100644 --- a/packages/vstory/src/edit/edit-component/edit-control/richtext-transform-control.ts +++ b/packages/vstory/src/edit/edit-component/edit-control/richtext-transform-control.ts @@ -11,7 +11,7 @@ import type { ILineGraphicAttribute, IGroup } from '@visactor/vrender-core'; -import { createRect } from '@visactor/vrender-core'; +import { createRect, getTheme } from '@visactor/vrender-core'; import type { IAABBBounds, IAABBBoundsLike, IPointLike } from '@visactor/vutils'; import { AABBBounds, merge, normalizePadding, pi } from '@visactor/vutils'; import { AbstractComponent } from '@visactor/vrender-components'; @@ -49,12 +49,16 @@ export class RichTextTransformControl extends TransformControl implements ITrans const { x, y, width, height } = this.rect.attribute; const container = document.createElement('div'); container.style.position = 'relative'; + container.style.display = 'flex'; + container.style.justifyContent = 'center'; container.style.left = x + 'px'; container.style.top = y + 'px'; container.style.width = width + 'px'; container.style.height = height + 'px'; container.style.pointerEvents = 'none'; const bg = document.createElement('div'); + bg.style.position = 'absolute'; + bg.style.zIndex = '-1'; bg.style.width = '100%'; bg.style.height = '100%'; bg.style.opacity = '0.2'; @@ -63,6 +67,35 @@ export class RichTextTransformControl extends TransformControl implements ITrans container.appendChild(bg); stage.window.getContainer().appendChild(container); this.domTextEditor = container; + + // 添加textArea + const character = this.editComponent.getActiveCharacter(); + const text = (character as any)._text && (character as any)._text._graphic; + if (text) { + const textArea = document.createElement('textArea') as HTMLTextAreaElement; + const bounds = text.AABBBounds; + const theme = getTheme(text).text; + const { + fontFamily = theme.fontFamily, + fontSize = theme.fontSize, + fontWeight = theme.fontWeight + } = text.attribute; + textArea.style.alignSelf = `center`; + textArea.style.width = `${bounds.width()}px`; + textArea.style.height = `${bounds.height()}px`; + textArea.innerText = text.attribute.text; + textArea.style.border = 'none'; + textArea.style.fontFamily = fontFamily; + textArea.style.fontSize = fontSize; + textArea.style.fontWeight = fontWeight; + textArea.style.overflow = 'hidden'; + textArea.style.outline = 'none'; + textArea.style.resize = 'none'; + textArea.style.pointerEvents = 'all'; + container.appendChild(textArea); + } + + // textArea.value = this.activeGraphic; } protected render() { @@ -83,11 +116,6 @@ export class RichTextTransformControl extends TransformControl implements ITrans } } - onInActive(): void { - super.onInActive(); - this.detachDomTextEditor(); - } - release(): void { this.detachDomTextEditor(); super.release(); diff --git a/packages/vstory/src/edit/edit-component/edit-control/transform-control.ts b/packages/vstory/src/edit/edit-component/edit-control/transform-control.ts index 8a35631e..4cbe1347 100644 --- a/packages/vstory/src/edit/edit-component/edit-control/transform-control.ts +++ b/packages/vstory/src/edit/edit-component/edit-control/transform-control.ts @@ -65,6 +65,8 @@ export type IUpdateParams = { anchor: [number | string, number | string]; // relative position of shape point shapePoints: IPointLike[]; + // text + text?: string | string[]; }; const borderAnchors = ['top', 'bottom', 'left', 'right']; @@ -96,7 +98,6 @@ const anchorCursorMap = { export interface ITransformControl extends IGroup { updateSubBounds: (b: IAABBBoundsLike) => void; onActive: () => void; - onInActive: () => void; onUpdate: (cb: (data: IUpdateParams, event?: VRenderPointerEvent) => Partial | false) => void; onEditorEnd: (cb: (event?: VRenderPointerEvent) => void) => void; onEditorStart: (cb: (event?: VRenderPointerEvent) => void) => void; @@ -147,6 +148,8 @@ export class TransformControl extends AbstractComponent void = null; + editComponent: IEditComponent; + // drag _dragger: DragComponent; private _lastBoxInDrag: IRect; @@ -193,8 +196,9 @@ export class TransformControl extends AbstractComponent) { + constructor(editComponent: IEditComponent, attributes: Partial) { super(merge({ shadowRootIdx: 1 }, TransformControl.defaultAttributes, attributes)); + this.editComponent = editComponent; this._editorConfig = { move: attributes.move !== false, rotate: attributes.rotate !== false, @@ -295,10 +299,6 @@ export class TransformControl extends AbstractComponent): ITransformControl { + return new TransformControl(this, attributes); + } } diff --git a/packages/vstory/src/edit/edit-component/rect-selection.ts b/packages/vstory/src/edit/edit-component/rect-selection.ts index cb7866f8..67df59c9 100644 --- a/packages/vstory/src/edit/edit-component/rect-selection.ts +++ b/packages/vstory/src/edit/edit-component/rect-selection.ts @@ -1,11 +1,11 @@ import type { IEditSelectionInfo } from '../interface'; import { EditActionEnum, type IEditActionInfo, type IEditComponent } from '../interface'; -import { StoryEvent } from '../../story/interface/runtime-interface'; import type { Edit } from '../edit'; import { BaseSelection } from './base-selection'; import type { TransformAttributes, ITransformControl, IUpdateParams } from './edit-control/transform-control'; import { TransformControl } from './edit-control/transform-control'; import type { VRenderPointerEvent } from '../../interface/type'; +import { RichTextTransformControl } from './edit-control/richtext-transform-control'; export class RectSelection extends BaseSelection implements IEditComponent { readonly level = 3; @@ -16,7 +16,7 @@ export class RectSelection extends BaseSelection implements IEditComponent { } protected _createLayoutComponent(attributes: Partial): ITransformControl { - return new TransformControl(attributes); + return new RichTextTransformControl(this, attributes); } editEnd(): void { diff --git a/packages/vstory/src/edit/edit-component/richtext-selection.ts b/packages/vstory/src/edit/edit-component/richtext-selection.ts index 6630dce0..c0d44778 100644 --- a/packages/vstory/src/edit/edit-component/richtext-selection.ts +++ b/packages/vstory/src/edit/edit-component/richtext-selection.ts @@ -15,7 +15,7 @@ export class RichTextSelection extends BaseSelection implements IEditComponent { } protected _createLayoutComponent(attributes: Partial): ITransformControl { - return new RichTextTransformControl(attributes); + return new RichTextTransformControl(this, attributes); } editEnd(): void { diff --git a/packages/vstory/src/edit/interface.ts b/packages/vstory/src/edit/interface.ts index 6aa287e7..4d261510 100644 --- a/packages/vstory/src/edit/interface.ts +++ b/packages/vstory/src/edit/interface.ts @@ -46,6 +46,8 @@ export interface IEditComponent { // 编辑结束 editEnd: () => void; + + getActiveCharacter: () => ICharacter | null; } export type IModelInfoSpecKey = {