init: 2.B

This commit is contained in:
AncTe committed 2026-02-28 20:23:34 +08:00
commit 6bf32174d8
610 files changed
+151444

No files matched your search

+121
View File
@@ -0,0 +1,121 @@
import {
ComponentOptionsMixin,
defineComponent,
DefineComponent,
h,
ReservedProps,
VNodeProps
} from 'vue';
import EventEmitter from 'eventemitter3';
import {
BaseProps,
BezierProps,
CirclesProps,
CommentProps,
ConatinerCustomProps,
ContainerProps,
CustomProps,
EllipseProps,
ImageProps,
LineProps,
PathProps,
QuadraticProps,
RectProps,
RectRProps,
ShaderProps,
SpriteProps,
TextProps
} from './props';
import {
ERenderItemEvent,
RenderItem,
ESpriteEvent,
EContainerEvent,
EShaderEvent
} from '@motajs/render-core';
import {
EImageEvent,
ETextEvent,
EGraphicItemEvent
} from '@motajs/render-elements';
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];
};
type _Define<P extends BaseProps, E extends ERenderItemEvent> = DefineComponent<
P,
{},
{},
{},
{},
ComponentOptionsMixin,
ComponentOptionsMixin,
WrapEventEmitterEvents<E>,
Exclude<keyof WrapEventEmitterEvents<E>, number | symbol>,
VNodeProps,
Readonly<P & MappingEvent<E>>
>;
export type TagDefine<T extends object, E extends ERenderItemEvent> = T &
MappingEvent<E> &
ReservedProps;
declare module 'vue/jsx-runtime' {
namespace JSX {
export interface IntrinsicElements {
sprite: TagDefine<SpriteProps, ESpriteEvent>;
container: TagDefine<ContainerProps, EContainerEvent>;
'container-custom': TagDefine<
ConatinerCustomProps,
EContainerEvent
>;
shader: TagDefine<ShaderProps, EShaderEvent>;
text: TagDefine<TextProps, ETextEvent>;
image: TagDefine<ImageProps, EImageEvent>;
comment: TagDefine<CommentProps, ERenderItemEvent>;
custom: TagDefine<CustomProps, ERenderItemEvent>;
'g-rect': TagDefine<RectProps, EGraphicItemEvent>;
'g-circle': TagDefine<CirclesProps, EGraphicItemEvent>;
'g-ellipse': TagDefine<EllipseProps, EGraphicItemEvent>;
'g-line': TagDefine<LineProps, EGraphicItemEvent>;
'g-bezier': TagDefine<BezierProps, EGraphicItemEvent>;
'g-quad': TagDefine<QuadraticProps, EGraphicItemEvent>;
'g-path': TagDefine<PathProps, EGraphicItemEvent>;
'g-rectr': TagDefine<RectRProps, EGraphicItemEvent>;
}
}
}
export interface InstancedElementProp {
item: RenderItem;
}
export function wrapInstancedComponent<
P extends BaseProps = BaseProps,
E extends ERenderItemEvent = ERenderItemEvent,
C extends RenderItem = RenderItem
>(onCreate: (props: P) => C): _Define<P, E> {
const Com = defineComponent((props, ctx) => {
return () => {
const p = {
...props,
...ctx.attrs,
_item: onCreate
};
return h('custom', p, ctx.slots);
};
});
return Com as _Define<P, E>;
}
+14
View File
@@ -0,0 +1,14 @@
import { ERenderItemEvent } from '@motajs/render-core';
import { TagDefine } from './elements';
import { BaseProps } from './props';
export type DefaultProps<
P extends BaseProps = BaseProps,
E extends ERenderItemEvent = ERenderItemEvent
> = TagDefine<P, E>;
export * from './elements';
export * from './map';
export * from './props';
export * from './renderer';
export * from './use';
+204
View File
@@ -0,0 +1,204 @@
import { logger } from '@motajs/common';
import {
ERenderItemEvent,
RenderItem,
RenderItemPosition,
Container,
ContainerCustom,
MotaRenderer,
Sprite,
Shader
} from '@motajs/render-core';
import {
Comment,
ETextEvent,
Image,
Text,
BezierCurve,
Circle,
Ellipse,
Line,
Path,
QuadraticCurve,
Rect,
RectR
} from '@motajs/render-elements';
import { ElementNamespace, VNodeProps } from 'vue';
type OnItemCreate<
E extends ERenderItemEvent = ERenderItemEvent,
T extends RenderItem<E> = RenderItem<E>
> = (
namespace?: ElementNamespace,
isCustomizedBuiltIn?: string,
vnodeProps?: (VNodeProps & { [key: string]: any }) | null
) => T;
class RenderTagMap {
private map: Map<string, OnItemCreate> = new Map();
/**
* 注册一个标签,每个标签对应一类元素,重复注册会覆盖之前的
* @param tag 标签名称
* @param ele 对应的元素类或其构造器
*/
register<E extends ERenderItemEvent, T extends RenderItem<E>>(
tag: string,
onCreate: OnItemCreate<E, T>
) {
if (this.map.has(tag)) {
logger.warn(34, tag);
}
this.map.set(tag, onCreate);
}
/**
* 获取一个标签对应的元素构造器
* @param tag 标签名
*/
get<E extends ERenderItemEvent, T extends RenderItem<E>>(
tag: string
): OnItemCreate<E, T> | undefined {
return this.map.get(tag) as OnItemCreate<E, T>;
}
}
export const tagMap = new RenderTagMap();
export const standardElement = (
Item: new (
type: RenderItemPosition,
cache?: boolean,
fall?: boolean
) => RenderItem
) => {
return (_0: any, _1: any, props?: any) => {
if (!props) return new Item('static');
else {
const {
type = 'static',
cache = true,
fall = false,
nocache = false
} = props;
return new Item(type, cache && !nocache, fall);
}
};
};
export const standardElementNoCache = (
Item: new (
type: RenderItemPosition,
cache?: boolean,
fall?: boolean
) => RenderItem
) => {
return (_0: any, _1: any, props?: any) => {
if (!props) return new Item('static');
else {
const {
type = 'static',
cache = false,
fall = false,
nocache = true
} = props;
return new Item(type, cache && !nocache, fall);
}
};
};
const enum ElementState {
None = 0,
Cache = 1,
Fall = 2
}
/**
* standardElementFor
*/
const _se = (
Item: new (
type: RenderItemPosition,
cache?: boolean,
fall?: boolean
) => RenderItem,
position: RenderItemPosition,
state: ElementState
) => {
const defaultCache = !!(state & ElementState.Cache);
const defautFall = !!(state & ElementState.Fall);
return (_0: any, _1: any, props?: any) => {
if (!props) return new Item('absolute');
else {
const {
type = position,
cache = defaultCache,
fall = defautFall,
nocache = !defaultCache
} = props;
return new Item(type, cache && !nocache, fall);
}
};
};
// Default elements
tagMap.register('container', standardElement(Container));
tagMap.register('container-custom', standardElement(ContainerCustom));
tagMap.register('template', standardElement(Container));
tagMap.register('mota-renderer', (_0, _1, props) => {
return new MotaRenderer(props?.id);
});
tagMap.register('sprite', standardElement(Sprite));
tagMap.register<ETextEvent, Text>('text', (_0, _1, props) => {
if (!props) return new Text();
else {
const { type = 'static', text = '' } = props;
return new Text(text, type);
}
});
const emptyImage = document.createElement('canvas');
emptyImage.width = 1;
emptyImage.height = 1;
tagMap.register('image', (_0, _1, props) => {
if (!props) return new Image(emptyImage);
else {
const { image = emptyImage, type = 'static' } = props;
return new Image(image, type);
}
});
tagMap.register('comment', (_0, _1, props) => {
if (!props) return new Comment();
else {
const { text = '' } = props;
return new Comment(text);
}
});
tagMap.register('shader', (_0, _1, props) => {
if (!props) return new Shader();
else {
const { type = 'static' } = props;
return new Shader(type);
}
});
tagMap.register('custom', (_0, _1, props) => {
if (!props) {
logger.error(22);
throw new Error('Cannot create custom element.');
} else {
const item = props._item;
if (!item) {
logger.error(22);
throw new Error('Cannot create custom element.');
}
return item(props);
}
});
tagMap.register('g-rect', standardElementNoCache(Rect));
tagMap.register('g-circle', standardElementNoCache(Circle));
tagMap.register('g-ellipse', standardElementNoCache(Ellipse));
tagMap.register('g-line', standardElementNoCache(Line));
tagMap.register('g-bezier', standardElementNoCache(BezierCurve));
tagMap.register('g-quad', standardElementNoCache(QuadraticCurve));
tagMap.register('g-path', standardElementNoCache(Path));
tagMap.register('g-rectr', standardElementNoCache(RectR));
+225
View File
@@ -0,0 +1,225 @@
import {
RenderFunction,
RenderItem,
RenderItemPosition,
Transform,
ElementAnchor,
ElementLocator,
ElementScale,
CustomContainerRenderFn,
CustomContainerPropagateFn,
CanvasStyle
} from '@motajs/render-core';
import {
BezierParams,
CircleParams,
EllipseParams,
ILineProperty,
LineParams,
QuadParams,
RectRCircleParams,
RectREllipseParams
} from '@motajs/render-elements';
import { Font } from '@motajs/render-style';
export interface CustomProps {
_item: (props: BaseProps) => RenderItem;
}
export interface BaseProps {
/** 元素的横坐标 */
x?: number;
/** 元素的纵坐标 */
y?: number;
/** 元素的横向锚点位置 */
anchorX?: number;
/** 元素的纵向锚点位置 */
anchorY?: number;
/** 元素的纵深,值越大越靠上 */
zIndex?: number;
/** 元素的宽度 */
width?: number;
/** 元素的高度 */
height?: number;
/** 元素的滤镜 */
filter?: string;
/** 是否启用高清画布 */
hd?: 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锚点]`,
* 对于横坐标与纵坐标、宽度与高度、x锚点与y锚点,两两一组要么都填,要么都不填
* 是 x, y, width, height, anchorX, anchorY 的简写属性
*/
loc?: ElementLocator;
/** 锚点属性,可以填 `[x锚点,y锚点]`,是 anchorX, anchorY 的简写属性 */
anc?: ElementAnchor;
/** 放缩属性,可以填 `[x比例,y比例]`,是 transform 的简写属性之一 */
scale?: ElementScale;
/** 旋转属性,单位弧度,是 transform 的简写属性之一 */
rotate?: number;
/** 这个元素是否不会触发任何交互事件(cursor 属性也会无效),当执行到此元素时,会下穿至下一个元素 */
noevent?: boolean;
}
export interface SpriteProps extends BaseProps {
/** 自定义的渲染函数 */
render?: RenderFunction;
}
export interface ContainerProps extends BaseProps {}
export interface ConatinerCustomProps extends ContainerProps {
/** 自定义容器渲染函数 */
render?: CustomContainerRenderFn;
/** 自定义容器事件传递函数 */
propagate?: CustomContainerPropagateFn;
}
export interface GL2Props extends BaseProps {}
export interface ShaderProps extends BaseProps {}
export interface TextProps extends BaseProps {
/** 要渲染的文字 */
text?: string;
/** 文字的填充样式 */
fillStyle?: CanvasStyle;
/** 文字的描边样式 */
strokeStyle?: CanvasStyle;
/** 文字的字体 */
font?: Font;
/** 文字的描边粗细 */
strokeWidth?: number;
}
export interface ImageProps extends BaseProps {
/** 图片对象 */
image: CanvasImageSource;
}
export interface CommentProps extends BaseProps {
text?: string;
}
export interface GraphicPropsBase extends BaseProps, Partial<ILineProperty> {
/** 是否填充,若填写 {@link stroke},那么表现为先填充后描边 */
fill?: boolean;
/** 是否描边,若填写 {@link fill},那么表现为先填充后描边 */
stroke?: boolean;
/** 是否先描边后填充,优先级最高,若设置,则 {@link fill} 与 {@link stroke} 无效。 */
strokeAndFill?: boolean;
/** 填充原则,比如 `nonzero` 表示非零环绕原则,默认为奇偶环绕原则 `evenodd` */
fillRule?: CanvasFillRule;
/** 填充样式 */
fillStyle?: CanvasStyle;
/** 描边样式 */
strokeStyle?: CanvasStyle;
/** 在交互时,是否只检查交互位置只在描边上,对 fill, stroke, strokeAndFill 均有效 */
actionStroke?: boolean;
}
export interface RectProps extends GraphicPropsBase {}
export interface CirclesProps extends GraphicPropsBase {
radius?: number;
start?: number;
end?: number;
/**
* 圆属性参数,可以填 `[圆心 x 坐标,圆心 y 坐标,半径,起始角度,终止角度]`,是 x, y, radius, start, end 的简写,
* 其中半径可选,后两项要么都填,要么都不填
*/
circle?: CircleParams;
}
export interface EllipseProps extends GraphicPropsBase {
radiusX?: number;
radiusY?: number;
start?: number;
end?: number;
/**
* 椭圆属性参数,可以填 `[圆心 x 坐标,圆心 y 坐标,x半径,y半径,起始角度,终止角度]`,是 x, y, radiusX, radiusY, start, end 的简写,
* 其中每两项要么都填,要么都不填
*/
ellipse?: EllipseParams;
}
export interface LineProps extends GraphicPropsBase {
x1?: number;
y1?: number;
x2?: number;
y2?: number;
/** 直线属性参数,可以填 `[x1, y1, x2, y2]`,都是必填 */
line?: LineParams;
}
export interface BezierProps extends GraphicPropsBase {
sx?: number;
sy?: number;
cp1x?: number;
cp1y?: number;
cp2x?: number;
cp2y?: number;
ex?: number;
ey?: number;
/** 三次贝塞尔曲线参数,可以填 `[sx, sy, cp1x, cp1y, cp2x, cp2y, ex, ey]`,都是必填 */
curve?: BezierParams;
}
export interface QuadraticProps extends GraphicPropsBase {
sx?: number;
sy?: number;
cpx?: number;
cpy?: number;
ex?: number;
ey?: number;
/** 二次贝塞尔曲线参数,可以填 `[sx, sy, cpx, cpy, ex, ey]`,都是必填 */
curve?: QuadParams;
}
export interface PathProps extends GraphicPropsBase {
path?: Path2D;
}
export interface RectRProps extends GraphicPropsBase {
/**
* 圆形圆角参数,可以填 `[r1, r2, r3, r4]`,后三项可选。填写不同数量下的表现:
* - 1个:每个角都是 `r1` 半径的圆
* - 2个:左上和右下是 `r1` 半径的圆,右上和左下是 `r2` 半径的圆
* - 3个:左上是 `r1` 半径的圆,右上和左下是 `r2` 半径的圆,右下是 `r3` 半径的圆
* - 4个:左上、右上、左下、右下 分别是 `r1, r2, r3, r4` 半径的圆
*/
circle?: RectRCircleParams;
/**
* 椭圆圆角参数,可以填 `[rx1, ry1, rx2, ry2, rx3, ry3, rx4, ry4]`,
* 两两一组,后三组可选,填写不同数量下的表现:
* - 1组:每个角都是 `[rx1, ry1]` 半径的椭圆
* - 2组:左上和右下是 `[rx1, ry1]` 半径的椭圆,右上和左下是 `[rx2, ry2]` 半径的椭圆
* - 3组:左上是 `[rx1, ry1]` 半径的椭圆,右上和左下是 `[rx2, ey2]` 半径的椭圆,右下是 `[rx3, ry3]` 半径的椭圆
* - 4组:左上、右上、左下、右下 分别是 `[rx1, ry1], [rx2, ry2], [rx3, ry3], [rx4, ry4]` 半径的椭圆
*/
ellipse?: RectREllipseParams;
}
+98
View File
@@ -0,0 +1,98 @@
import { logger } from '@motajs/common';
import { ERenderItemEvent, RenderItem } from '@motajs/render-core';
import { ETextEvent, Text, Comment } from '@motajs/render-elements';
import {
ComponentInternalInstance,
createRenderer,
ElementNamespace,
VNodeProps
} from 'vue';
import { tagMap } from './map';
export const { createApp, render } = createRenderer<RenderItem, RenderItem>({
patchProp: function (
el: RenderItem,
key: string,
prevValue: any,
nextValue: any,
namespace?: ElementNamespace,
parentComponent?: ComponentInternalInstance | null
): void {
el.patchProp(key, prevValue, nextValue, namespace, parentComponent);
},
insert: function (
el: RenderItem<ERenderItemEvent>,
parent: RenderItem,
_anchor?: RenderItem<ERenderItemEvent> | null
): void {
parent.appendChild(el);
},
remove: function (el: RenderItem<ERenderItemEvent>): void {
el.destroy();
},
createElement: function (
type: string,
namespace?: ElementNamespace,
isCustomizedBuiltIn?: string,
vnodeProps?: (VNodeProps & { [key: string]: any }) | null
): RenderItem {
const onCreate = tagMap.get(type);
if (!onCreate) {
logger.error(20, type);
throw new Error(`Cannot create element '${type}'`);
}
return onCreate(namespace, isCustomizedBuiltIn, vnodeProps);
},
createText: function (text: string): RenderItem<ETextEvent> {
if (/^\s*$/.test(text)) {
return new Comment();
} else {
logger.warn(38);
}
return new Text(text);
},
createComment: function (text: string): RenderItem<ERenderItemEvent> {
return new Comment(text);
},
setText: function (node: RenderItem<ERenderItemEvent>, text: string): void {
if (node instanceof Text) {
node.setText(text);
} else {
logger.warn(39);
}
},
setElementText: function (node: RenderItem, text: string): void {
if (node instanceof Text) {
node.setText(text);
} else {
logger.warn(39);
}
},
parentNode: function (
node: RenderItem<ERenderItemEvent>
): RenderItem | null {
return node.parent ?? null;
},
nextSibling: function (
node: RenderItem<ERenderItemEvent>
): RenderItem<ERenderItemEvent> | null {
if (!node) return null;
if (!node.parent) {
return null;
} else {
const parent = node.parent;
const list = [...parent.children];
const index = list.indexOf(node);
return list[index] ?? null;
}
}
});
+54
View File
@@ -0,0 +1,54 @@
import { Animation, Ticker, Transition } from 'mutate-animate';
import { ERenderItemEvent, RenderItem } from '@motajs/render-core';
import { onMounted, onUnmounted } from 'vue';
import EventEmitter from 'eventemitter3';
const ticker = new Ticker();
/**
* 在组件中每帧执行一次函数
* @param fn 每帧执行的函数
*/
export function onTick(fn: (time: number) => void) {
onMounted(() => {
ticker.add(fn);
});
onUnmounted(() => {
ticker.remove(fn);
});
}
type AnimationUsing = [Animation];
type TransitionUsing = [Transition];
/**
* 在组件中创建一个动画实例
*/
export function useAnimation(): AnimationUsing {
const ani = new Animation();
onUnmounted(() => {
ani.ticker.destroy();
});
return [ani];
}
/**
* 在组件中创建一个渐变实例
*/
export function useTransition(): TransitionUsing {
const tran = new Transition();
onUnmounted(() => {
tran.ticker.destroy();
});
return [tran];
}
export function onEvent<
T extends ERenderItemEvent,
K extends EventEmitter.EventNames<T>
>(item: RenderItem<T>, key: K, listener: EventEmitter.EventListener<T, K>) {
item.on(key, listener);
onUnmounted(() => {
item.off(key, listener);
});
}