From 4a55255b747721b793daef327b309db55947337d Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Wed, 27 Nov 2024 21:19:46 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20graphics=E6=A1=86=E6=9E=B6=20&=20patchP?= =?UTF-8?q?rop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/render/index.tsx | 60 +----- src/core/render/preset/graphics.ts | 293 +++++++++++++++++++++----- src/core/render/preset/misc.ts | 14 ++ src/core/render/renderer/elements.tsx | 29 +-- src/core/render/renderer/map.ts | 65 ++++-- src/core/render/renderer/props.ts | 18 ++ vite.config.ts | 4 +- 7 files changed, 333 insertions(+), 150 deletions(-) diff --git a/src/core/render/index.tsx b/src/core/render/index.tsx index 0e4f7d5..d133803 100644 --- a/src/core/render/index.tsx +++ b/src/core/render/index.tsx @@ -2,7 +2,6 @@ import { FloorItemDetail } from '@/plugin/fx/itemDetail'; import { FloorDamageExtends } from './preset/damage'; import { LayerDoorAnimate } from './preset/floor'; import { HeroRenderer } from './preset/hero'; -import { LayerGroup, FloorLayer } from './preset/layer'; import { MotaRenderer } from './render'; import { LayerShadowExtends } from '../fx/shadow'; import { LayerGroupFilter } from '@/plugin/fx/gameCanvas'; @@ -10,15 +9,10 @@ import { LayerGroupAnimate } from './preset/animate'; import { LayerGroupPortal } from '@/plugin/fx/portal'; import { LayerGroupHalo } from '@/plugin/fx/halo'; import { FloorViewport } from './preset/viewport'; -import { Container } from './container'; import { PopText } from '@/plugin/fx/pop'; import { FloorChange } from '@/plugin/fallback'; -import { onTick, render } from './renderer'; -import { MotaOffscreenCanvas2D } from '../fx/canvas2d'; -import { SpriteComponent } from './renderer/elements'; -import { defineComponent, onActivated, onMounted, ref } from 'vue'; -import { Ticker } from 'mutate-animate'; -import { Sprite } from './sprite'; +import { render } from './renderer'; +import { defineComponent, ref } from 'vue'; let main: MotaRenderer; @@ -26,8 +20,6 @@ Mota.require('var', 'loading').once('coreInit', () => { main = new MotaRenderer(); const Com = defineComponent(props => { - const group = ref(); - return () => ( { > { main.hide(); render(, 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); }); diff --git a/src/core/render/preset/graphics.ts b/src/core/render/preset/graphics.ts index 050f6c2..bb5dbe2 100644 --- a/src/core/render/preset/graphics.ts +++ b/src/core/render/preset/graphics.ts @@ -1,82 +1,267 @@ import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d'; import { RenderItem } from '../item'; 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 { - /** 渲染方式,是描边还是填充 */ - mode: DrawType; +interface IGraphicProperty extends ILineProperty { + /** 渲染模式,可选 {@link GraphicMode.Fill}, {@link GraphicMode.Stroke}, {@link GraphicMode.All} */ + mode: GraphicMode; + /** 填充样式 */ + fill: CanvasStyle; + /** 描边样式 */ + stroke: CanvasStyle; +} + +export const enum GraphicMode { + /** 仅填充 */ + Fill = 1, + /** 仅描边 */ + Stroke = 2, + /** 填充+描边 */ + All = 3 } export class Graphics extends RenderItem { - protected render( - canvas: MotaOffscreenCanvas2D, - transform: Transform - ): void {} -} + /** 排序后的子元素 */ + sortedChildren: RenderItem[] = []; -export class Rect extends RenderItem implements IGraphicProperty { - mode: DrawType = 'fill'; - - rectX: number = 0; - rectY: number = 0; - rectWidth: number = 100; - rectHeight: number = 100; + /** 是否需要重排 */ + private needSort: boolean = false; protected render( canvas: MotaOffscreenCanvas2D, transform: Transform ): void { 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; - this.rectY = y; - this.update(); + /** + * 为这个Graphics元素添加子元素,要求只能是Graphics元素或基于GraphicItemBase的元素 + * @param child 要添加的子元素 + */ + appendChild(...child: RenderItem[]): 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[]): void {} + + /** + * 申请对子元素按照zIndex进行重排 + */ + requestSort(): void { + // 在下一帧渲染前进行排序 + if (!this.needSort) { + this.needSort = true; + this.requestBeforeFrame(() => {}); + } } } -export class Circle extends RenderItem implements IGraphicProperty { - mode: DrawType = 'fill'; - - protected render( - canvas: MotaOffscreenCanvas2D, - transform: Transform - ): void {} -} - -export class Ellipse extends RenderItem implements IGraphicProperty { - mode: DrawType = 'fill'; - - protected render( - canvas: MotaOffscreenCanvas2D, - transform: Transform - ): void {} -} - -export class Line extends RenderItem implements IGraphicProperty { - mode: DrawType = 'fill'; - - protected render( - canvas: MotaOffscreenCanvas2D, - transform: Transform - ): void {} -} - -export class Path extends RenderItem implements IGraphicProperty { - mode: DrawType = 'fill'; +export abstract class GraphicItemBase + extends RenderItem + implements IGraphicProperty +{ + mode: number = GraphicMode.Fill; + fill: CanvasStyle = '#fff'; + stroke: CanvasStyle = '#fff'; + lineWidth: number = 2; + lineDash?: number[] | undefined; + lineDashOffset?: number | undefined; + lineJoin: CanvasLineJoin = 'bevel'; + lineCap: CanvasLineCap = 'butt'; + miterLimit: number = 10; + + /** + * 设置描边绘制的信息 + * @param options 线的信息 + */ + setLineOption(options: Partial) {} + + /** + * 设置绘制样式 + * @param style 绘制样式 + */ + setStyle(style: CanvasStyle) {} + + /** + * 设置绘制模式,是描边还是填充 + * @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( 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); + } } diff --git a/src/core/render/preset/misc.ts b/src/core/render/preset/misc.ts index db5dd99..1b1536a 100644 --- a/src/core/render/preset/misc.ts +++ b/src/core/render/preset/misc.ts @@ -159,6 +159,20 @@ export class Image extends RenderItem { this.image = image; 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 { diff --git a/src/core/render/renderer/elements.tsx b/src/core/render/renderer/elements.tsx index 8cd7a3c..01a22b0 100644 --- a/src/core/render/renderer/elements.tsx +++ b/src/core/render/renderer/elements.tsx @@ -10,14 +10,22 @@ import EventEmitter from 'eventemitter3'; import { AnimateProps, BaseProps, + BezierProps, + CirclesProps, CommentProps, ContainerProps, CustomProps, DamageProps, + EllipseProps, GL2Props, + GraphicsProps, ImageProps, LayerGroupProps, LayerProps, + LineProps, + PathProps, + QuadraticProps, + RectProps, ShaderProps, SpriteProps, TextProps @@ -65,19 +73,6 @@ type TagDefine = T & MappingEvent & ReservedProps; -export type RenderItemComponent = _Define; -export type SpriteComponent = _Define; -export type ContainerComponent = _Define; -export type GL2Component = _Define; -export type ShaderComponent = _Define; -export type TextComponent = _Define; -export type ImageComponent = _Define; -export type CommentComponent = _Define; -export type LayerGroupComponent = _Define; -export type LayerComponent = _Define; -export type AnimateComponent = _Define; -export type DamageComponent = _Define; - declare module 'vue/jsx-runtime' { namespace JSX { export interface IntrinsicElements { @@ -92,6 +87,14 @@ declare module 'vue/jsx-runtime' { 'layer-group': TagDefine; damage: TagDefine; animate: TagDefine; + graphics: TagDefine; + 'g-rect': TagDefine; + 'g-circle': TagDefine; + 'g-ellipse': TagDefine; + 'g-line': TagDefine; + 'g-bezier': TagDefine; + 'g-quad': TagDefine; + 'g-path': TagDefine; } } } diff --git a/src/core/render/renderer/map.ts b/src/core/render/renderer/map.ts index e84c563..bebf601 100644 --- a/src/core/render/renderer/map.ts +++ b/src/core/render/renderer/map.ts @@ -1,5 +1,5 @@ import { logger } from '@/core/common/logger'; -import { ERenderItemEvent, RenderItem } from '../item'; +import { ERenderItemEvent, RenderItem, RenderItemPosition } from '../item'; import { ElementNamespace, VNodeProps } from 'vue'; import { Container } from '../container'; import { MotaRenderer } from '../render'; @@ -7,6 +7,17 @@ import { Sprite } from '../sprite'; import { Comment, Image, Text } from '../preset/misc'; import { Shader } from '../shader'; 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< E extends ERenderItemEvent = ERenderItemEvent, @@ -48,32 +59,32 @@ class 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 -tagMap.register('container', (_0, _1, props) => { - if (!props) return new Container(); - else { - const { - type = 'static', - enableCache = true, - fallthrough = false - } = props; - return new Container(type, enableCache, fallthrough); - } -}); +tagMap.register('container', standardElement(Container)); tagMap.register('mota-renderer', (_0, _1, props) => { return new MotaRenderer(props?.id); }); -tagMap.register('sprite', (_0, _1, props) => { - if (!props) return new Sprite(); - else { - const { - type = 'static', - enableCache = true, - fallthrough = false - } = props; - return new Sprite(type, enableCache, fallthrough); - } -}); +tagMap.register('sprite', standardElement(Sprite)); tagMap.register('text', (_0, _1, props) => { if (!props) return new Text(); else { @@ -159,3 +170,11 @@ tagMap.register('damage', (_0, _1, props) => { tagMap.register('animate', (_0, _1, props) => { 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)); diff --git a/src/core/render/renderer/props.ts b/src/core/render/renderer/props.ts index 0f89eae..240a912 100644 --- a/src/core/render/renderer/props.ts +++ b/src/core/render/renderer/props.ts @@ -25,6 +25,8 @@ export interface BaseProps { hidden?: boolean; transform?: Transform; type?: RenderItemPosition; + enableCache?: boolean; + fallthrough?: boolean; id?: string; alpha?: number; composite?: GlobalCompositeOperation; @@ -87,3 +89,19 @@ export interface DamageProps extends BaseProps { strokeStyle?: string; 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 {} diff --git a/vite.config.ts b/vite.config.ts index 4e01706..c2b721c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -11,7 +11,7 @@ const FSHOST = 'http://127.0.0.1:3000/'; const custom = [ 'container', 'image', 'sprite', 'shader', 'text', 'comment', 'custom', - 'layer', 'layer-group', 'animate', 'damage' + 'layer', 'layer-group', 'animate', 'damage', 'graphics' ] // https://vitejs.dev/config/ @@ -22,7 +22,7 @@ export default defineConfig({ }), vuejsx({ isCustomElement: (tag) => { - return custom.includes(tag) + return custom.includes(tag) || tag.startsWith('g-'); } }), legacy({