HumanBreak/src/core/render/item.ts

201 lines
5.2 KiB
TypeScript
Raw Normal View History

2024-05-09 23:49:53 +08:00
import { isNil } from 'lodash-es';
import { EmitableEvent, EventEmitter } from '../common/eventEmitter';
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
import { Camera } from './camera';
export type RenderFunction = (
canvas: MotaOffscreenCanvas2D,
camera: Camera
) => void;
export type RenderItemPosition = 'absolute' | 'static';
interface IRenderCache {
/** 缓存列表 */
cacheList: Map<string, MotaOffscreenCanvas2D>;
/** 当前正在使用的缓存 */
using?: string;
/** 下次绘制需要写入的缓存 */
writing?: string;
/**
* 使使
* @param index 使
*/
useCache(index?: string): void;
/**
* 使
* @param index
*/
cache(index?: string): void;
/**
*
* @param index
*/
clearCache(index?: string): void;
}
export interface IRenderUpdater {
/**
*
* @param item
*/
update(item?: RenderItem): void;
}
export interface ICanvasCachedRenderItem {
/** 离屏画布首先渲染到它上面然后由Renderer渲染到最终画布上 */
canvas: MotaOffscreenCanvas2D;
}
interface IRenderAnchor {
anchorX: number;
anchorY: number;
/**
*
* @param x 01
* @param y 01
*/
setAnchor(x: number, y: number): void;
}
interface RenderItemEvent extends EmitableEvent {
beforeUpdate: (item?: RenderItem) => void;
afterUpdate: (item?: RenderItem) => void;
}
export abstract class RenderItem
extends EventEmitter<RenderItemEvent>
implements IRenderCache, IRenderUpdater, IRenderAnchor
{
zIndex: number = 0;
x: number = 0;
y: number = 0;
width: number = 200;
height: number = 200;
cacheList: Map<string, MotaOffscreenCanvas2D> = new Map();
using?: string;
writing?: string;
anchorX: number = 0;
anchorY: number = 0;
/** 渲染模式absolute表示绝对位置static表示跟随摄像机移动只对顶层元素有效 */
type: 'absolute' | 'static' = 'static';
parent?: RenderItem;
constructor() {
super();
this.using = '@default';
}
/**
*
* @param canvas
* @param ctx
* @param camera 使
*/
abstract render(
canvas: HTMLCanvasElement,
ctx: CanvasRenderingContext2D,
camera: Camera
): void;
/**
*
*/
abstract size(width: number, height: number): void;
/**
*
*/
abstract pos(x: number, y: number): void;
useCache(index?: string): void {
if (isNil(index)) {
this.using = void 0;
return;
}
if (!this.cacheList.has(index)) {
this.writing = index;
}
this.using = index;
}
cache(index?: string): void {
this.writing = index;
this.using = index;
}
clearCache(index?: string): void {
if (isNil(index)) {
this.writing = void 0;
this.using = void 0;
this.cacheList.clear();
} else {
this.cacheList.delete(index);
}
}
setAnchor(x: number, y: number): void {
this.anchorX = x;
this.anchorY = y;
}
update(item?: RenderItem): void {
this.writing = this.using;
this.using = void 0;
this.parent?.update(item);
}
}
export function withCacheRender(
item: RenderItem & ICanvasCachedRenderItem,
canvas: HTMLCanvasElement,
ctx: CanvasRenderingContext2D,
camera: Camera,
fn: RenderFunction
) {
const { width, height } = item;
const ax = width * item.anchorX;
const ay = height * item.anchorY;
let write = item.writing;
if (!isNil(item.using)) {
const cache = item.cacheList.get(item.using);
if (cache) {
ctx.drawImage(
cache.canvas,
item.x - ax,
item.y - ay,
item.width,
item.height
);
return;
}
write = item.using;
}
const { canvas: c, ctx: ct } = item.canvas;
ct.clearRect(0, 0, c.width, c.height);
fn(item.canvas, camera);
if (!isNil(write)) {
const cache = item.cacheList.get(write);
if (cache) {
const { canvas, ctx } = cache;
ctx.drawImage(c, 0, 0);
} else {
item.cacheList.set(write, MotaOffscreenCanvas2D.clone(item.canvas));
}
}
ctx.drawImage(c, item.x - ax, item.y - ay, item.width, item.height);
item.using = write;
}