refactor: 渲染系统从 eventemitter3 换为钩子系统

This commit is contained in:
AncTe committed 2026-11-11 15:50:58 +08:00
1 parent e094ac28a4
commit 7acb1d52e7
12 files changed
+334 -316

No files matched your search

+1
View File
@@ -320,6 +320,7 @@
"212": "Duplicate implements for hotkey command '$1', old one will be overwritten. This probably caused by unclearing implementation config, please check.",
"213": "Setting item '$1' has existed in group '$2'. The item will be moved to new group '$3'.",
"214": "Range setting item get a number range that cannot be divided into integer by step: range=[$1, $2], step=$3. This may cause issues, please ensure number range can be divided by step evenly.",
"215": "Cannot recognize '$1' as a proper render item prop or emit.",
"1001": "Event(setBlock): Unknown tile '$1' when setting block."
}
}
+20 -36
View File
@@ -1,5 +1,4 @@
import { ReservedProps } from 'vue';
import EventEmitter from 'eventemitter3';
import {
AnimatedProps,
BezierProps,
@@ -18,49 +17,34 @@ import {
ShaderProps,
TextProps
} from './props';
import { ERenderItemEvent } from '@motajs/render';
import { IRenderItemBaseHooks } from '@motajs/render';
export type WrapEventEmitterEvents<T extends EventEmitter.ValidEventTypes> =
T extends string | symbol
? T
: {
[P in keyof T]: T[P] extends any[]
? (...args: T[P]) => void
: (...args: any[]) => void;
};
type MappingEvent<E extends ERenderItemEvent> = {
[P in keyof WrapEventEmitterEvents<E> as P extends string
? `on${Capitalize<P>}`
: never]?: WrapEventEmitterEvents<E>[P];
};
export type TagDefine<T extends object, E extends ERenderItemEvent> = T &
MappingEvent<E> &
ReservedProps;
export type TagDefine<T extends object, H extends IRenderItemBaseHooks> = T & {
[K in keyof H]?: H[K];
} & ReservedProps;
declare module 'vue/jsx-runtime' {
namespace JSX {
export interface IntrinsicElements {
container: TagDefine<ContainerProps, ERenderItemEvent>;
container: TagDefine<ContainerProps, IRenderItemBaseHooks>;
'container-custom': TagDefine<
ConatinerCustomProps,
ERenderItemEvent
IRenderItemBaseHooks
>;
shader: TagDefine<ShaderProps, ERenderItemEvent>;
text: TagDefine<TextProps, ERenderItemEvent>;
image: TagDefine<ImageProps, ERenderItemEvent>;
animated: TagDefine<AnimatedProps, ERenderItemEvent>;
comment: TagDefine<CommentProps, ERenderItemEvent>;
custom: TagDefine<CustomProps, ERenderItemEvent>;
'g-rect': TagDefine<RectProps, ERenderItemEvent>;
'g-circle': TagDefine<CirclesProps, ERenderItemEvent>;
'g-ellipse': TagDefine<EllipseProps, ERenderItemEvent>;
'g-line': TagDefine<LineProps, ERenderItemEvent>;
'g-bezier': TagDefine<BezierProps, ERenderItemEvent>;
'g-quad': TagDefine<QuadraticProps, ERenderItemEvent>;
'g-path': TagDefine<PathProps, ERenderItemEvent>;
'g-rectr': TagDefine<RectRProps, ERenderItemEvent>;
shader: TagDefine<ShaderProps, IRenderItemBaseHooks>;
text: TagDefine<TextProps, IRenderItemBaseHooks>;
image: TagDefine<ImageProps, IRenderItemBaseHooks>;
animated: TagDefine<AnimatedProps, IRenderItemBaseHooks>;
comment: TagDefine<CommentProps, IRenderItemBaseHooks>;
custom: TagDefine<CustomProps, IRenderItemBaseHooks>;
'g-rect': TagDefine<RectProps, IRenderItemBaseHooks>;
'g-circle': TagDefine<CirclesProps, IRenderItemBaseHooks>;
'g-ellipse': TagDefine<EllipseProps, IRenderItemBaseHooks>;
'g-line': TagDefine<LineProps, IRenderItemBaseHooks>;
'g-bezier': TagDefine<BezierProps, IRenderItemBaseHooks>;
'g-quad': TagDefine<QuadraticProps, IRenderItemBaseHooks>;
'g-path': TagDefine<PathProps, IRenderItemBaseHooks>;
'g-rectr': TagDefine<RectRProps, IRenderItemBaseHooks>;
}
}
}
+7 -26
View File
@@ -1,4 +1,8 @@
import { ERenderItemEvent, IRenderItem, IRenderTreeRoot } from '@motajs/render';
import {
IRenderItem,
IRenderItemBaseHooks,
IRenderTreeRoot
} from '@motajs/render';
import { VNodeProps } from 'vue';
import { BaseProps } from './props';
import { TagDefine } from './elements';
@@ -8,7 +12,6 @@ import {
IExcitation,
ITransition
} from '@motajs/animate';
import EventEmitter from 'eventemitter3';
//#region 标签管理
@@ -57,8 +60,8 @@ export interface IRenderTagManager {
export type DefaultProps<
P extends BaseProps = BaseProps,
E extends ERenderItemEvent = ERenderItemEvent
> = TagDefine<P, E>;
H extends IRenderItemBaseHooks = IRenderItemBaseHooks
> = TagDefine<P, H>;
//#endregion
@@ -88,28 +91,6 @@ export interface IRendererUsing {
*/
onExcitedFunc(fn: (payload: number) => void): void;
/**
* 监听渲染元素的指定事件。
*
* 在组件内如果你需要监听事件,应该先考虑使用 `JSX` 的 `onXxx` 来监听事件,而不是使用此接口。
*
* 如果 `JSX` 的 `onXxx` 不能满足你的需求,再考虑使用此接口,在组件内**不要**直接使用 `item.on` 来监听事件,
* 否则可能导致事件监听没有正确删除,出现内存泄漏问题。
*
* 在**组件外不要**使用此接口,否则可能没有效果!
* @param item 要监听的渲染元素
* @param key 事件类型
* @param listener 当事件触发时执行的函数
*/
listenEvent<
T extends ERenderItemEvent,
K extends EventEmitter.EventNames<T>
>(
item: IRenderItem,
key: K,
listener: EventEmitter.EventListener<T, K>
): void;
/**
* 创建一个动画执行器。
*
+1 -16
View File
@@ -1,6 +1,5 @@
import { ERenderItemEvent, IRenderItem, IRenderTreeRoot } from '@motajs/render';
import { IRenderTreeRoot } from '@motajs/render';
import { onMounted, onUnmounted } from 'vue';
import EventEmitter from 'eventemitter3';
import { IRendererUsing } from './types';
import {
IExcitable,
@@ -28,20 +27,6 @@ export class RendererUsing implements IRendererUsing {
this.onExcited(excited(fn));
}
listenEvent<
T extends ERenderItemEvent,
K extends EventEmitter.EventNames<T>
>(
item: IRenderItem,
key: K,
listener: EventEmitter.EventListener<T, K>
): void {
item.on(key, listener);
onUnmounted(() => {
item.off(key, listener);
});
}
useAnimater(excitation: IExcitation<number>): IAnimater {
const anim = new Animater();
onMounted(() => {
+1 -3
View File
@@ -87,9 +87,7 @@ export interface IScanAnimaterData {
/**
* 扫描动画控制器,会按照先从左到右,再从上到下的顺序依次输出,可以用于动画精灵图等
*/
export class TextureScanAnimater
implements ITextureAnimater<IScanAnimaterData>
{
export class TextureScanAnimater implements ITextureAnimater<IScanAnimaterData> {
*once(
texture: ITexture,
data: IScanAnimaterData
+1 -3
View File
@@ -101,9 +101,7 @@ interface MaxRectsRectangle extends IRectangle {
readonly data: ITexture;
}
export class TextureMaxRectsStreamComposer
implements ITextureStreamComposer<void>
{
export class TextureMaxRectsStreamComposer implements ITextureStreamComposer<void> {
/** Max Rects 打包器 */
readonly packer: MaxRectsPacker<MaxRectsRectangle>;
-135
View File
@@ -1,135 +0,0 @@
type AdapterFunction<T> = (item: T, ...params: any[]) => Promise<any>;
type SyncAdapterFunction<T> = (item: T, ...params: any[]) => any;
type GlobalAdapterFunction = (...params: any[]) => Promise<any>;
/**
* 渲染适配器,用作渲染层与数据层沟通的桥梁,用于在数据层等待渲染层执行,常用与动画等。
* 例如移动图块,移动图块操作先由数据层执行,删除原有图块,然后通知渲染层进行移动动画,
* 而移动动画是耗时的,因此需要通过本类进行适配,实现等待移动动画执行完毕,然后进行后续操作
*/
export class RenderAdapter<T> {
static adapters: Map<string, RenderAdapter<any>> = new Map();
/** 所有元素的集合 */
items: Set<T> = new Set();
/** 适配器的id */
id: string;
private execute: Map<string, AdapterFunction<T>> = new Map();
private syncExecutes: Map<string, SyncAdapterFunction<T>> = new Map();
private globalExecutes: Map<string, GlobalAdapterFunction> = new Map();
constructor(id: string) {
this.id = id;
RenderAdapter.adapters.set(id, this);
}
/**
* 添加一个元素
*/
add(item: T) {
this.items.add(item);
}
/**
* 移除一个元素
*/
remove(item: T) {
this.items.delete(item);
}
/**
* 定义一个类似于静态函数的函数,只会调用一次,不会对每个元素执行
* @param id 函数名称
* @param fn 执行的函数
*/
receiveGlobal(id: string, fn: GlobalAdapterFunction) {
this.globalExecutes.set(id, fn);
}
/**
* 设置执行函数
* @param fn 对于每个元素执行的函数
*/
receive(id: string, fn: AdapterFunction<T>): void {
this.execute.set(id, fn);
}
/**
* 设置同步执行函数
* @param fn 对于每个元素执行的函数
*/
receiveSync(id: string, fn: SyncAdapterFunction<T>): void {
this.syncExecutes.set(id, fn);
}
/**
* 对所有元素执行函数,当所有元素都运行完毕后兑现,类似于Promise.all
* @returns 包含每个元素运行结果的数组
*/
all<R = any>(fn: string, ...params: any[]): Promise<R[]> {
const execute = this.execute.get(fn);
if (!execute) {
return Promise.reject();
} else {
return Promise.all(
[...this.items].map(v => execute!(v, ...params))
);
}
}
/**
* 对所有元素执行函数,当任意一个元素运行完毕后兑现,类似于Promise.any
* @returns 最先运行完毕的元素的结果
*/
any<R = any>(fn: string, ...params: any[]): Promise<R> {
const execute = this.execute.get(fn);
if (!execute) {
return Promise.reject();
} else {
return Promise.any(
[...this.items].map(v => execute!(v, ...params))
);
}
}
/**
* 对所有元素执行同步函数
* @returns 包含每个元素运行结果的数组
*/
sync<R = any>(fn: string, ...params: any[]): R[] {
const execute = this.syncExecutes.get(fn);
if (!execute) {
return [];
} else {
return [...this.items].map(v => execute!(v, ...params));
}
}
/**
* 调用一个全局函数
*/
global<R = any>(id: string, ...params: any[]): Promise<R> {
const execute = this.globalExecutes.get(id);
if (!execute) {
return Promise.reject();
} else {
return execute(...params);
}
}
/**
* 销毁这个adapter
*/
destroy() {
RenderAdapter.adapters.delete(this.id);
}
/**
* 获取适配器
*/
static get<T>(id: string): RenderAdapter<T> | undefined {
return this.adapters.get(id);
}
}
-10
View File
@@ -142,13 +142,3 @@ export interface ActionEventMap {
[ActionType.Up]: IActionEvent;
[ActionType.Wheel]: IWheelEvent;
}
export const eventNameMap: Record<ActionType, keyof ERenderItemActionEvent> = {
[ActionType.Click]: 'click',
[ActionType.Down]: 'down',
[ActionType.Move]: 'move',
[ActionType.Up]: 'up',
[ActionType.Enter]: 'enter',
[ActionType.Leave]: 'leave',
[ActionType.Wheel]: 'wheel'
};
-1
View File
@@ -1,4 +1,3 @@
export * from './adapter';
export * from './canvas2d';
export * from './container';
export * from './custom';
+200 -56
View File
@@ -1,23 +1,27 @@
import { isEqual, isNil } from 'lodash-es';
import { EventEmitter } from 'eventemitter3';
import { MotaOffscreenCanvas2D } from './canvas2d';
import { ITransformUpdatable, Transform } from './transform';
import { logger } from '@motajs/common';
import {
Hookable,
HookController,
IHookController,
logger
} from '@motajs/common';
import { ElementNamespace, ComponentInternalInstance } from 'vue';
import {
ActionEventMap,
ActionType,
ERenderItemActionEvent,
eventNameMap,
EventProgress,
IActionEvent,
IActionEventBase,
IWheelEvent,
MouseType
} from './event';
import { vec3 } from 'gl-matrix';
import {
ERenderItemEvent,
ExcitableDelegation,
IRenderItem,
IRenderItemBaseHooks,
IRenderTreeRoot,
RenderPosition
} from './types';
@@ -34,7 +38,7 @@ interface ToDelegateExcitables {
}
export abstract class RenderItem
extends EventEmitter<ERenderItemEvent>
extends Hookable<IRenderItemBaseHooks>
implements IRenderItem, ITransformUpdatable<Transform>
{
//#region 元素属性
@@ -123,6 +127,11 @@ export abstract class RenderItem
//#region 交互事件
/** 当前元素包含的监听事件名称列表 */
private readonly eventNames: Set<string> = new Set();
/** 当前元素包含的监听事件,这是为了能够使用 `prop` 传入时不需要为每个内容都添加一个 `hook` 对象 */
private readonly events: Map<string, (...params: any[]) => any> = new Map();
/** 是否调用了 `ev.stopPropagation` */
protected propagationStoped: Map<ActionType, boolean> = new Map();
/** 捕获阶段缓存的事件对象 */
@@ -170,6 +179,36 @@ export abstract class RenderItem
} else {
this.cache = null;
}
this.eventNames
.add('onClick')
.add('onClickCapture')
.add('onDown')
.add('onDownCapture')
.add('onMove')
.add('onMoveCapture')
.add('onUp')
.add('onUpCapture')
.add('onEnter')
.add('onLeave')
.add('onWheel')
.add('onWheelCapture')
.add('onResize')
.add('onTransform');
this.addHook(this.getDefaultHook());
}
/**
* 获取默认钩子对象,子类可以拓展以实现自己默认钩子的 `prop` 支持
*/
protected getDefaultHook(): IRenderItemBaseHooks {
return new RenderItemDefaultHooks(this.events);
}
protected createController(
hook: Partial<IRenderItemBaseHooks>
): IHookController<IRenderItemBaseHooks> {
return new HookController(this, hook);
}
setID(id: string): void {
@@ -322,7 +361,7 @@ export abstract class RenderItem
this.cache!.size(width, height);
}
this.update();
this.emit('resize', width, height);
this.forEachHook(hook => hook.onResize?.(width, height));
}
/**
@@ -489,11 +528,31 @@ export abstract class RenderItem
}
updateTransform(transform: Transform) {
// 更新变换矩阵时,不需要更新自身的缓存,直接调用父元素的更新即可
this.parent?.update();
// 更新变换矩阵时,只需要更新有 `selfTransform` 的子元素,其他的不需要更新
let childUpdated = false;
if (this.children.size > 0) {
const stack: RenderItem[] = [this];
while (stack.length > 0) {
const item = stack.pop();
if (!item || !item.selfTransform) continue;
childUpdated = true;
item.cacheDirty = true;
item.children.forEach(v => stack.push(v));
}
}
// 根据子元素更新情况以及自身是否 `selfTransform` 来决定是更新自己还是更新父元素
if (!childUpdated) {
if (this.selfTransform) {
this.update();
} else {
this.parent?.update();
}
} else {
this.parent?.update();
}
this.x = transform.x;
this.y = transform.y;
this.emit('transform', this, transform);
this.forEachHook(hook => hook.onTransform?.(transform));
}
requestBeforeFrame(fn: () => void): void {
@@ -616,7 +675,7 @@ export abstract class RenderItem
this.checkRoot();
this.transform.bind(this);
this.onResize(parent.scale);
this.emit('resize', this.width, this.height);
this.forEachHook(hook => hook.onResize?.(this.width, this.height));
}
/**
@@ -690,22 +749,60 @@ export abstract class RenderItem
this.noEvent = ignore;
}
/**
* 根据事件类型和事件阶段获取事件名称
* @param type 事件类型
* @param progress 事件阶段
*/
getEventName(
type: ActionType,
progress: EventProgress
): keyof ERenderItemActionEvent {
if (type === ActionType.Enter || type === ActionType.Leave) {
return eventNameMap[type];
} else if (progress === EventProgress.Capture) {
return `${eventNameMap[type]}Capture` as keyof ERenderItemActionEvent;
} else {
return eventNameMap[type];
private emitEvent<T extends ActionType>(
type: T,
progress: EventProgress,
ev: ActionEventMap[T]
): ActionEventMap[T] {
if (type === ActionType.Enter) {
this.forEachHook(hook => hook.onEnter?.(ev));
return ev;
} else if (type === ActionType.Leave) {
this.forEachHook(hook => hook.onLeave?.(ev));
return ev;
}
if (progress === EventProgress.Capture) {
switch (type) {
case ActionType.Click:
this.forEachHook(hook => hook.onClickCapture?.(ev));
break;
case ActionType.Down:
this.forEachHook(hook => hook.onDownCapture?.(ev));
break;
case ActionType.Move:
this.forEachHook(hook => hook.onMoveCapture?.(ev));
break;
case ActionType.Up:
this.forEachHook(hook => hook.onUpCapture?.(ev));
break;
case ActionType.Wheel:
this.forEachHook(hook =>
hook.onWheelCapture?.(ev as IWheelEvent)
);
break;
}
} else {
switch (type) {
case ActionType.Click:
this.forEachHook(hook => hook.onClick?.(ev));
break;
case ActionType.Down:
this.forEachHook(hook => hook.onDown?.(ev));
break;
case ActionType.Move:
this.forEachHook(hook => hook.onMove?.(ev));
break;
case ActionType.Up:
this.forEachHook(hook => hook.onUp?.(ev));
break;
case ActionType.Wheel:
this.forEachHook(hook => hook.onWheel?.(ev as IWheelEvent));
break;
}
}
return ev;
}
/**
@@ -734,13 +831,17 @@ export abstract class RenderItem
event: ActionEventMap[T]
) {
const ev = this.processEvent(type, progress, event);
if (ev) {
const name = this.getEventName(type, progress);
this.emit(name, ev);
if (!this.propagationStoped.get(type)) {
this.propagateEvent(type, progress, ev);
}
if (!ev) {
this.propagationStoped.set(type, false);
return ev;
}
this.emitEvent(type, progress, ev);
if (!this.propagationStoped.get(type)) {
this.propagateEvent(type, progress, ev);
}
this.propagationStoped.set(type, false);
return ev;
}
@@ -947,21 +1048,6 @@ export abstract class RenderItem
}
}
/**
* 解析事件key
* @param key 键名
* @returns 返回字符串表示解析后的键名,返回空字符串说明没有对应的事件
*/
protected parseEvent(key: string): string {
if (key.startsWith('on')) {
const code = key.charCodeAt(2);
if (code >= 65 && code <= 90) {
return key[2].toLowerCase() + key.slice(3);
}
}
return '';
}
/**
* `override` 以自定义 `vue` 参数处理,返回 false 以避免后续判断提高运行速度。
* @param _key 属性键名
@@ -1057,8 +1143,7 @@ export abstract class RenderItem
}
case 'transform': {
if (!this.assertType(nextValue, Transform, key)) return;
this.transform = nextValue;
this.update();
this.setTransform(nextValue);
return;
}
case 'selfTransform': {
@@ -1136,12 +1221,12 @@ export abstract class RenderItem
return;
}
}
const ev = this.parseEvent(key);
if (ev.length > 0) {
if (prevValue) {
this.off(ev as keyof ERenderItemActionEvent, prevValue);
}
this.on(ev as keyof ERenderItemActionEvent, nextValue);
if (this.eventNames.has(key)) {
// 事件参数
if (!this.assertType(nextValue, Function, key)) return;
this.events.set(key, nextValue);
} else {
logger.warn(215, key);
}
}
@@ -1152,7 +1237,6 @@ export abstract class RenderItem
*/
destroy(): void {
this.remove();
this.removeAllListeners();
this.canvases.clear();
this.cache?.clear();
this.propagationStoped.clear();
@@ -1163,3 +1247,63 @@ export abstract class RenderItem
this.setRoot(null);
}
}
export class RenderItemDefaultHooks implements IRenderItemBaseHooks {
constructor(readonly list: Map<string, (...params: any[]) => any>) {}
onClick(ev: Readonly<IActionEvent>): void {
this.list.get('onClick')?.(ev);
}
onClickCapture(ev: Readonly<IActionEvent>): void {
this.list.get('onClickCapture')?.(ev);
}
onDown(ev: Readonly<IActionEvent>): void {
this.list.get('onDown')?.(ev);
}
onDownCapture(ev: Readonly<IActionEvent>): void {
this.list.get('onDownCapture')?.(ev);
}
onMove(ev: Readonly<IActionEvent>): void {
this.list.get('onMove')?.(ev);
}
onMoveCapture(ev: Readonly<IActionEvent>): void {
this.list.get('onMoveCapture')?.(ev);
}
onUp(ev: Readonly<IActionEvent>): void {
this.list.get('onUp')?.(ev);
}
onUpCapture(ev: Readonly<IActionEvent>): void {
this.list.get('onUpCapture')?.(ev);
}
onEnter(ev: Readonly<IActionEventBase>): void {
this.list.get('onEnter')?.(ev);
}
onLeave(ev: Readonly<IActionEventBase>): void {
this.list.get('onLeave')?.(ev);
}
onWheel(ev: Readonly<IWheelEvent>): void {
this.list.get('onWheel')?.(ev);
}
onWheelCapture(ev: Readonly<IWheelEvent>): void {
this.list.get('onWheelCapture')?.(ev);
}
onResize(width: number, height: number): void {
this.list.get('onResize')?.(width, height);
}
onTransform(transform: Transform): void {
this.list.get('onTransform')?.(transform);
}
}
+10 -17
View File
@@ -333,7 +333,8 @@ export class MotaRenderer
this.getMouseType(ev)
);
this.hoveredElement.forEach(v => {
v.emit('leave', this.createMouseActionBase(ev, id, v));
const base = this.createMouseActionBase(ev, id, v);
v.forEachHook(hook => hook.onLeave?.(base));
});
this.hoveredElement.clear();
this.beforeHovered.clear();
@@ -677,18 +678,14 @@ export class MotaRenderer
// 先 leave,再 enter
before.forEach(v => {
if (!now.has(v)) {
v.emit(
'leave',
this.createMouseActionBase(event, ev.identifier, v)
);
const e = this.createMouseActionBase(event, ev.identifier, v);
v.forEachHook(hook => hook.onLeave?.(e));
}
});
now.forEach(v => {
if (!before.has(v)) {
v.emit(
'enter',
this.createMouseActionBase(event, ev.identifier, v)
);
const e = this.createMouseActionBase(event, ev.identifier, v);
v.forEachHook(hook => hook.onEnter?.(e));
}
});
}
@@ -702,18 +699,14 @@ export class MotaRenderer
// 先 leave,再 enter
before.forEach(v => {
if (!now.has(v)) {
v.emit(
'leave',
this.createTouchActionBase(event, ev.identifier, v)
);
const e = this.createTouchActionBase(event, ev.identifier, v);
v.forEachHook(hook => hook.onLeave?.(e));
}
});
now.forEach(v => {
if (!before.has(v)) {
v.emit(
'enter',
this.createTouchActionBase(event, ev.identifier, v)
);
const e = this.createTouchActionBase(event, ev.identifier, v);
v.forEachHook(hook => hook.onEnter?.(e));
}
});
}
+93 -13
View File
@@ -4,15 +4,18 @@ import {
ActionEventMap,
ActionType,
ERenderItemActionEvent,
EventProgress
EventProgress,
IActionEvent,
IActionEventBase,
IWheelEvent
} from './event';
import { MotaOffscreenCanvas2D } from './canvas2d';
import { Font } from '../style';
import { DefineComponent, DefineSetupFnComponent } from 'vue';
import { JSX } from 'vue/jsx-runtime';
import EventEmitter from 'eventemitter3';
import { SizedCanvasImageSource } from '../types';
import { ITexture, ITextureAnimater } from '../assets';
import { IHookable, IHookBase } from '@motajs/common';
//#region 功能类型
@@ -59,8 +62,95 @@ export interface ERenderItemEvent extends ERenderItemActionEvent {
transform: [item: IRenderItem, transform: Transform];
}
export interface IRenderItemBaseHooks extends IHookBase {
/**
* 当这个元素被点击时触发,触发阶段为冒泡阶段,点击要求按下与抬起都在同一个元素上完成
* @param ev 交互事件对象
*/
onClick?(ev: Readonly<IActionEvent>): void;
/**
* 当这个元素被点击时触发,触发阶段为捕获阶段,点击要求按下与抬起都在同一个元素上完成
* @param ev 交互事件对象
*/
onClickCapture?(ev: Readonly<IActionEvent>): void;
/**
* 当鼠标或触屏在这个元素上按下时触发,触发阶段为冒泡阶段
* @param ev 交互事件对象
*/
onDown?(ev: Readonly<IActionEvent>): void;
/**
* 当鼠标或触屏在这个元素上按下时触发,触发阶段为捕获阶段
* @param ev 交互事件对象
*/
onDownCapture?(ev: Readonly<IActionEvent>): void;
/**
* 当鼠标或触屏在这个元素上移动时触发,触发阶段为冒泡阶段
* @param ev 交互事件对象
*/
onMove?(ev: Readonly<IActionEvent>): void;
/**
* 当鼠标或触屏在这个元素上移动时触发,触发阶段为捕获阶段
* @param ev 交互事件对象
*/
onMoveCapture?(ev: Readonly<IActionEvent>): void;
/**
* 当鼠标或触屏在这个元素上抬起时触发,触发阶段为冒泡阶段
* @param ev 交互事件对象
*/
onUp?(ev: Readonly<IActionEvent>): void;
/**
* 当鼠标或触屏在这个元素上抬起时触发,触发阶段为捕获阶段
* @param ev 交互事件对象
*/
onUpCapture?(ev: Readonly<IActionEvent>): void;
/**
* 当鼠标或触屏进入这个元素时触发,不区分捕获和冒泡阶段,在绝大多数情况下能保证触发顺序
* @param ev 交互事件对象
*/
onEnter?(ev: Readonly<IActionEventBase>): void;
/**
* 当鼠标或触屏离开这个元素时触发,不区分捕获和冒泡阶段,在绝大多数情况下能保证触发顺序
* @param ev 交互事件对象
*/
onLeave?(ev: Readonly<IActionEventBase>): void;
/**
* 当鼠标在当前元素上滚动时触发(某些触屏手势也可以触发),触发阶段为冒泡阶段
* @param ev 交互事件对象
*/
onWheel?(ev: Readonly<IWheelEvent>): void;
/**
* 当鼠标在当前元素上滚动时触发(某些触屏手势也可以触发),触发阶段为冒泡阶段
* @param ev 交互事件对象
*/
onWheelCapture?(ev: Readonly<IWheelEvent>): void;
/**
* 当元素的尺寸被修改时触发
* @param width 元素宽度
* @param height 元素高度
*/
onResize?(width: number, height: number): void;
/**
* 当元素的变换矩阵发生变化时触发
* @param transform 变换矩阵对象
*/
onTransform?(transform: Transform): void;
}
export interface IRenderItem
extends ITransformUpdatable<Transform>, EventEmitter<ERenderItemEvent> {
extends ITransformUpdatable<Transform>, IHookable<IRenderItemBaseHooks> {
/** 是否为根元素 */
readonly isRoot: boolean;
/** 当前元素的父元素 */
@@ -363,16 +453,6 @@ export interface IRenderItem
*/
ignoreEvent(ignore: boolean): void;
/**
* 根据事件类型和事件阶段获取事件名称
* @param type 事件类型
* @param progress 事件阶段
*/
getEventName(
type: ActionType,
progress: EventProgress
): keyof ERenderItemActionEvent;
//#endregion
//#region 其他接口