diff --git a/src/core/render/preset/misc.ts b/src/core/render/preset/misc.ts index 4310b14..7fa69fd 100644 --- a/src/core/render/preset/misc.ts +++ b/src/core/render/preset/misc.ts @@ -1,9 +1,18 @@ import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d'; import { Sprite } from '../sprite'; -import { ERenderItemEvent, RenderItem, RenderItemPosition } from '../item'; +import { + ERenderItemEvent, + IAnimateFrame, + renderEmits, + 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'; +import { logger } from '@/core/common/logger'; type CanvasStyle = string | CanvasGradient | CanvasPattern; @@ -207,7 +216,7 @@ export class Comment extends RenderItem { export interface EIconEvent extends ERenderItemEvent {} -export class Icon extends RenderItem { +export class Icon extends RenderItem implements IAnimateFrame { /** 图标id */ icon: AllNumbers = 0; /** 帧数 */ @@ -220,13 +229,69 @@ export class Icon extends RenderItem { protected render( canvas: MotaOffscreenCanvas2D, transform: Transform - ): void {} + ): void { + const ctx = canvas.ctx; + const renderable = this.renderable; + if (!renderable) return; + const [x, y, w, h] = renderable.render[0]; + const cw = canvas.width; + const ch = canvas.height; + const frame = this.animate + ? RenderItem.animatedFrame % renderable.frame + : 0; + if (this.animate) { + if (renderable.autotile) { + ctx.drawImage(renderable.image[0], x, y, w, h, 0, 0, cw, ch); + } else { + ctx.drawImage(renderable.image, x, y, w, h, 0, 0, cw, ch); + } + } else { + const [x1, y1, w1, h1] = renderable.render[frame]; + if (renderable.autotile) { + ctx.drawImage( + renderable.image[0], + x1, + y1, + w1, + h1, + 0, + 0, + cw, + ch + ); + } else { + ctx.drawImage(renderable.image, x1, y1, w1, h1, 0, 0, cw, ch); + } + this.update(this), renderEmits.addFramer(this); + } + } /** * 设置图标 * @param id 图标id */ - setIcon(id: AllIds | AllNumbers) {} + setIcon(id: AllIds | AllNumbers) { + const num = typeof id === 'number' ? id : texture.idNumberMap[id]; + const renderable = texture.getRenderable(num); + + if (!renderable) { + //todo: logger.warn() + return; + } else { + this.icon = num; + renderable.animate = 0; + this.renderable = renderable; + this.frame = renderable.frame; + } + this.update(); + } + + /** + * 更新动画帧 + */ + updateFrameAnimate(): void { + this.update(this); + } patchProp( key: string, @@ -242,10 +307,12 @@ export class Icon extends RenderItem { case 'animate': if (!this.assertType(nextValue, 'boolean', key)) return; this.animate = nextValue; + this.update(); return; case 'frame': if (!this.assertType(nextValue, 'number', key)) return; this.frame = nextValue; + this.update(); return; } super.patchProp(key, prevValue, nextValue, namespace, parentComponent); @@ -270,19 +337,162 @@ export class Winskin extends RenderItem { protected render( canvas: MotaOffscreenCanvas2D, transform: Transform - ): void {} + ): void { + const ctx = canvas.ctx; + const img = this.image; + const x = 0; + const y = 0; + const w = canvas.width; + const h = canvas.height; + const sz = this.borderSize / 32; + ctx.drawImage(img, 128, 0, 16, 16, x, y, 16 * sz, 16 * sz); + for (var dx = 0; dx < w - 64 * sz; dx += 32 * sz) { + ctx.drawImage( + img, + 144, + 0, + 32, + 16, + x + dx + 16, + y, + 32 * sz, + 16 * sz + ); + ctx.drawImage( + img, + 144, + 48, + 32, + 16, + x + dx + 16, + y + h - 16 * sz, + 32 * sz, + 16 * sz + ); + } + ctx.drawImage( + img, + 144, + 0, + w - dx - 32, + 16, + x + dx + 16 * sz, + y, + w - dx - 32 * sz, + 16 * sz + ); + ctx.drawImage( + img, + 144, + 48, + w - dx - 32, + 16, + x + dx + 16 * sz, + y + h - 16 * sz, + w - dx - 32 * sz, + 16 * sz + ); + ctx.drawImage( + img, + 176, + 0, + 16, + 16, + x + w - 16 * sz, + y, + 16 * sz, + 16 * sz + ); + // 左右 + for (var dy = 0; dy < h - 64 * sz; dy += 32 * sz) { + ctx.drawImage( + img, + 128, + 16, + 16, + 32, + x, + y + dy + 16 * sz, + 16 * sz, + 32 * sz + ); + ctx.drawImage( + img, + 176, + 16, + 16, + 32, + x + w - 16 * sz, + y + dy + 16 * sz, + 16 * sz, + 32 * sz + ); + } + ctx.drawImage( + img, + 128, + 16, + 16, + h - dy - 32, + x, + y + dy + 16 * sz, + 16 * sz, + h - dy - 32 * sz + ); + ctx.drawImage( + img, + 176, + 16, + 16, + h - dy - 32, + x + w - 16 * sz, + y + dy + 16 * sz, + 16 * sz, + h - dy - 32 * sz + ); + // 下方 + ctx.drawImage( + img, + 128, + 48, + 16, + 16, + x, + y + h - 16 * sz, + 16 * sz, + 16 * sz + ); + ctx.drawImage( + img, + 176, + 48, + 16, + 16, + x + w - 16 * sz, + y + h - 16 * sz, + 16 * sz, + 16 * sz + ); + this.update(); + } /** * 设置winskin图片 * @param image winskin图片 */ - setImage(image: SizedCanvasImageSource) {} + setImage(image: SizedCanvasImageSource) { + this.image = image; + this.update(); + } /** * 设置边框大小 * @param size 边框大小 */ - setBorderSize(size: number) {} + setBorderSize(size: number) { + this.borderSize = size; + this.update(); + } patchProp( key: string, @@ -292,6 +502,13 @@ export class Winskin extends RenderItem { parentComponent?: ComponentInternalInstance | null ): void { switch (key) { + case 'image': + this.setImage(nextValue); + return; + case 'borderSize': + if (!this.assertType(nextValue, 'number', key)) return; + this.setBorderSize(nextValue); + return; } super.patchProp(key, prevValue, nextValue, namespace, parentComponent); } diff --git a/src/core/render/renderer/props.ts b/src/core/render/renderer/props.ts index ddc9cfc..71a55dc 100644 --- a/src/core/render/renderer/props.ts +++ b/src/core/render/renderer/props.ts @@ -7,6 +7,7 @@ import { } from '../preset/layer'; import type { EnemyCollection } from '@/game/enemy/damage'; import { ILineProperty } from '../preset/graphics'; +import { SizedCanvasImageSource } from '../preset'; export interface CustomProps { _item: (props: BaseProps) => RenderItem; @@ -152,6 +153,13 @@ export interface PathProps extends GraphicPropsBase { path?: Path2D; } -export interface IconProps extends BaseProps {} +export interface IconProps extends BaseProps { + icon: AllNumbers; + frame: number; + animate: boolean; +} -export interface WinskinProps extends BaseProps {} +export interface WinskinProps extends BaseProps { + image: SizedCanvasImageSource; + borderSize: number; +}