refactor: 换用新的缩放方案

This commit is contained in:
AncTe committed 2025-05-25 20:59:20 +08:00
1 parent a3115fff60
commit e4a6a136ed
39 files changed
+239 -345

No files matched your search

+20 -110
View File
@@ -1,5 +1,4 @@
import { EventEmitter } from 'eventemitter3';
import { logger } from '@motajs/common';
interface OffscreenCanvasEvent {
/** 当被动触发resize时(例如core.domStyle.scale变化、窗口大小变化)时触发,使用size函数并不会触发 */
@@ -7,16 +6,12 @@ interface OffscreenCanvasEvent {
}
export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
static list: Set<MotaOffscreenCanvas2D> = new Set();
canvas: HTMLCanvasElement;
ctx: CanvasRenderingContext2D;
width: number;
height: number;
/** 是否自动跟随样板的core.domStyle.scale进行缩放 */
autoScale: boolean = false;
/** 是否是高清画布 */
highResolution: boolean = true;
/** 是否启用抗锯齿 */
@@ -27,12 +22,6 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
/** 更新标识符,如果发生变化则说明画布被动清空 */
symbol: number = 0;
private _freezed: boolean = false;
/** 当前画布是否被冻结 */
get freezed() {
return this._freezed;
}
private _active: boolean = true;
get active() {
return this._active;
@@ -46,65 +35,52 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
*/
constructor(alpha: boolean = true, canvas?: HTMLCanvasElement) {
super();
// console.trace();
this.canvas = canvas ?? document.createElement('canvas');
this.ctx = this.canvas.getContext('2d', { alpha })!;
this.width = this.canvas.width / devicePixelRatio;
this.height = this.canvas.height / devicePixelRatio;
}
MotaOffscreenCanvas2D.list.add(this);
/**
* 设置画布的缩放比
* @param scale 缩放比
*/
setScale(scale: number) {
if (scale === this.scale) {
this.clear();
return;
}
this.scale = scale;
let ratio = this.highResolution ? devicePixelRatio : 1;
ratio *= this.scale;
this.canvas.width = this.width * ratio;
this.canvas.height = this.height * ratio;
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.scale(ratio, ratio);
this.ctx.imageSmoothingEnabled = this.antiAliasing;
}
/**
* 设置画布的大小
*/
size(width: number, height: number) {
if (this._freezed) {
logger.warn(33);
return;
}
const w = Math.max(width, 1);
const h = Math.max(height, 1);
let ratio = this.highResolution ? devicePixelRatio : 1;
const scale = core.domStyle.scale;
if (this.autoScale) {
ratio *= scale;
}
this.scale = ratio;
ratio *= this.scale;
this.canvas.width = w * ratio;
this.canvas.height = h * ratio;
this.width = w;
this.height = height;
this.height = h;
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.scale(ratio, ratio);
this.ctx.imageSmoothingEnabled = this.antiAliasing;
if (this.canvas.isConnected) {
this.canvas.style.width = `${w * scale}px`;
this.canvas.style.height = `${h * scale}px`;
}
}
/**
* 设置当前画布是否跟随样板的 core.domStyle.scale 一同进行缩放
*/
withGameScale(auto: boolean) {
if (this._freezed) {
logger.warn(33);
return;
}
this.autoScale = auto;
this.size(this.width, this.height);
}
/**
* 设置当前画布是否为高清画布
*/
setHD(hd: boolean) {
if (this._freezed) {
logger.warn(33);
return;
}
this.highResolution = hd;
this.size(this.width, this.height);
}
@@ -113,10 +89,6 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
* 设置当前画布的抗锯齿设置
*/
setAntiAliasing(anti: boolean) {
if (this._freezed) {
logger.warn(33);
return;
}
this.antiAliasing = anti;
this.ctx.imageSmoothingEnabled = anti;
}
@@ -125,56 +97,12 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
* 清空画布
*/
clear() {
if (this._freezed) {
logger.warn(33);
return;
}
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.restore();
}
/**
* 删除这个画布
*/
delete() {
this.canvas.remove();
this.ctx.reset();
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this._freezed = true;
MotaOffscreenCanvas2D.list.delete(this);
}
/**
* 冻结这个画布的属性,之后便不能被修改,同时会从画布列表中删去。
*/
freeze() {
this._freezed = true;
MotaOffscreenCanvas2D.list.delete(this);
}
/**
* 使此画布生效,使用前请务必调用此函数,生效后会跟随游戏的放缩比例更改大小,但会导致不会被垃圾回收
*/
activate() {
if (this._active || this._freezed) return;
MotaOffscreenCanvas2D.list.add(this);
if (this.autoScale) {
this.size(this.width, this.height);
this.symbol++;
this.emit('resize');
}
}
/**
* 使此画布失效,当这个画布暂时不会被使用时请务必调用此函数,失效后若没有对此画布的引用,那么会自动垃圾回收
*/
deactivate() {
if (!this._active || this._freezed) return;
MotaOffscreenCanvas2D.list.delete(this);
}
/**
* 复制一个离屏Canvas2D对象,一般用于缓存等操作
* @param canvas 被复制的MotaOffscreenCanvas2D对象
@@ -183,7 +111,6 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
static clone(canvas: MotaOffscreenCanvas2D): MotaOffscreenCanvas2D {
const newCanvas = new MotaOffscreenCanvas2D();
newCanvas.setHD(canvas.highResolution);
newCanvas.withGameScale(canvas.autoScale);
newCanvas.size(canvas.width, canvas.height);
newCanvas.ctx.drawImage(
canvas.canvas,
@@ -192,23 +119,6 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
canvas.width,
canvas.height
);
newCanvas.freeze();
return newCanvas;
}
static refreshAll(force: boolean = false) {
this.list.forEach(v => {
if (force || v.autoScale) {
v.size(v.width, v.height);
v.symbol++;
v.emit('resize');
}
});
}
}
window.addEventListener('resize', () => {
requestAnimationFrame(() => {
MotaOffscreenCanvas2D.refreshAll();
});
});
+5
View File
@@ -42,6 +42,11 @@ export class Container<E extends EContainerEvent = EContainerEvent>
});
}
onResize(scale: number): void {
this.sortedChildren.forEach(v => v.onResize(scale));
super.onResize(scale);
}
requestSort() {
if (!this.needSort) {
this.needSort = true;
+6 -1
View File
@@ -184,6 +184,11 @@ export abstract class GL2<E extends EGL2Event = EGL2Event> extends RenderItem<
this.init();
}
onResize(scale: number): void {
this.sizeGL(this.width, this.height);
super.onResize(scale);
}
setHD(hd: boolean): void {
super.setHD(hd);
this.sizeGL(this.width, this.height);
@@ -196,7 +201,7 @@ export abstract class GL2<E extends EGL2Event = EGL2Event> extends RenderItem<
private sizeGL(width: number, height: number) {
const ratio = this.highResolution ? devicePixelRatio : 1;
const scale = ratio * core.domStyle.scale;
const scale = ratio * this.cache.scale;
this.canvas.width = width * scale;
this.canvas.height = height * scale;
}
+36 -12
View File
@@ -132,6 +132,14 @@ export interface IRenderTickerSupport {
hasTicker(id: number): boolean;
}
export interface IRenderEvent {
/**
* 当触发缩放事件时,此函数执行的内容
* @param scale 缩放至的缩放比
*/
onResize(scale: number): void;
}
export interface IRenderVueSupport {
/**
* 在 jsx, vue 中当属性改变后触发此函数,用于处理响应式等情况
@@ -185,6 +193,10 @@ export interface IRenderTreeRoot {
hoverElement(element: RenderItem): void;
}
interface RenderItemCanvasData {
autoScale: boolean;
}
export interface ERenderItemEvent extends ERenderItemActionEvent {
beforeRender: [transform: Transform];
afterRender: [transform: Transform];
@@ -213,7 +225,8 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
IRenderTickerSupport,
IRenderChildable,
IRenderVueSupport,
ITransformUpdatable
ITransformUpdatable,
IRenderEvent
{
/** 渲染的全局ticker */
static ticker: Ticker = new Ticker();
@@ -332,6 +345,11 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
readonly transformFallThrough: boolean = false;
/** 这个渲染元素使用到的所有画布 */
protected readonly canvases: Set<MotaOffscreenCanvas2D> = new Set();
/** 这个渲染元素每个画布的配置信息 */
private readonly canvasMap: Map<
MotaOffscreenCanvas2D,
RenderItemCanvasData
> = new Map();
//#endregion
//#region 交互事件
@@ -371,11 +389,8 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
this._transform.bind(this);
this.cache = this.requireCanvas();
this.cache.withGameScale(true);
if (!enableCache) {
this.cache.withGameScale(false);
this.cache.size(1, 1);
this.cache.freeze();
}
}
@@ -439,10 +454,12 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
/**
* 申请一个 `MotaOffscreenCanvas2D`,即申请一个画布
* @param alpha 是否启用画布的 alpha 通道
* @param autoScale 是否自动跟随缩放
*/
protected requireCanvas(alpha: boolean = true) {
requireCanvas(alpha: boolean = true, autoScale: boolean = false) {
const canvas = new MotaOffscreenCanvas2D(alpha);
this.canvases.add(canvas);
this.canvasMap.set(canvas, { autoScale });
return canvas;
}
@@ -450,9 +467,20 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
* 删除由 `requireCanvas` 申请的画布,当画布不再使用时,可以用该方法删除画布
* @param canvas 要删除的画布
*/
protected deleteCanvas(canvas: MotaOffscreenCanvas2D) {
if (!this.canvases.delete(canvas)) return;
canvas.delete();
deleteCanvas(canvas: MotaOffscreenCanvas2D) {
this.canvases.delete(canvas);
this.canvasMap.delete(canvas);
}
//#region 事件处理
onResize(scale: number): void {
this.cache.setScale(scale);
this.canvases.forEach(v => {
if (this.canvasMap.get(v)?.autoScale) {
v.setScale(scale);
}
});
}
//#region 修改元素属性
@@ -736,7 +764,6 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
this.update();
this.checkRoot();
this._root?.connect(this);
this.canvases.forEach(v => v.activate());
this._transform.bind(this);
}
@@ -751,7 +778,6 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
this._parent = void 0;
parent.requestSort();
parent.update();
this.canvases.forEach(v => v.deactivate());
this._transform.bind();
if (!success) return false;
this._root?.disconnect(this);
@@ -1257,8 +1283,6 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
this.remove();
this.emit('destroy');
this.removeAllListeners();
this.cache.delete();
this.canvases.forEach(v => v.delete());
this.canvases.clear();
}
}
+28 -2
View File
@@ -42,6 +42,9 @@ export interface MotaRendererConfig {
export class MotaRenderer extends Container implements IRenderTreeRoot {
static list: Map<string, MotaRenderer> = new Map();
/** 缩放比 */
private scale: number = 1;
/** 所有连接到此根元素的渲染元素的 id 到元素自身的映射 */
protected idMap: Map<string, RenderItem> = new Map();
@@ -79,7 +82,6 @@ export class MotaRenderer extends Container implements IRenderTreeRoot {
}
this.target = new MotaOffscreenCanvas2D(config.alpha ?? true, canvas);
this.size(config.width, config.height);
this.target.withGameScale(true);
this.target.setAntiAliasing(false);
this.setAnchor(0.5, 0.5);
@@ -98,6 +100,31 @@ export class MotaRenderer extends Container implements IRenderTreeRoot {
this.listen();
}
/**
* 设置这个渲染器的缩放比
* @param scale 缩放比
*/
setScale(scale: number) {
this.scale = scale;
this.onResize(scale);
}
/**
* 获取这个渲染器的缩放比
*/
getScale() {
return this.scale;
}
onResize(scale: number): void {
this.target.setScale(scale);
const width = this.target.width * scale;
const height = this.target.height * scale;
this.target.canvas.style.width = `${width}px`;
this.target.canvas.style.height = `${height}px`;
super.onResize(scale);
}
private getMountCanvas(canvas: string | HTMLCanvasElement) {
if (typeof canvas === 'string') {
return document.querySelector(canvas) as HTMLCanvasElement;
@@ -576,7 +603,6 @@ export class MotaRenderer extends Container implements IRenderTreeRoot {
destroy() {
super.destroy();
MotaRenderer.list.delete(this.id);
this.target.delete();
this.abort?.abort();
}
+4 -6
View File
@@ -1,6 +1,4 @@
import { TimingFn } from 'mutate-animate';
// import { RenderAdapter } from './adapter';
// import { FloorViewport } from './preset/viewport';
import { JSX } from 'vue/jsx-runtime';
import { DefineComponent, DefineSetupFnComponent } from 'vue';
import { MotaOffscreenCanvas2D } from './canvas2d';
@@ -14,10 +12,10 @@ export type Props<
> = T extends keyof JSX.IntrinsicElements
? JSX.IntrinsicElements[T]
: T extends DefineSetupFnComponent<any>
? InstanceType<T>['$props'] & InstanceType<T>['$emits']
: T extends DefineComponent
? InstanceType<T>['$props'] & InstanceType<T>['$emits']
: unknown;
? InstanceType<T>['$props'] & InstanceType<T>['$emits']
: T extends DefineComponent
? InstanceType<T>['$props'] & InstanceType<T>['$emits']
: unknown;
export type ElementLocator = [
x?: number,