mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-04-11 15:47:06 +08:00
refactor: enter 和 leave 事件 & patchProp 实现
This commit is contained in:
parent
73acde7f49
commit
91e448fdcc
@ -1,4 +1,3 @@
|
||||
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
||||
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||
import { ActionType, EventProgress, ActionEventMap } from './event';
|
||||
import {
|
||||
@ -168,20 +167,18 @@ export class ContainerCustom extends Container {
|
||||
this.renderFn = render;
|
||||
}
|
||||
|
||||
patchProp(
|
||||
protected handleProps(
|
||||
key: string,
|
||||
prevValue: any,
|
||||
nextValue: any,
|
||||
namespace?: ElementNamespace,
|
||||
parentComponent?: ComponentInternalInstance | null
|
||||
): void {
|
||||
nextValue: any
|
||||
): boolean {
|
||||
switch (key) {
|
||||
case 'render': {
|
||||
if (!this.assertType(nextValue, 'function', key)) return;
|
||||
if (!this.assertType(nextValue, 'function', key)) return false;
|
||||
this.setRenderFn(nextValue);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||
return super.handleProps(key, prevValue, nextValue);
|
||||
}
|
||||
}
|
||||
|
@ -49,21 +49,11 @@ export const enum EventProgress {
|
||||
Bubble
|
||||
}
|
||||
|
||||
export interface IActionEvent {
|
||||
export interface IActionEventBase {
|
||||
/** 当前事件是监听的哪个元素 */
|
||||
target: RenderItem;
|
||||
/** 这次操作的标识符,在按下、移动、抬起阶段中保持不变 */
|
||||
identifier: number;
|
||||
/** 是触摸操作还是鼠标操作 */
|
||||
touch: boolean;
|
||||
/** 相对于触发元素左上角的横坐标 */
|
||||
offsetX: number;
|
||||
/** 相对于触发元素左上角的纵坐标 */
|
||||
offsetY: number;
|
||||
/** 相对于整个画布左上角的横坐标 */
|
||||
absoluteX: number;
|
||||
/** 相对于整个画布左上角的纵坐标 */
|
||||
absoluteY: number;
|
||||
/**
|
||||
* 触发的按键种类,会出现在点击、按下、抬起三个事件中,而其他的如移动等该值只会是 {@link MouseType.None},
|
||||
* 电脑端可以有左键、中键、右键等,手机只会触发左键,每一项的值参考 {@link MouseType}
|
||||
@ -83,6 +73,19 @@ export interface IActionEvent {
|
||||
ctrlKey: boolean;
|
||||
/** 触发时是否按下了 Windows(Windows) / Command(Mac) 键 */
|
||||
metaKey: boolean;
|
||||
}
|
||||
|
||||
export interface IActionEvent extends IActionEventBase {
|
||||
/** 这次操作的标识符,在按下、移动、抬起阶段中保持不变 */
|
||||
identifier: number;
|
||||
/** 相对于触发元素左上角的横坐标 */
|
||||
offsetX: number;
|
||||
/** 相对于触发元素左上角的纵坐标 */
|
||||
offsetY: number;
|
||||
/** 相对于整个画布左上角的横坐标 */
|
||||
absoluteX: number;
|
||||
/** 相对于整个画布左上角的纵坐标 */
|
||||
absoluteY: number;
|
||||
|
||||
/**
|
||||
* 调用后将停止事件的继续传播。
|
||||
@ -120,14 +123,10 @@ export interface ERenderItemActionEvent {
|
||||
upCapture: [ev: Readonly<IActionEvent>];
|
||||
/** 当鼠标或手指在该元素上抬起的冒泡阶段触发 */
|
||||
up: [ev: Readonly<IActionEvent>];
|
||||
/** 当鼠标或手指进入该元素的捕获阶段触发 */
|
||||
enterCapture: [ev: Readonly<IActionEvent>];
|
||||
/** 当鼠标或手指进入该元素的冒泡阶段触发 */
|
||||
enter: [ev: Readonly<IActionEvent>];
|
||||
/** 当鼠标或手指离开该元素的捕获阶段触发 */
|
||||
leaveCapture: [ev: Readonly<IActionEvent>];
|
||||
/** 当鼠标或手指离开该元素的冒泡阶段触发 */
|
||||
leave: [ev: Readonly<IActionEvent>];
|
||||
/** 当鼠标或手指进入该元素时触发 */
|
||||
enter: [ev: Readonly<IActionEventBase>];
|
||||
/** 当鼠标或手指离开该元素时触发 */
|
||||
leave: [ev: Readonly<IActionEventBase>];
|
||||
/** 当鼠标滚轮时的捕获阶段触发 */
|
||||
wheelCapture: [ev: Readonly<IWheelEvent>];
|
||||
/** 当鼠标滚轮时的冒泡阶段触发 */
|
||||
@ -144,7 +143,7 @@ export interface ActionEventMap {
|
||||
[ActionType.Wheel]: IWheelEvent;
|
||||
}
|
||||
|
||||
export const eventNameMap: Record<ActionType, string> = {
|
||||
export const eventNameMap: Record<ActionType, keyof ERenderItemActionEvent> = {
|
||||
[ActionType.Click]: 'click',
|
||||
[ActionType.Down]: 'down',
|
||||
[ActionType.Move]: 'move',
|
||||
|
@ -272,7 +272,9 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
alpha: number = 1;
|
||||
|
||||
/** 鼠标覆盖在此元素上时的光标样式 */
|
||||
cursor: string = 'auto';
|
||||
cursor: string = 'inherit';
|
||||
/** 该元素是否忽略交互事件 */
|
||||
noEvent: boolean = false;
|
||||
|
||||
get x() {
|
||||
return this._transform.x;
|
||||
@ -340,14 +342,12 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
private cachedEvent: Map<ActionType, IActionEvent> = new Map();
|
||||
/** 下穿模式下当前下穿过来的变换矩阵 */
|
||||
private fallTransform?: Transform;
|
||||
/** 鼠标当前是否覆盖在当前元素上 */
|
||||
private hovered: boolean = false;
|
||||
/** 是否在元素内 */
|
||||
private inElement: boolean = false;
|
||||
/** 鼠标标识符映射,键为按下的鼠标按键类型,值表示本次操作的唯一标识符,在按下、移动、抬起过程中保持一致 */
|
||||
protected mouseId: Map<MouseType, number> = new Map();
|
||||
/** 当前所有的触摸标识符 */
|
||||
protected touchId: Set<number> = new Set();
|
||||
readonly touchId: Set<number> = new Set();
|
||||
|
||||
//#endregion
|
||||
|
||||
@ -650,7 +650,7 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
|
||||
//#region 父子关系
|
||||
|
||||
checkRoot() {
|
||||
checkRoot(): RenderItem | null {
|
||||
if (this._root) return this._root;
|
||||
if (this.isRoot) return this;
|
||||
let ele: RenderItem = this;
|
||||
@ -756,10 +756,12 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
type: ActionType,
|
||||
progress: EventProgress
|
||||
): keyof ERenderItemActionEvent {
|
||||
if (progress === EventProgress.Capture) {
|
||||
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] as keyof ERenderItemActionEvent;
|
||||
return eventNameMap[type];
|
||||
}
|
||||
}
|
||||
|
||||
@ -829,6 +831,7 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
progress: EventProgress,
|
||||
event: ActionEventMap[T]
|
||||
): ActionEventMap[T] | null {
|
||||
if (this.noEvent) return null;
|
||||
if (progress === EventProgress.Capture) {
|
||||
// 捕获阶段需要计算鼠标位置
|
||||
const tran = this.transformFallThrough
|
||||
@ -836,7 +839,6 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
: this._transform;
|
||||
if (!tran) return null;
|
||||
const [nx, ny] = this.calActionPosition(event, tran);
|
||||
|
||||
const inElement = this.isActionInElement(nx, ny);
|
||||
// 在元素范围内,执行事件
|
||||
const newEvent: ActionEventMap[T] = {
|
||||
@ -877,17 +879,6 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
if (inElement) {
|
||||
this._root?.hoverElement(this);
|
||||
}
|
||||
if (this.hovered && !inElement) {
|
||||
this.hovered = false;
|
||||
this.emit('leaveCapture', event);
|
||||
this.emit('leave', event);
|
||||
return false;
|
||||
} else if (!this.hovered && inElement) {
|
||||
this.hovered = true;
|
||||
this.emit('enterCapture', event);
|
||||
this.emit('enter', event);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ActionType.Down: {
|
||||
@ -929,10 +920,15 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
* @returns 是否继续传递事件
|
||||
*/
|
||||
protected processBubble<T extends ActionType>(
|
||||
_type: T,
|
||||
type: T,
|
||||
_event: ActionEventMap[T],
|
||||
inElement: boolean
|
||||
): boolean {
|
||||
switch (type) {
|
||||
case ActionType.Enter:
|
||||
case ActionType.Leave:
|
||||
return false;
|
||||
}
|
||||
return inElement;
|
||||
}
|
||||
|
||||
@ -1039,6 +1035,21 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义处理 props,自定义元素需要 override 此函数来处理 props
|
||||
* @param key 传入的 props 的键名
|
||||
* @param prevValue 这个 props 之前的值
|
||||
* @param nextValue 这个 props 传入的值
|
||||
* @returns 是否处理成功
|
||||
*/
|
||||
protected handleProps(
|
||||
_key: string,
|
||||
_prevValue: any,
|
||||
_nextValue: any
|
||||
): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
patchProp(
|
||||
key: string,
|
||||
prevValue: any,
|
||||
@ -1047,6 +1058,7 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
_parentComponent?: ComponentInternalInstance | null
|
||||
): void {
|
||||
if (isNil(prevValue) && isNil(nextValue)) return;
|
||||
if (this.handleProps(key, prevValue, nextValue)) return;
|
||||
switch (key) {
|
||||
case 'x': {
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
@ -1093,11 +1105,16 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
this.setHD(nextValue);
|
||||
return;
|
||||
}
|
||||
case 'antiAliasing': {
|
||||
case 'anti': {
|
||||
if (!this.assertType(nextValue, 'boolean', key)) return;
|
||||
this.setAntiAliasing(nextValue);
|
||||
return;
|
||||
}
|
||||
case 'noanti': {
|
||||
if (!this.assertType(nextValue, 'boolean', key)) return;
|
||||
this.setAntiAliasing(!nextValue);
|
||||
return;
|
||||
}
|
||||
case 'hidden': {
|
||||
if (!this.assertType(nextValue, 'boolean', key)) return;
|
||||
if (nextValue) this.hide();
|
||||
|
@ -18,7 +18,6 @@ import { getDamageColor } from '@/plugin/utils';
|
||||
import { ERenderItemEvent, RenderItem } from '../item';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { Transform } from '../transform';
|
||||
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
||||
import { transformCanvas } from '../utils';
|
||||
|
||||
const ensureFloorDamage = Mota.require('fn', 'ensureFloorDamage');
|
||||
@ -529,46 +528,44 @@ export class Damage extends RenderItem<EDamageEvent> {
|
||||
// console.timeEnd('damage');
|
||||
}
|
||||
|
||||
patchProp(
|
||||
protected handleProps(
|
||||
key: string,
|
||||
prevValue: any,
|
||||
nextValue: any,
|
||||
namespace?: ElementNamespace,
|
||||
parentComponent?: ComponentInternalInstance | null
|
||||
): void {
|
||||
_prevValue: any,
|
||||
nextValue: any
|
||||
): boolean {
|
||||
switch (key) {
|
||||
case 'mapWidth':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setMapSize(nextValue, this.mapHeight);
|
||||
return;
|
||||
return true;
|
||||
case 'mapHeight':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setMapSize(this.mapWidth, nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'cellSize':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setCellSize(nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'enemy':
|
||||
if (!this.assertType(nextValue, 'object', key)) return;
|
||||
if (!this.assertType(nextValue, 'object', key)) return false;
|
||||
this.updateCollection(nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'font':
|
||||
if (!this.assertType(nextValue, 'string', key)) return;
|
||||
if (!this.assertType(nextValue, 'string', key)) return false;
|
||||
this.font = nextValue;
|
||||
this.update();
|
||||
return;
|
||||
return true;
|
||||
case 'strokeStyle':
|
||||
this.strokeStyle = nextValue;
|
||||
this.update();
|
||||
return;
|
||||
return true;
|
||||
case 'strokeWidth':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.strokeWidth = nextValue;
|
||||
this.update();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||
return false;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||
import { ERenderItemEvent, RenderItem } from '../item';
|
||||
import { Transform } from '../transform';
|
||||
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
||||
import { clamp, isNil } from 'lodash-es';
|
||||
import { logger } from '@/core/common/logger';
|
||||
|
||||
@ -167,15 +166,22 @@ export abstract class GraphicItemBase
|
||||
}
|
||||
const path = this.cachePath;
|
||||
if (!path) return false;
|
||||
const fixX = x * devicePixelRatio;
|
||||
const fixY = y * devicePixelRatio;
|
||||
ctx.lineWidth = this.lineWidth;
|
||||
ctx.lineCap = this.lineCap;
|
||||
ctx.lineJoin = this.lineJoin;
|
||||
ctx.setLineDash(this.lineDash);
|
||||
switch (this.mode) {
|
||||
case GraphicMode.Fill:
|
||||
return ctx.isPointInPath(path, x, y, this.fillRule);
|
||||
return ctx.isPointInPath(path, fixX, fixY, this.fillRule);
|
||||
case GraphicMode.Stroke:
|
||||
return ctx.isPointInStroke(path, fixX, fixY);
|
||||
case GraphicMode.FillAndStroke:
|
||||
case GraphicMode.StrokeAndFill:
|
||||
return (
|
||||
ctx.isPointInPath(path, x, y, this.fillRule) ||
|
||||
ctx.isPointInStroke(path, x, y)
|
||||
ctx.isPointInPath(path, fixX, fixY, this.fillRule) ||
|
||||
ctx.isPointInStroke(path, fixX, fixY)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -287,69 +293,66 @@ export abstract class GraphicItemBase
|
||||
ctx.miterLimit = this.miterLimit;
|
||||
}
|
||||
|
||||
patchProp(
|
||||
protected handleProps(
|
||||
key: string,
|
||||
prevValue: any,
|
||||
nextValue: any,
|
||||
namespace?: ElementNamespace,
|
||||
parentComponent?: ComponentInternalInstance | null
|
||||
): void {
|
||||
if (isNil(prevValue) && isNil(nextValue)) return;
|
||||
_prevValue: any,
|
||||
nextValue: any
|
||||
): boolean {
|
||||
switch (key) {
|
||||
case 'fill':
|
||||
if (!this.assertType(nextValue, 'boolean', key)) return;
|
||||
if (!this.assertType(nextValue, 'boolean', key)) return false;
|
||||
this.checkMode(GraphicModeProp.Fill, nextValue);
|
||||
break;
|
||||
return true;
|
||||
case 'stroke':
|
||||
if (!this.assertType(nextValue, 'boolean', key)) return;
|
||||
if (!this.assertType(nextValue, 'boolean', key)) return false;
|
||||
this.checkMode(GraphicModeProp.Stroke, nextValue);
|
||||
break;
|
||||
return true;
|
||||
case 'strokeAndFill':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.checkMode(GraphicModeProp.StrokeAndFill, nextValue);
|
||||
break;
|
||||
return true;
|
||||
case 'fillRule':
|
||||
if (!this.assertType(nextValue, 'string', key)) return;
|
||||
if (!this.assertType(nextValue, 'string', key)) return false;
|
||||
this.setFillRule(nextValue);
|
||||
break;
|
||||
return true;
|
||||
case 'fillStyle':
|
||||
this.setFillStyle(nextValue);
|
||||
break;
|
||||
return true;
|
||||
case 'strokeStyle':
|
||||
this.setStrokeStyle(nextValue);
|
||||
break;
|
||||
return true;
|
||||
case 'lineWidth':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.lineWidth = nextValue;
|
||||
this.update();
|
||||
break;
|
||||
return true;
|
||||
case 'lineDash':
|
||||
if (!this.assertType(nextValue, Array, key)) return;
|
||||
if (!this.assertType(nextValue, Array, key)) return false;
|
||||
this.lineDash = nextValue as number[];
|
||||
this.update();
|
||||
break;
|
||||
return true;
|
||||
case 'lineDashOffset':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.lineDashOffset = nextValue;
|
||||
this.update();
|
||||
break;
|
||||
return true;
|
||||
case 'lineJoin':
|
||||
if (!this.assertType(nextValue, 'string', key)) return;
|
||||
if (!this.assertType(nextValue, 'string', key)) return false;
|
||||
this.lineJoin = nextValue;
|
||||
this.update();
|
||||
break;
|
||||
return true;
|
||||
case 'lineCap':
|
||||
if (!this.assertType(nextValue, 'string', key)) return;
|
||||
if (!this.assertType(nextValue, 'string', key)) return false;
|
||||
this.lineCap = nextValue;
|
||||
this.update();
|
||||
break;
|
||||
return true;
|
||||
case 'miterLimit':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.miterLimit = nextValue;
|
||||
this.update();
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -407,29 +410,27 @@ export class Circle extends GraphicItemBase {
|
||||
this.update();
|
||||
}
|
||||
|
||||
patchProp(
|
||||
protected handleProps(
|
||||
key: string,
|
||||
prevValue: any,
|
||||
nextValue: any,
|
||||
namespace?: ElementNamespace,
|
||||
parentComponent?: ComponentInternalInstance | null
|
||||
): void {
|
||||
nextValue: any
|
||||
): boolean {
|
||||
switch (key) {
|
||||
case 'radius':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setRadius(nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'start':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setAngle(nextValue, this.end);
|
||||
return;
|
||||
return true;
|
||||
case 'end':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setAngle(this.start, nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'circle': {
|
||||
const value = nextValue as CircleParams;
|
||||
if (!this.assertType(value, Array, key)) return;
|
||||
if (!this.assertType(value, Array, key)) return false;
|
||||
const [cx, cy, radius, start, end] = value;
|
||||
if (!isNil(cx) && !isNil(cy)) {
|
||||
this.pos(cx, cy);
|
||||
@ -440,10 +441,10 @@ export class Circle extends GraphicItemBase {
|
||||
if (!isNil(start) && !isNil(end)) {
|
||||
this.setAngle(start, end);
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||
return super.handleProps(key, prevValue, nextValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -494,33 +495,31 @@ export class Ellipse extends GraphicItemBase {
|
||||
this.update();
|
||||
}
|
||||
|
||||
patchProp(
|
||||
protected handleProps(
|
||||
key: string,
|
||||
prevValue: any,
|
||||
nextValue: any,
|
||||
namespace?: ElementNamespace,
|
||||
parentComponent?: ComponentInternalInstance | null
|
||||
): void {
|
||||
nextValue: any
|
||||
): boolean {
|
||||
switch (key) {
|
||||
case 'radiusX':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setRadius(nextValue, this.radiusY);
|
||||
return;
|
||||
return true;
|
||||
case 'radiusY':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setRadius(this.radiusY, nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'start':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setAngle(nextValue, this.end);
|
||||
return;
|
||||
return true;
|
||||
case 'end':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setAngle(this.start, nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'ellipse': {
|
||||
const value = nextValue as EllipseParams;
|
||||
if (!this.assertType(value, Array, key)) return;
|
||||
if (!this.assertType(value, Array, key)) return false;
|
||||
const [cx, cy, radiusX, radiusY, start, end] = value;
|
||||
if (!isNil(cx) && !isNil(cy)) {
|
||||
this.pos(cx, cy);
|
||||
@ -531,10 +530,10 @@ export class Ellipse extends GraphicItemBase {
|
||||
if (!isNil(start) && !isNil(end)) {
|
||||
this.setAngle(start, end);
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||
return super.handleProps(key, prevValue, nextValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -584,37 +583,37 @@ export class Line extends GraphicItemBase {
|
||||
this.pathDirty = true;
|
||||
}
|
||||
|
||||
patchProp(
|
||||
protected handleProps(
|
||||
key: string,
|
||||
prevValue: any,
|
||||
nextValue: any,
|
||||
namespace?: ElementNamespace,
|
||||
parentComponent?: ComponentInternalInstance | null
|
||||
): void {
|
||||
nextValue: any
|
||||
): boolean {
|
||||
switch (key) {
|
||||
case 'x1':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setPoint1(nextValue, this.y1);
|
||||
return;
|
||||
return true;
|
||||
case 'y1':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setPoint1(this.x1, nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'x2':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setPoint2(nextValue, this.y2);
|
||||
return;
|
||||
return true;
|
||||
case 'y2':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setPoint2(this.x2, nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'line':
|
||||
if (!this.assertType(nextValue as number[], Array, key)) return;
|
||||
if (!this.assertType(nextValue as number[], Array, key)) {
|
||||
return false;
|
||||
}
|
||||
this.setPoint1(nextValue[0], nextValue[1]);
|
||||
this.setPoint2(nextValue[2], nextValue[3]);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||
return super.handleProps(key, prevValue, nextValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -685,6 +684,10 @@ export class BezierCurve extends GraphicItemBase {
|
||||
this.update();
|
||||
}
|
||||
|
||||
protected isActionInElement(x: number, y: number): boolean {
|
||||
return x >= 0 && x < this.width && y >= 0 && y < this.height;
|
||||
}
|
||||
|
||||
private fitRect() {
|
||||
const left = Math.min(this.sx, this.cp1x, this.cp2x, this.ex);
|
||||
const top = Math.min(this.sy, this.cp1y, this.cp2y, this.ey);
|
||||
@ -695,55 +698,55 @@ export class BezierCurve extends GraphicItemBase {
|
||||
this.pathDirty = true;
|
||||
}
|
||||
|
||||
patchProp(
|
||||
protected handleProps(
|
||||
key: string,
|
||||
prevValue: any,
|
||||
nextValue: any,
|
||||
namespace?: ElementNamespace,
|
||||
parentComponent?: ComponentInternalInstance | null
|
||||
): void {
|
||||
nextValue: any
|
||||
): boolean {
|
||||
switch (key) {
|
||||
case 'sx':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setStart(nextValue, this.sy);
|
||||
return;
|
||||
return true;
|
||||
case 'sy':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setStart(this.sx, nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'cp1x':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setControl1(nextValue, this.cp1y);
|
||||
return;
|
||||
return true;
|
||||
case 'cp1y':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setControl1(this.cp1x, nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'cp2x':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setControl2(nextValue, this.cp2y);
|
||||
return;
|
||||
return true;
|
||||
case 'cp2y':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setControl2(this.cp2x, nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'ex':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setEnd(nextValue, this.ey);
|
||||
return;
|
||||
return true;
|
||||
case 'ey':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setEnd(this.ex, nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'curve':
|
||||
if (!this.assertType(nextValue as number[], Array, key)) return;
|
||||
if (!this.assertType(nextValue as number[], Array, key)) {
|
||||
return false;
|
||||
}
|
||||
this.setStart(nextValue[0], nextValue[1]);
|
||||
this.setControl1(nextValue[2], nextValue[3]);
|
||||
this.setControl2(nextValue[4], nextValue[5]);
|
||||
this.setEnd(nextValue[6], nextValue[7]);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||
return super.handleProps(key, prevValue, nextValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -822,46 +825,50 @@ export class QuadraticCurve extends GraphicItemBase {
|
||||
this.pathDirty = true;
|
||||
}
|
||||
|
||||
patchProp(
|
||||
protected isActionInElement(x: number, y: number): boolean {
|
||||
return x >= 0 && x < this.width && y >= 0 && y < this.height;
|
||||
}
|
||||
|
||||
protected handleProps(
|
||||
key: string,
|
||||
prevValue: any,
|
||||
nextValue: any,
|
||||
namespace?: ElementNamespace,
|
||||
parentComponent?: ComponentInternalInstance | null
|
||||
): void {
|
||||
nextValue: any
|
||||
): boolean {
|
||||
switch (key) {
|
||||
case 'sx':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setStart(nextValue, this.sy);
|
||||
return;
|
||||
return true;
|
||||
case 'sy':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setStart(this.sx, nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'cpx':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setControl(nextValue, this.cpy);
|
||||
return;
|
||||
return true;
|
||||
case 'cpy':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setControl(this.cpx, nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'ex':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setEnd(nextValue, this.ey);
|
||||
return;
|
||||
return true;
|
||||
case 'ey':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setEnd(this.ex, nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'curve':
|
||||
if (!this.assertType(nextValue as number[], Array, key)) return;
|
||||
if (!this.assertType(nextValue as number[], Array, key)) {
|
||||
return false;
|
||||
}
|
||||
this.setStart(nextValue[0], nextValue[1]);
|
||||
this.setControl(nextValue[2], nextValue[3]);
|
||||
this.setEnd(nextValue[4], nextValue[5]);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||
return super.handleProps(key, prevValue, nextValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -886,22 +893,24 @@ export class Path extends GraphicItemBase {
|
||||
this.update();
|
||||
}
|
||||
|
||||
patchProp(
|
||||
protected isActionInElement(x: number, y: number): boolean {
|
||||
return x >= 0 && x < this.width && y >= 0 && y < this.height;
|
||||
}
|
||||
|
||||
protected handleProps(
|
||||
key: string,
|
||||
prevValue: any,
|
||||
nextValue: any,
|
||||
namespace?: ElementNamespace,
|
||||
parentComponent?: ComponentInternalInstance | null
|
||||
): void {
|
||||
nextValue: any
|
||||
): boolean {
|
||||
switch (key) {
|
||||
case 'path':
|
||||
if (!this.assertType(nextValue, Path2D, key)) return;
|
||||
if (!this.assertType(nextValue, Path2D, key)) return false;
|
||||
this.path = nextValue;
|
||||
this.pathDirty = true;
|
||||
this.update();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||
return super.handleProps(key, prevValue, nextValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1041,27 +1050,25 @@ export class RectR extends GraphicItemBase {
|
||||
}
|
||||
}
|
||||
|
||||
patchProp(
|
||||
protected handleProps(
|
||||
key: string,
|
||||
prevValue: any,
|
||||
nextValue: any,
|
||||
namespace?: ElementNamespace,
|
||||
parentComponent?: ComponentInternalInstance | null
|
||||
): void {
|
||||
nextValue: any
|
||||
): boolean {
|
||||
switch (key) {
|
||||
case 'circle': {
|
||||
const value = nextValue as RectRCircleParams;
|
||||
if (!this.assertType(value, Array, key)) return;
|
||||
if (!this.assertType(value, Array, key)) return false;
|
||||
this.setCircle(value);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
case 'ellipse': {
|
||||
const value = nextValue as RectREllipseParams;
|
||||
if (!this.assertType(value, Array, key)) return;
|
||||
if (!this.assertType(value, Array, key)) return false;
|
||||
this.setEllipse(value);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||
return super.handleProps(key, prevValue, nextValue);
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import { BlockCacher, CanvasCacheItem, ICanvasCacheItem } from './block';
|
||||
import { Transform } from '../transform';
|
||||
import { LayerFloorBinder, LayerGroupFloorBinder } from './floor';
|
||||
import { RenderAdapter } from '../adapter';
|
||||
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
||||
import { IAnimateFrame, renderEmits } from '../frame';
|
||||
|
||||
export interface ILayerGroupRenderExtends {
|
||||
@ -332,36 +331,34 @@ export class LayerGroup
|
||||
}
|
||||
}
|
||||
|
||||
patchProp(
|
||||
protected handleProps(
|
||||
key: string,
|
||||
prevValue: any,
|
||||
nextValue: any,
|
||||
namespace?: ElementNamespace,
|
||||
parentComponent?: ComponentInternalInstance | null
|
||||
): void {
|
||||
_prevValue: any,
|
||||
nextValue: any
|
||||
): boolean {
|
||||
switch (key) {
|
||||
case 'cellSize':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setCellSize(nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'blockSize':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setBlockSize(nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'floorId': {
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
const binder = this.getExtends('floor-binder');
|
||||
if (binder instanceof LayerGroupFloorBinder) {
|
||||
binder.bindFloor(nextValue);
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
case 'camera':
|
||||
if (!this.assertType(nextValue, Transform, key)) return;
|
||||
if (!this.assertType(nextValue, Transform, key)) return false;
|
||||
this.camera = nextValue;
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||
return false;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
@ -1438,16 +1435,14 @@ export class Layer extends Container<ELayerEvent> {
|
||||
});
|
||||
}
|
||||
|
||||
patchProp(
|
||||
protected handleProps(
|
||||
key: string,
|
||||
prevValue: any,
|
||||
nextValue: any,
|
||||
namespace?: ElementNamespace,
|
||||
parentComponent?: ComponentInternalInstance | null
|
||||
): void {
|
||||
_prevValue: any,
|
||||
nextValue: any
|
||||
): boolean {
|
||||
switch (key) {
|
||||
case 'layer': {
|
||||
if (!this.assertType(nextValue, 'string', key)) return;
|
||||
if (!this.assertType(nextValue, 'string', key)) return false;
|
||||
const parent = this.parent;
|
||||
if (parent instanceof LayerGroup) {
|
||||
parent.removeLayer(this);
|
||||
@ -1457,30 +1452,30 @@ export class Layer extends Container<ELayerEvent> {
|
||||
this.layer = nextValue;
|
||||
}
|
||||
this.update();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
case 'cellSize':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setCellSize(nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'mapWidth':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setMapSize(nextValue, this.mapHeight);
|
||||
return;
|
||||
return true;
|
||||
case 'mapHeight':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setMapSize(this.mapWidth, nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'background':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setBackground(nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'floorImage':
|
||||
if (!this.assertType(nextValue, Array, key)) return;
|
||||
if (!this.assertType(nextValue, Array, key)) return false;
|
||||
this.setFloorImage(nextValue as FloorAnimate[]);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||
return false;
|
||||
}
|
||||
|
||||
private addToGroup(group: LayerGroup) {
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||
import { ERenderItemEvent, RenderItem, RenderItemPosition } from '../item';
|
||||
import { Transform } from '../transform';
|
||||
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
||||
import { AutotileRenderable, RenderableData } from '../cache';
|
||||
import { texture } from '../cache';
|
||||
import { isNil } from 'lodash-es';
|
||||
@ -114,33 +113,31 @@ export class Text extends RenderItem<ETextEvent> {
|
||||
this.size(width, actualBoundingBoxAscent + actualBoundingBoxDescent);
|
||||
}
|
||||
|
||||
patchProp(
|
||||
protected handleProps(
|
||||
key: string,
|
||||
prevValue: any,
|
||||
nextValue: any,
|
||||
namespace?: ElementNamespace,
|
||||
parentComponent?: ComponentInternalInstance | null
|
||||
): void {
|
||||
_prevValue: any,
|
||||
nextValue: any
|
||||
): boolean {
|
||||
switch (key) {
|
||||
case 'text':
|
||||
if (!this.assertType(nextValue, 'string', key)) return;
|
||||
if (!this.assertType(nextValue, 'string', key)) return false;
|
||||
this.setText(nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'fillStyle':
|
||||
this.setStyle(nextValue, this.strokeStyle);
|
||||
return;
|
||||
return true;
|
||||
case 'strokeStyle':
|
||||
this.setStyle(this.fillStyle, nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'font':
|
||||
if (!this.assertType(nextValue, 'string', key)) return;
|
||||
if (!this.assertType(nextValue, 'string', key)) return false;
|
||||
this.setFont(nextValue);
|
||||
break;
|
||||
case 'strokeWidth':
|
||||
this.setStrokeWidth(nextValue);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,19 +178,17 @@ export class Image extends RenderItem<EImageEvent> {
|
||||
this.update();
|
||||
}
|
||||
|
||||
patchProp(
|
||||
protected handleProps(
|
||||
key: string,
|
||||
prevValue: any,
|
||||
nextValue: any,
|
||||
namespace?: ElementNamespace,
|
||||
parentComponent?: ComponentInternalInstance | null
|
||||
): void {
|
||||
_prevValue: any,
|
||||
nextValue: any
|
||||
): boolean {
|
||||
switch (key) {
|
||||
case 'image':
|
||||
this.setImage(nextValue);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,6 +208,10 @@ export class Comment extends RenderItem {
|
||||
_canvas: MotaOffscreenCanvas2D,
|
||||
_transform: Transform
|
||||
): void {}
|
||||
|
||||
protected handleProps(): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export interface EIconEvent extends ERenderItemEvent {}
|
||||
@ -313,31 +312,29 @@ export class Icon extends RenderItem<EIconEvent> implements IAnimateFrame {
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
patchProp(
|
||||
protected handleProps(
|
||||
key: string,
|
||||
prevValue: any,
|
||||
nextValue: any,
|
||||
namespace?: ElementNamespace,
|
||||
parentComponent?: ComponentInternalInstance | null
|
||||
): void {
|
||||
_prevValue: any,
|
||||
nextValue: any
|
||||
): boolean {
|
||||
switch (key) {
|
||||
case 'icon':
|
||||
this.setIcon(nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'animate':
|
||||
if (!this.assertType(nextValue, 'boolean', key)) return;
|
||||
if (!this.assertType(nextValue, 'boolean', key)) return false;
|
||||
this.animate = nextValue;
|
||||
if (nextValue) renderEmits.addFramer(this);
|
||||
else renderEmits.removeFramer(this);
|
||||
this.update();
|
||||
return;
|
||||
return true;
|
||||
case 'frame':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.frame = nextValue;
|
||||
this.update();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -544,23 +541,21 @@ export class Winskin extends RenderItem<EWinskinEvent> {
|
||||
this.update();
|
||||
}
|
||||
|
||||
patchProp(
|
||||
protected handleProps(
|
||||
key: string,
|
||||
prevValue: any,
|
||||
nextValue: any,
|
||||
namespace?: ElementNamespace,
|
||||
parentComponent?: ComponentInternalInstance | null
|
||||
): void {
|
||||
_prevValue: any,
|
||||
nextValue: any
|
||||
): boolean {
|
||||
switch (key) {
|
||||
case 'image':
|
||||
if (!this.assertType(nextValue, 'string', key)) return;
|
||||
if (!this.assertType(nextValue, 'string', key)) return false;
|
||||
this.setImageByName(nextValue);
|
||||
return;
|
||||
return true;
|
||||
case 'borderSize':
|
||||
if (!this.assertType(nextValue, 'number', key)) return;
|
||||
if (!this.assertType(nextValue, 'number', key)) return false;
|
||||
this.setBorderSize(nextValue);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,9 @@ import { logger } from '../common/logger';
|
||||
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||
import { Container } from './container';
|
||||
import {
|
||||
ActionEventMap,
|
||||
ActionType,
|
||||
IActionEvent,
|
||||
IActionEventBase,
|
||||
IWheelEvent,
|
||||
MouseType,
|
||||
WheelType
|
||||
@ -49,6 +49,10 @@ export class MotaRenderer extends Container implements IRenderTreeRoot {
|
||||
private abort?: AbortController;
|
||||
/** 根据捕获行为判断光标样式 */
|
||||
private targetCursor: string = 'auto';
|
||||
/** 当前鼠标覆盖的元素 */
|
||||
private hoveredElement: Set<RenderItem> = new Set();
|
||||
/** 本次交互前鼠标覆盖的元素 */
|
||||
private beforeHovered: Set<RenderItem> = new Set();
|
||||
|
||||
target!: MotaOffscreenCanvas2D;
|
||||
|
||||
@ -112,17 +116,26 @@ export class MotaRenderer extends Container implements IRenderTreeRoot {
|
||||
this.lastMouse
|
||||
);
|
||||
this.targetCursor = 'auto';
|
||||
const temp = this.beforeHovered;
|
||||
temp.clear();
|
||||
this.beforeHovered = this.hoveredElement;
|
||||
this.hoveredElement = temp;
|
||||
this.captureEvent(ActionType.Move, event);
|
||||
});
|
||||
canvas.addEventListener('mouseenter', ev => {
|
||||
const event = this.createMouseAction(ev, ActionType.Enter);
|
||||
this.emit('enterCapture', event);
|
||||
this.emit('enter', event);
|
||||
if (this.targetCursor !== this.target.canvas.style.cursor) {
|
||||
this.target.canvas.style.cursor = this.targetCursor;
|
||||
}
|
||||
this.checkMouseEnterLeave(
|
||||
ev,
|
||||
this.beforeHovered,
|
||||
this.hoveredElement
|
||||
);
|
||||
});
|
||||
canvas.addEventListener('mouseleave', ev => {
|
||||
const event = this.createMouseAction(ev, ActionType.Leave);
|
||||
this.emit('leaveCapture', event);
|
||||
this.emit('leave', event);
|
||||
this.hoveredElement.forEach(v => {
|
||||
v.emit('leave', this.createMouseActionBase(ev, v));
|
||||
});
|
||||
this.hoveredElement.clear();
|
||||
this.beforeHovered.clear();
|
||||
});
|
||||
document.addEventListener('touchstart', ev => {
|
||||
this.createTouchAction(ev, ActionType.Down).forEach(v => {
|
||||
@ -133,27 +146,29 @@ export class MotaRenderer extends Container implements IRenderTreeRoot {
|
||||
this.createTouchAction(ev, ActionType.Up).forEach(v => {
|
||||
this.captureEvent(ActionType.Up, v);
|
||||
this.captureEvent(ActionType.Click, v);
|
||||
this.touchInfo.delete(v.identifier);
|
||||
});
|
||||
});
|
||||
document.addEventListener('touchcancel', ev => {
|
||||
this.createTouchAction(ev, ActionType.Up).forEach(v => {
|
||||
this.captureEvent(ActionType.Up, v);
|
||||
this.touchInfo.delete(v.identifier);
|
||||
});
|
||||
});
|
||||
document.addEventListener('touchmove', ev => {
|
||||
this.createTouchAction(ev, ActionType.Move).forEach(v => {
|
||||
const touch = this.touchInfo.get(v.identifier);
|
||||
if (!touch) return;
|
||||
const inElement = this.isTouchInCanvas(v.offsetX, v.offsetY);
|
||||
if (touch.hovered && !inElement) {
|
||||
this.emit('leaveCapture', v);
|
||||
this.emit('leave', v);
|
||||
}
|
||||
if (!touch.hovered && inElement) {
|
||||
this.emit('enterCapture', v);
|
||||
this.emit('enter', v);
|
||||
}
|
||||
const temp = this.beforeHovered;
|
||||
temp.clear();
|
||||
this.beforeHovered = this.hoveredElement;
|
||||
this.hoveredElement = temp;
|
||||
this.captureEvent(ActionType.Move, v);
|
||||
this.checkTouchEnterLeave(
|
||||
ev,
|
||||
this.beforeHovered,
|
||||
this.hoveredElement
|
||||
);
|
||||
});
|
||||
});
|
||||
canvas.addEventListener('wheel', ev => {
|
||||
@ -246,6 +261,39 @@ export class MotaRenderer extends Container implements IRenderTreeRoot {
|
||||
return buttons;
|
||||
}
|
||||
|
||||
private createMouseActionBase(
|
||||
event: MouseEvent,
|
||||
target: RenderItem = this,
|
||||
mouse: MouseType = this.getMouseType(event)
|
||||
): IActionEventBase {
|
||||
return {
|
||||
target: target,
|
||||
touch: false,
|
||||
type: mouse,
|
||||
buttons: this.getMouseButtons(event),
|
||||
altKey: event.altKey,
|
||||
ctrlKey: event.ctrlKey,
|
||||
shiftKey: event.shiftKey,
|
||||
metaKey: event.metaKey
|
||||
};
|
||||
}
|
||||
|
||||
private createTouchActionBase(
|
||||
event: TouchEvent,
|
||||
target: RenderItem
|
||||
): IActionEventBase {
|
||||
return {
|
||||
target: target,
|
||||
touch: false,
|
||||
type: MouseType.Left,
|
||||
buttons: MouseType.Left,
|
||||
altKey: event.altKey,
|
||||
ctrlKey: event.ctrlKey,
|
||||
shiftKey: event.shiftKey,
|
||||
metaKey: event.metaKey
|
||||
};
|
||||
}
|
||||
|
||||
private createMouseAction(
|
||||
event: MouseEvent,
|
||||
type: ActionType,
|
||||
@ -385,14 +433,40 @@ export class MotaRenderer extends Container implements IRenderTreeRoot {
|
||||
return list;
|
||||
}
|
||||
|
||||
bubbleEvent<T extends ActionType>(
|
||||
type: T,
|
||||
event: ActionEventMap[T]
|
||||
): ActionEventMap[T] | null {
|
||||
if (this.targetCursor !== this.target.canvas.style.cursor) {
|
||||
this.target.canvas.style.cursor = this.targetCursor;
|
||||
}
|
||||
return super.bubbleEvent(type, event);
|
||||
private checkMouseEnterLeave(
|
||||
event: MouseEvent,
|
||||
before: Set<RenderItem>,
|
||||
now: Set<RenderItem>
|
||||
) {
|
||||
// 先 leave,再 enter
|
||||
before.forEach(v => {
|
||||
if (!now.has(v)) {
|
||||
v.emit('leave', this.createMouseActionBase(event, v));
|
||||
}
|
||||
});
|
||||
now.forEach(v => {
|
||||
if (!before.has(v)) {
|
||||
v.emit('enter', this.createMouseActionBase(event, v));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private checkTouchEnterLeave(
|
||||
event: TouchEvent,
|
||||
before: Set<RenderItem>,
|
||||
now: Set<RenderItem>
|
||||
) {
|
||||
// 先 leave,再 enter
|
||||
before.forEach(v => {
|
||||
if (!now.has(v)) {
|
||||
v.emit('leave', this.createTouchActionBase(event, v));
|
||||
}
|
||||
});
|
||||
now.forEach(v => {
|
||||
if (!before.has(v)) {
|
||||
v.emit('enter', this.createTouchActionBase(event, v));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
update(_item: RenderItem = this) {
|
||||
@ -465,9 +539,10 @@ export class MotaRenderer extends Container implements IRenderTreeRoot {
|
||||
}
|
||||
|
||||
hoverElement(element: RenderItem): void {
|
||||
if (element.cursor !== 'auto') {
|
||||
if (element.cursor !== 'inherit') {
|
||||
this.targetCursor = element.cursor;
|
||||
}
|
||||
this.hoveredElement.add(element);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
@ -76,7 +76,7 @@ type _Define<P extends BaseProps, E extends ERenderItemEvent> = DefineComponent<
|
||||
Readonly<P & MappingEvent<E>>
|
||||
>;
|
||||
|
||||
type TagDefine<T extends object, E extends ERenderItemEvent> = T &
|
||||
export type TagDefine<T extends object, E extends ERenderItemEvent> = T &
|
||||
MappingEvent<E> &
|
||||
ReservedProps;
|
||||
|
||||
|
@ -8,6 +8,13 @@ import { ERenderItemEvent, RenderItem } from '../item';
|
||||
import { tagMap } from './map';
|
||||
import { logger } from '@/core/common/logger';
|
||||
import { Comment, ETextEvent, Text } from '../preset/misc';
|
||||
import { BaseProps } from './props';
|
||||
import { TagDefine } from './elements';
|
||||
|
||||
export type DefaultProps<
|
||||
P extends BaseProps = BaseProps,
|
||||
E extends ERenderItemEvent = ERenderItemEvent
|
||||
> = TagDefine<P, E>;
|
||||
|
||||
export const { createApp, render } = createRenderer<RenderItem, RenderItem>({
|
||||
patchProp: function (
|
||||
|
@ -24,27 +24,47 @@ export interface CustomProps {
|
||||
}
|
||||
|
||||
export interface BaseProps {
|
||||
/** 元素的横坐标 */
|
||||
x?: number;
|
||||
/** 元素的纵坐标 */
|
||||
y?: number;
|
||||
/** 元素的横向锚点位置 */
|
||||
anchorX?: number;
|
||||
/** 元素的纵向锚点位置 */
|
||||
anchorY?: number;
|
||||
/** 元素的纵深,值越大越靠上 */
|
||||
zIndex?: number;
|
||||
/** 元素的宽度 */
|
||||
width?: number;
|
||||
/** 元素的高度 */
|
||||
height?: number;
|
||||
/** 元素的滤镜 */
|
||||
filter?: string;
|
||||
/** 是否启用高清画布 */
|
||||
hd?: boolean;
|
||||
antiAliasing?: boolean;
|
||||
/** 是否启用抗锯齿 */
|
||||
anti?: boolean;
|
||||
/** 是否不启用抗锯齿,优先级大于 anti,主要用于像素图片渲染 */
|
||||
noanti?: boolean;
|
||||
/** 元素是否隐藏,可以用于一些画面效果,也可以用于调试 */
|
||||
hidden?: boolean;
|
||||
/** 元素的变换矩阵 */
|
||||
transform?: Transform;
|
||||
/** 元素的定位模式,static 表示常规定位,absolute 定位模式下元素位置始终处于左上角 */
|
||||
type?: RenderItemPosition;
|
||||
/** 是否启用缓存,用处较少,主要用于一些默认不启用缓存的元素的特殊优化 */
|
||||
cache?: boolean;
|
||||
/** 是否不启用缓存,优先级大于 cache,用处较少,主要用于一些特殊优化 */
|
||||
nocache?: boolean;
|
||||
/** 是否启用变换矩阵下穿,下穿模式下,当前元素会使用由父元素传递过来的变换矩阵,而非元素自身的 */
|
||||
fall?: boolean;
|
||||
/** 这个元素的唯一标识符,不可重复 */
|
||||
id?: string;
|
||||
/** 这个元素的不透明度 */
|
||||
alpha?: number;
|
||||
/** 这个元素与已渲染内容的混合模式,默认为 source-over */
|
||||
composite?: GlobalCompositeOperation;
|
||||
/** 鼠标放在这个元素上时的光标样式 */
|
||||
cursor?: string;
|
||||
/**
|
||||
* 定位属性,可以填 `[横坐标,纵坐标,宽度,高度,x锚点,y锚点]`,
|
||||
|
@ -6,7 +6,6 @@ import {
|
||||
} from './item';
|
||||
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||
import { Transform } from './transform';
|
||||
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
||||
|
||||
export interface ESpriteEvent extends ERenderItemEvent {}
|
||||
|
||||
@ -42,19 +41,17 @@ export class Sprite<
|
||||
this.update(this);
|
||||
}
|
||||
|
||||
patchProp(
|
||||
protected handleProps(
|
||||
key: string,
|
||||
prevValue: any,
|
||||
nextValue: any,
|
||||
namespace?: ElementNamespace,
|
||||
parentComponent?: ComponentInternalInstance | null
|
||||
): void {
|
||||
_prevValue: any,
|
||||
nextValue: any
|
||||
): boolean {
|
||||
switch (key) {
|
||||
case 'render':
|
||||
if (!this.assertType(nextValue, 'function', key)) return;
|
||||
if (!this.assertType(nextValue, 'function', key)) return false;
|
||||
this.setRenderFn(nextValue);
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { ElementLocator, Sprite } from '@/core/render';
|
||||
import { DefaultProps, ElementLocator, Sprite } from '@/core/render';
|
||||
import { defineComponent, ref, watch } from 'vue';
|
||||
import { SetupComponentOptions } from './types';
|
||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||
|
||||
interface ProgressProps {
|
||||
interface ProgressProps extends DefaultProps {
|
||||
/** 进度条的位置 */
|
||||
loc: ElementLocator;
|
||||
/** 进度条的进度,1表示完成,0表示未完成 */
|
||||
|
@ -10,12 +10,12 @@ import {
|
||||
} from 'vue';
|
||||
import { SetupComponentOptions } from './types';
|
||||
import { clamp } from 'lodash-es';
|
||||
import { ElementLocator } from '@/core/render';
|
||||
import { DefaultProps, ElementLocator } from '@/core/render';
|
||||
|
||||
/** 圆角矩形页码距离容器的边框大小,与 pageSize 相乘 */
|
||||
const RECT_PAD = 0.1;
|
||||
|
||||
export interface PageProps {
|
||||
export interface PageProps extends DefaultProps {
|
||||
/** 共有多少页 */
|
||||
pages: number;
|
||||
/** 页码组件的定位 */
|
||||
@ -40,6 +40,23 @@ const pageProps = {
|
||||
props: ['pages', 'loc', 'pageSize']
|
||||
} satisfies SetupComponentOptions<PageProps, {}, string, PageSlots>;
|
||||
|
||||
/**
|
||||
* 分页组件,用于多页切换,例如存档界面等。参数参考 {@link PageProps},函数接口参考 {@link PageExpose}
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* 用例如下,是一个在每页显示文字的用例,其中 page 表示第几页:
|
||||
* ```tsx
|
||||
* <Page maxPage={5}>
|
||||
* {
|
||||
* (page: number) => {
|
||||
* // 页码从第一页开始,因此这里索引要减一
|
||||
* return items[page - 1].map(v => <text text={v.text} />)
|
||||
* }
|
||||
* }
|
||||
* </Page>
|
||||
* ```
|
||||
*/
|
||||
export const Page = defineComponent<PageProps, {}, string, PageSlots>(
|
||||
(props, { slots, expose }) => {
|
||||
const nowPage = ref(1);
|
||||
@ -60,8 +77,8 @@ export const Page = defineComponent<PageProps, {}, string, PageSlots>(
|
||||
const textLoc = ref<ElementLocator>([0, 0, 0, 0]);
|
||||
|
||||
// 两个监听的参数
|
||||
const leftArrow = ref<Path2D>(new Path2D());
|
||||
const rightArrow = ref<Path2D>(new Path2D());
|
||||
const leftArrow = ref<Path2D>();
|
||||
const rightArrow = ref<Path2D>();
|
||||
|
||||
const isFirst = computed(() => nowPage.value === 1);
|
||||
const isLast = computed(() => nowPage.value === props.pages);
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
import { SetupComponentOptions } from './types';
|
||||
import {
|
||||
Container,
|
||||
DefaultProps,
|
||||
ElementLocator,
|
||||
RenderItem,
|
||||
Sprite,
|
||||
@ -38,7 +39,7 @@ export interface ScrollExpose {
|
||||
scrollTo(y: number, time?: number): void;
|
||||
}
|
||||
|
||||
export interface ScrollProps {
|
||||
export interface ScrollProps extends DefaultProps {
|
||||
loc: ElementLocator;
|
||||
hor?: boolean;
|
||||
noscroll?: boolean;
|
||||
|
@ -12,12 +12,12 @@ import {
|
||||
watch
|
||||
} from 'vue';
|
||||
import { logger } from '@/core/common/logger';
|
||||
import { Sprite } from '../../../core/render/sprite';
|
||||
import { ContainerProps } from '../../../core/render/renderer';
|
||||
import { Sprite } from '@/core/render/sprite';
|
||||
import { ContainerProps, DefaultProps } from '@/core/render/renderer';
|
||||
import { isNil } from 'lodash-es';
|
||||
import { SetupComponentOptions } from './types';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { Text } from '../../../core/render/preset';
|
||||
import { Text } from '@/core/render/preset';
|
||||
import {
|
||||
ITextContentConfig,
|
||||
TextContentTyper,
|
||||
@ -26,7 +26,7 @@ import {
|
||||
} from './textboxTyper';
|
||||
|
||||
export interface TextContentProps
|
||||
extends ContainerProps,
|
||||
extends DefaultProps,
|
||||
Partial<ITextContentConfig> {
|
||||
/** 显示的文字 */
|
||||
text?: string;
|
||||
|
@ -1125,8 +1125,6 @@ export class TextContentParser {
|
||||
this.checkRestLine(width, guess);
|
||||
}
|
||||
|
||||
console.log(this.renderable);
|
||||
|
||||
return {
|
||||
lineHeights: this.lineHeights,
|
||||
data: this.renderable
|
||||
|
Loading…
Reference in New Issue
Block a user