feat: graphics框架 & patchProp

This commit is contained in:
unanmed 2024-11-27 21:19:46 +08:00
parent ac68d64f5f
commit 4a55255b74
7 changed files with 333 additions and 150 deletions

View File

@ -2,7 +2,6 @@ import { FloorItemDetail } from '@/plugin/fx/itemDetail';
import { FloorDamageExtends } from './preset/damage'; import { FloorDamageExtends } from './preset/damage';
import { LayerDoorAnimate } from './preset/floor'; import { LayerDoorAnimate } from './preset/floor';
import { HeroRenderer } from './preset/hero'; import { HeroRenderer } from './preset/hero';
import { LayerGroup, FloorLayer } from './preset/layer';
import { MotaRenderer } from './render'; import { MotaRenderer } from './render';
import { LayerShadowExtends } from '../fx/shadow'; import { LayerShadowExtends } from '../fx/shadow';
import { LayerGroupFilter } from '@/plugin/fx/gameCanvas'; import { LayerGroupFilter } from '@/plugin/fx/gameCanvas';
@ -10,15 +9,10 @@ import { LayerGroupAnimate } from './preset/animate';
import { LayerGroupPortal } from '@/plugin/fx/portal'; import { LayerGroupPortal } from '@/plugin/fx/portal';
import { LayerGroupHalo } from '@/plugin/fx/halo'; import { LayerGroupHalo } from '@/plugin/fx/halo';
import { FloorViewport } from './preset/viewport'; import { FloorViewport } from './preset/viewport';
import { Container } from './container';
import { PopText } from '@/plugin/fx/pop'; import { PopText } from '@/plugin/fx/pop';
import { FloorChange } from '@/plugin/fallback'; import { FloorChange } from '@/plugin/fallback';
import { onTick, render } from './renderer'; import { render } from './renderer';
import { MotaOffscreenCanvas2D } from '../fx/canvas2d'; import { defineComponent, ref } from 'vue';
import { SpriteComponent } from './renderer/elements';
import { defineComponent, onActivated, onMounted, ref } from 'vue';
import { Ticker } from 'mutate-animate';
import { Sprite } from './sprite';
let main: MotaRenderer; let main: MotaRenderer;
@ -26,8 +20,6 @@ Mota.require('var', 'loading').once('coreInit', () => {
main = new MotaRenderer(); main = new MotaRenderer();
const Com = defineComponent(props => { const Com = defineComponent(props => {
const group = ref<LayerGroup>();
return () => ( return () => (
<container <container
id="map-draw" id="map-draw"
@ -38,7 +30,6 @@ Mota.require('var', 'loading').once('coreInit', () => {
> >
<layer-group <layer-group
id="layer-main" id="layer-main"
ref={group}
ex={[ ex={[
new FloorDamageExtends(), new FloorDamageExtends(),
new FloorItemDetail(), new FloorItemDetail(),
@ -72,53 +63,6 @@ Mota.require('var', 'loading').once('coreInit', () => {
main.hide(); main.hide();
render(<Com></Com>, main); render(<Com></Com>, main);
// const mapDraw = new Container();
// const layer = new LayerGroup();
// const pop = new PopText('static');
// const floorChange = new FloorChange('static');
// mapDraw.id = 'map-draw';
// layer.id = 'layer-main';
// pop.id = 'pop-main';
// floorChange.id = 'floor-change';
// mapDraw.setHD(true);
// mapDraw.setAntiAliasing(false);
// mapDraw.size(core._PX_, core._PY_);
// floorChange.size(480, 480);
// floorChange.setHD(true);
// floorChange.setZIndex(50);
// floorChange.setTips(tips);
// pop.setZIndex(80);
// ['bg', 'bg2', 'event', 'fg', 'fg2'].forEach(v => {
// layer.addLayer(v as FloorLayer);
// });
// const damage = new FloorDamageExtends();
// const hero = new HeroRenderer();
// const detail = new FloorItemDetail();
// const door = new LayerDoorAnimate();
// const shadow = new LayerShadowExtends();
// const filter = new LayerGroupFilter();
// const animate = new LayerGroupAnimate();
// const portal = new LayerGroupPortal();
// const halo = new LayerGroupHalo();
// const viewport = new FloorViewport();
// layer.extends(damage);
// layer.extends(detail);
// layer.extends(filter);
// layer.extends(portal);
// layer.extends(halo);
// layer.getLayer('event')?.extends(hero);
// layer.getLayer('event')?.extends(door);
// layer.getLayer('event')?.extends(shadow);
// layer.extends(animate);
// layer.extends(viewport);
// main.appendChild(mapDraw);
// mapDraw.appendChild(layer);
// layer.appendChild(pop);
// mapDraw.appendChild(floorChange);
console.log(main); console.log(main);
}); });

View File

@ -1,82 +1,267 @@
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d'; import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
import { RenderItem } from '../item'; import { RenderItem } from '../item';
import { Transform } from '../transform'; import { Transform } from '../transform';
import { ElementNamespace, ComponentInternalInstance } from 'vue';
type DrawType = 'fill' | 'stroke'; interface ILineProperty {
/** 线宽 */
lineWidth: number;
/** 线的虚线设置 */
lineDash?: number[];
/** 虚线偏移量 */
lineDashOffset?: number;
/** 线的连接样式 */
lineJoin: CanvasLineJoin;
/** 线的顶端样式 */
lineCap: CanvasLineCap;
/** 线的斜接限制当连接为miter类型时可填默认为10 */
miterLimit: number;
}
interface IGraphicProperty { interface IGraphicProperty extends ILineProperty {
/** 渲染方式,是描边还是填充 */ /** 渲染模式,可选 {@link GraphicMode.Fill}, {@link GraphicMode.Stroke}, {@link GraphicMode.All} */
mode: DrawType; mode: GraphicMode;
/** 填充样式 */
fill: CanvasStyle;
/** 描边样式 */
stroke: CanvasStyle;
}
export const enum GraphicMode {
/** 仅填充 */
Fill = 1,
/** 仅描边 */
Stroke = 2,
/** 填充+描边 */
All = 3
} }
export class Graphics extends RenderItem { export class Graphics extends RenderItem {
protected render( /** 排序后的子元素 */
canvas: MotaOffscreenCanvas2D, sortedChildren: RenderItem[] = [];
transform: Transform
): void {}
}
export class Rect extends RenderItem implements IGraphicProperty { /** 是否需要重排 */
mode: DrawType = 'fill'; private needSort: boolean = false;
rectX: number = 0;
rectY: number = 0;
rectWidth: number = 100;
rectHeight: number = 100;
protected render( protected render(
canvas: MotaOffscreenCanvas2D, canvas: MotaOffscreenCanvas2D,
transform: Transform transform: Transform
): void { ): void {
const ctx = canvas.ctx; const ctx = canvas.ctx;
ctx.rect(this.rectX, this.rectY, this.rectWidth, this.rectHeight); this.sortedChildren.forEach(v => {
ctx.save();
v.renderContent(canvas, transform);
ctx.restore();
});
} }
setPos(x: number, y: number) { /**
this.rectX = x; * Graphics元素添加子元素Graphics元素或基于GraphicItemBase的元素
this.rectY = y; * @param child
this.update(); */
appendChild(...child: RenderItem<any>[]): void {
child.forEach(v => {
// 如果是Graphics或GraphicItemBase实例则加入子元素列表否则抛出警告并忽略本次添加
if (v instanceof Graphics || v instanceof GraphicItemBase) {
} else {
}
});
} }
setSize(w: number, h: number) { /**
this.rectWidth = w; *
this.rectHeight = h; */
this.update(); removeChild(...child: RenderItem<any>[]): void {}
/**
* zIndex进行重排
*/
requestSort(): void {
// 在下一帧渲染前进行排序
if (!this.needSort) {
this.needSort = true;
this.requestBeforeFrame(() => {});
}
} }
} }
export class Circle extends RenderItem implements IGraphicProperty { export abstract class GraphicItemBase
mode: DrawType = 'fill'; extends RenderItem
implements IGraphicProperty
protected render( {
canvas: MotaOffscreenCanvas2D, mode: number = GraphicMode.Fill;
transform: Transform fill: CanvasStyle = '#fff';
): void {} stroke: CanvasStyle = '#fff';
} lineWidth: number = 2;
lineDash?: number[] | undefined;
export class Ellipse extends RenderItem implements IGraphicProperty { lineDashOffset?: number | undefined;
mode: DrawType = 'fill'; lineJoin: CanvasLineJoin = 'bevel';
lineCap: CanvasLineCap = 'butt';
protected render( miterLimit: number = 10;
canvas: MotaOffscreenCanvas2D,
transform: Transform /**
): void {} *
} * @param options 线
*/
export class Line extends RenderItem implements IGraphicProperty { setLineOption(options: Partial<ILineProperty>) {}
mode: DrawType = 'fill';
/**
protected render( *
canvas: MotaOffscreenCanvas2D, * @param style
transform: Transform */
): void {} setStyle(style: CanvasStyle) {}
}
/**
export class Path extends RenderItem implements IGraphicProperty { *
mode: DrawType = 'fill'; * @param mode
*/
setMode(mode: GraphicMode) {}
patchProp(
key: string,
prevValue: any,
nextValue: any,
namespace?: ElementNamespace,
parentComponent?: ComponentInternalInstance | null
): void {
switch (key) {
}
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
}
}
export class Rect extends GraphicItemBase {
protected render(
canvas: MotaOffscreenCanvas2D,
transform: Transform
): void {}
patchProp(
key: string,
prevValue: any,
nextValue: any,
namespace?: ElementNamespace,
parentComponent?: ComponentInternalInstance | null
): void {
switch (key) {
}
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
}
}
export class Circle extends GraphicItemBase {
protected render(
canvas: MotaOffscreenCanvas2D,
transform: Transform
): void {}
patchProp(
key: string,
prevValue: any,
nextValue: any,
namespace?: ElementNamespace,
parentComponent?: ComponentInternalInstance | null
): void {
switch (key) {
}
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
}
}
export class Ellipse extends GraphicItemBase {
protected render(
canvas: MotaOffscreenCanvas2D,
transform: Transform
): void {}
patchProp(
key: string,
prevValue: any,
nextValue: any,
namespace?: ElementNamespace,
parentComponent?: ComponentInternalInstance | null
): void {
switch (key) {
}
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
}
}
export class Line extends GraphicItemBase {
protected render(
canvas: MotaOffscreenCanvas2D,
transform: Transform
): void {}
patchProp(
key: string,
prevValue: any,
nextValue: any,
namespace?: ElementNamespace,
parentComponent?: ComponentInternalInstance | null
): void {
switch (key) {
}
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
}
}
export class BezierCurve extends GraphicItemBase {
protected render(
canvas: MotaOffscreenCanvas2D,
transform: Transform
): void {}
patchProp(
key: string,
prevValue: any,
nextValue: any,
namespace?: ElementNamespace,
parentComponent?: ComponentInternalInstance | null
): void {
switch (key) {
}
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
}
}
export class QuadraticCurve extends GraphicItemBase {
protected render(
canvas: MotaOffscreenCanvas2D,
transform: Transform
): void {}
patchProp(
key: string,
prevValue: any,
nextValue: any,
namespace?: ElementNamespace,
parentComponent?: ComponentInternalInstance | null
): void {
switch (key) {
}
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
}
}
export class Path extends GraphicItemBase {
/** 路径 */
path: Path2D = new Path2D();
protected render( protected render(
canvas: MotaOffscreenCanvas2D, canvas: MotaOffscreenCanvas2D,
transform: Transform transform: Transform
): void {} ): void {}
patchProp(
key: string,
prevValue: any,
nextValue: any,
namespace?: ElementNamespace,
parentComponent?: ComponentInternalInstance | null
): void {
switch (key) {
}
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
}
} }

View File

@ -159,6 +159,20 @@ export class Image extends RenderItem<EImageEvent> {
this.image = image; this.image = image;
this.update(); this.update();
} }
patchProp(
key: string,
prevValue: any,
nextValue: any,
namespace?: ElementNamespace,
parentComponent?: ComponentInternalInstance | null
): void {
switch (key) {
case 'image':
this.setImage(nextValue);
return;
}
}
} }
export class Comment extends RenderItem { export class Comment extends RenderItem {

View File

@ -10,14 +10,22 @@ import EventEmitter from 'eventemitter3';
import { import {
AnimateProps, AnimateProps,
BaseProps, BaseProps,
BezierProps,
CirclesProps,
CommentProps, CommentProps,
ContainerProps, ContainerProps,
CustomProps, CustomProps,
DamageProps, DamageProps,
EllipseProps,
GL2Props, GL2Props,
GraphicsProps,
ImageProps, ImageProps,
LayerGroupProps, LayerGroupProps,
LayerProps, LayerProps,
LineProps,
PathProps,
QuadraticProps,
RectProps,
ShaderProps, ShaderProps,
SpriteProps, SpriteProps,
TextProps TextProps
@ -65,19 +73,6 @@ type TagDefine<T extends object, E extends ERenderItemEvent> = T &
MappingEvent<E> & MappingEvent<E> &
ReservedProps; ReservedProps;
export type RenderItemComponent = _Define<BaseProps, ERenderItemEvent>;
export type SpriteComponent = _Define<SpriteProps, ESpriteEvent>;
export type ContainerComponent = _Define<ContainerProps, EContainerEvent>;
export type GL2Component = _Define<GL2Props, EGL2Event>;
export type ShaderComponent = _Define<ShaderProps, EShaderEvent>;
export type TextComponent = _Define<TextProps, ETextEvent>;
export type ImageComponent = _Define<ImageProps, EImageEvent>;
export type CommentComponent = _Define<CommentProps, ERenderItemEvent>;
export type LayerGroupComponent = _Define<LayerGroupProps, ELayerGroupEvent>;
export type LayerComponent = _Define<LayerProps, ELayerEvent>;
export type AnimateComponent = _Define<AnimateProps, EAnimateEvent>;
export type DamageComponent = _Define<DamageProps, EDamageEvent>;
declare module 'vue/jsx-runtime' { declare module 'vue/jsx-runtime' {
namespace JSX { namespace JSX {
export interface IntrinsicElements { export interface IntrinsicElements {
@ -92,6 +87,14 @@ declare module 'vue/jsx-runtime' {
'layer-group': TagDefine<LayerGroupProps, ELayerGroupEvent>; 'layer-group': TagDefine<LayerGroupProps, ELayerGroupEvent>;
damage: TagDefine<DamageProps, EDamageEvent>; damage: TagDefine<DamageProps, EDamageEvent>;
animate: TagDefine<AnimateProps, EAnimateEvent>; animate: TagDefine<AnimateProps, EAnimateEvent>;
graphics: TagDefine<GraphicsProps, 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>;
} }
} }
} }

View File

@ -1,5 +1,5 @@
import { logger } from '@/core/common/logger'; import { logger } from '@/core/common/logger';
import { ERenderItemEvent, RenderItem } from '../item'; import { ERenderItemEvent, RenderItem, RenderItemPosition } from '../item';
import { ElementNamespace, VNodeProps } from 'vue'; import { ElementNamespace, VNodeProps } from 'vue';
import { Container } from '../container'; import { Container } from '../container';
import { MotaRenderer } from '../render'; import { MotaRenderer } from '../render';
@ -7,6 +7,17 @@ import { Sprite } from '../sprite';
import { Comment, Image, Text } from '../preset/misc'; import { Comment, Image, Text } from '../preset/misc';
import { Shader } from '../shader'; import { Shader } from '../shader';
import { Animate, Damage, EDamageEvent, Layer, LayerGroup } from '../preset'; import { Animate, Damage, EDamageEvent, Layer, LayerGroup } from '../preset';
import {
BezierCurve,
Circle,
Ellipse,
Graphics,
Line,
Path,
QuadraticCurve,
Rect
} from '../preset/graphics';
import { BaseProps } from './props';
type OnItemCreate< type OnItemCreate<
E extends ERenderItemEvent = ERenderItemEvent, E extends ERenderItemEvent = ERenderItemEvent,
@ -48,32 +59,32 @@ class RenderTagMap {
export const tagMap = new RenderTagMap(); export const tagMap = new RenderTagMap();
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',
enableCache = true,
fallthrough = false
} = props;
return new Item(type, enableCache, fallthrough);
}
};
};
// Default elements // Default elements
tagMap.register('container', (_0, _1, props) => { tagMap.register('container', standardElement(Container));
if (!props) return new Container();
else {
const {
type = 'static',
enableCache = true,
fallthrough = false
} = props;
return new Container(type, enableCache, fallthrough);
}
});
tagMap.register('mota-renderer', (_0, _1, props) => { tagMap.register('mota-renderer', (_0, _1, props) => {
return new MotaRenderer(props?.id); return new MotaRenderer(props?.id);
}); });
tagMap.register('sprite', (_0, _1, props) => { tagMap.register('sprite', standardElement(Sprite));
if (!props) return new Sprite();
else {
const {
type = 'static',
enableCache = true,
fallthrough = false
} = props;
return new Sprite(type, enableCache, fallthrough);
}
});
tagMap.register('text', (_0, _1, props) => { tagMap.register('text', (_0, _1, props) => {
if (!props) return new Text(); if (!props) return new Text();
else { else {
@ -159,3 +170,11 @@ tagMap.register<EDamageEvent, Damage>('damage', (_0, _1, props) => {
tagMap.register('animate', (_0, _1, props) => { tagMap.register('animate', (_0, _1, props) => {
return new Animate(); return new Animate();
}); });
tagMap.register('graphics', standardElement(Graphics));
tagMap.register('g-rect', standardElement(Rect));
tagMap.register('g-circle', standardElement(Circle));
tagMap.register('g-ellipse', standardElement(Ellipse));
tagMap.register('g-line', standardElement(Line));
tagMap.register('g-bezier', standardElement(BezierCurve));
tagMap.register('g-quad', standardElement(QuadraticCurve));
tagMap.register('g-path', standardElement(Path));

View File

@ -25,6 +25,8 @@ export interface BaseProps {
hidden?: boolean; hidden?: boolean;
transform?: Transform; transform?: Transform;
type?: RenderItemPosition; type?: RenderItemPosition;
enableCache?: boolean;
fallthrough?: boolean;
id?: string; id?: string;
alpha?: number; alpha?: number;
composite?: GlobalCompositeOperation; composite?: GlobalCompositeOperation;
@ -87,3 +89,19 @@ export interface DamageProps extends BaseProps {
strokeStyle?: string; strokeStyle?: string;
strokeWidth?: number; strokeWidth?: number;
} }
export interface GraphicsProps extends BaseProps {}
export interface RectProps extends BaseProps {}
export interface CirclesProps extends BaseProps {}
export interface EllipseProps extends BaseProps {}
export interface LineProps extends BaseProps {}
export interface BezierProps extends BaseProps {}
export interface QuadraticProps extends BaseProps {}
export interface PathProps extends BaseProps {}

View File

@ -11,7 +11,7 @@ const FSHOST = 'http://127.0.0.1:3000/';
const custom = [ const custom = [
'container', 'image', 'sprite', 'shader', 'text', 'comment', 'custom', 'container', 'image', 'sprite', 'shader', 'text', 'comment', 'custom',
'layer', 'layer-group', 'animate', 'damage' 'layer', 'layer-group', 'animate', 'damage', 'graphics'
] ]
// https://vitejs.dev/config/ // https://vitejs.dev/config/
@ -22,7 +22,7 @@ export default defineConfig({
}), }),
vuejsx({ vuejsx({
isCustomElement: (tag) => { isCustomElement: (tag) => {
return custom.includes(tag) return custom.includes(tag) || tag.startsWith('g-');
} }
}), }),
legacy({ legacy({