fix: 画布内存泄漏

This commit is contained in:
unanmed 2025-09-05 13:10:38 +08:00
parent 351ede3996
commit 65855d1a64
13 changed files with 50 additions and 30 deletions

View File

@ -48,10 +48,10 @@ function constructor(
创建一个新的离屏画布。 创建一个新的离屏画布。
**参数** **参数**
- `alpha`: 是否启用透明度通道(默认为 `true`)。 - `alpha`: 是否启用透明度通道(默认为 `true`)。
- `canvas`: 可指定现有画布,未提供时自动创建新画布。 - `canvas`: 可指定现有画布,未提供时自动创建新画布。
**注意** **注意**
- 在自定义渲染元素中,建议使用 `RenderItem.requireCanvas` 而非直接调用此构造函数。 - 在自定义渲染元素中,建议使用 `RenderItem.requireCanvas` 而非直接调用此构造函数。之后如果不使用,再使用 `RenderItem.deleteCanvas` 删除。
--- ---
@ -67,11 +67,11 @@ function size(width: number, height: number): void;
设置画布的尺寸。 设置画布的尺寸。
**参数** **参数**
- `width`: 逻辑宽度(最小为 1 - `width`: 逻辑宽度(最小为 1
- `height`: 逻辑高度(最小为 1 - `height`: 逻辑高度(最小为 1
**行为** **行为**
- 自动计算缩放比例(考虑 `highResolution``autoScale`)。 - 自动计算缩放比例(考虑 `highResolution``autoScale`)。
- 调整画布物理尺寸和样式尺寸。 - 调整画布物理尺寸和样式尺寸。
**示例** **示例**
@ -92,7 +92,7 @@ function withGameScale(auto: boolean): void;
设置画布是否跟随 `core.domStyle.scale` 自动缩放。 设置画布是否跟随 `core.domStyle.scale` 自动缩放。
**参数** **参数**
- `auto`: 是否启用自动缩放。 - `auto`: 是否启用自动缩放。
**示例** **示例**
@ -112,7 +112,7 @@ function setHD(hd: boolean): void;
设置是否为高清画布(基于设备像素比例)。 设置是否为高清画布(基于设备像素比例)。
**参数** **参数**
- `hd`: 是否启用高清模式。 - `hd`: 是否启用高清模式。
**示例** **示例**
@ -132,7 +132,7 @@ function setAntiAliasing(anti: boolean): void;
设置抗锯齿功能。 设置抗锯齿功能。
**参数** **参数**
- `anti`: 是否启用抗锯齿。 - `anti`: 是否启用抗锯齿。
**示例** **示例**
@ -152,7 +152,7 @@ function clear(): void;
清空画布内容。 清空画布内容。
**注意** **注意**
- 冻结状态下调用此方法会触发警告。 - 冻结状态下调用此方法会触发警告。
**示例** **示例**
@ -242,7 +242,7 @@ function clone(canvas: MotaOffscreenCanvas2D): MotaOffscreenCanvas2D;
复制一个画布对象,结果画布将被冻结。 复制一个画布对象,结果画布将被冻结。
**返回值** **返回值**
- 复制的画布对象(不可修改属性,但可绘制)。 - 复制的画布对象(不可修改属性,但可绘制)。
**示例** **示例**
@ -262,7 +262,7 @@ function refreshAll(force: boolean = false): void;
刷新所有已注册画布的尺寸(仅在窗口大小变化时自动调用)。 刷新所有已注册画布的尺寸(仅在窗口大小变化时自动调用)。
**参数** **参数**
- `force`: 是否强制刷新所有画布(默认仅刷新启用 `autoScale` 的画布)。 - `force`: 是否强制刷新所有画布(默认仅刷新启用 `autoScale` 的画布)。
--- ---

View File

@ -1,9 +1,10 @@
import { EventEmitter } from 'eventemitter3'; import { EventEmitter } from 'eventemitter3';
import { logger } from '@motajs/common'; import { logger } from '@motajs/common';
import { MotaOffscreenCanvas2D } from '@motajs/render-core'; import { MotaOffscreenCanvas2D, RenderItem } from '@motajs/render-core';
interface BlockCacherEvent { interface BlockCacherEvent {
split: []; split: [];
beforeClear: [index: number];
} }
interface BlockData { interface BlockData {
@ -307,9 +308,12 @@ export interface ICanvasCacheItem extends IBlockCacheable {
export class CanvasCacheItem implements ICanvasCacheItem { export class CanvasCacheItem implements ICanvasCacheItem {
constructor( constructor(
public canvas: MotaOffscreenCanvas2D, public readonly canvas: MotaOffscreenCanvas2D,
public symbol: number public readonly symbol: number,
public readonly element: RenderItem
) {} ) {}
destroy(): void {} destroy(): void {
this.element.deleteCanvas(this.canvas);
}
} }

View File

@ -539,7 +539,7 @@ export class Damage extends RenderItem<EDamageEvent> {
}); });
ctx.drawImage(temp.canvas, px, py, size, size); ctx.drawImage(temp.canvas, px, py, size, size);
block.cache.set(v, new CanvasCacheItem(temp, temp.symbol)); block.cache.set(v, new CanvasCacheItem(temp, temp.symbol, this));
}); });
ctx.restore(); ctx.restore();
// console.timeEnd('damage'); // console.timeEnd('damage');

View File

@ -774,6 +774,7 @@ export class Layer extends Container<ELayerEvent> {
const num = this.background; const num = this.background;
const data = texture.getRenderable(num); const data = texture.getRenderable(num);
this.backImage.forEach(v => this.deleteCanvas(v));
this.backImage = []; this.backImage = [];
if (!data) return; if (!data) return;
@ -800,6 +801,7 @@ export class Layer extends Container<ELayerEvent> {
this.backImage.push(canvas); this.backImage.push(canvas);
} }
this.deleteCanvas(temp);
for (const ex of this.extend.values()) { for (const ex of this.extend.values()) {
ex.onBackgroundGenerated?.(this, this.backImage); ex.onBackgroundGenerated?.(this, this.backImage);
@ -1239,7 +1241,10 @@ export class Layer extends Container<ELayerEvent> {
blockSize * cell, blockSize * cell,
blockSize * cell blockSize * cell
); );
this.block.cache.set(index, new CanvasCacheItem(temp, temp.symbol)); this.block.cache.set(
index,
new CanvasCacheItem(temp, temp.symbol, this)
);
}); });
} }

View File

@ -211,6 +211,7 @@ export class Winskin extends RenderItem<EWinskinEvent> {
Winskin.patternMap.set(this.imageName, winskinPattern); Winskin.patternMap.set(this.imageName, winskinPattern);
} }
this.patternCache = winskinPattern; this.patternCache = winskinPattern;
this.deleteCanvas(pattern);
return winskinPattern; return winskinPattern;
} }

View File

@ -73,6 +73,7 @@ export class SplittableBall extends Projectile<PalaceBoss> {
private splitted: boolean = false; private splitted: boolean = false;
static init(colors: Record<string, string[]>) { static init(colors: Record<string, string[]>) {
this.ball.forEach(v => mainRenderer.deleteCanvas(v));
this.ball.clear(); this.ball.clear();
for (const [key, color] of Object.entries(colors)) { for (const [key, color] of Object.entries(colors)) {
const canvas = mainRenderer.requireCanvas(); const canvas = mainRenderer.requireCanvas();

View File

@ -24,7 +24,8 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
/** /**
* \ * \
* ****使使使 `RenderItem.requireCanvas` * ****使使使 `RenderItem.requireCanvas`
* 使使 `RenderItem.deleteCanvas`
* @param alpha * @param alpha
* @param canvas * @param canvas
*/ */

View File

@ -348,10 +348,10 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
/** 这个渲染元素使用到的所有画布 */ /** 这个渲染元素使用到的所有画布 */
protected readonly canvases: Set<MotaOffscreenCanvas2D> = new Set(); protected readonly canvases: Set<MotaOffscreenCanvas2D> = new Set();
/** 这个渲染元素每个画布的配置信息 */ /** 这个渲染元素每个画布的配置信息 */
private readonly canvasMap: Map< private readonly canvasMap: WeakMap<
MotaOffscreenCanvas2D, MotaOffscreenCanvas2D,
RenderItemCanvasData RenderItemCanvasData
> = new Map(); > = new WeakMap();
//#endregion //#endregion
//#region 交互事件 //#region 交互事件
@ -1305,6 +1305,14 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
this.emit('destroy'); this.emit('destroy');
this.removeAllListeners(); this.removeAllListeners();
this.canvases.clear(); this.canvases.clear();
this.cache.clear();
this.propagationStoped.clear();
this.cachedEvent.clear();
this.mouseId.clear();
this.touchId.clear();
this.children.clear();
this._root = void 0;
this._parent = void 0;
} }
} }

View File

@ -89,7 +89,7 @@ main.floors.MT16=
"no": [ "no": [
{ {
"type": "function", "type": "function",
"function": "function(){\nMota.require('replay_g').readyClip();\n}" "function": "function(){\nMota.require('@user/legacy-plugin-data').readyClip();\n}"
}, },
{ {
"type": "choices", "type": "choices",
@ -400,7 +400,7 @@ main.floors.MT16=
}, },
{ {
"type": "function", "type": "function",
"function": "function(){\nMota.require('chase_r').start(false);\n}" "function": "function(){\nMota.require('legacy-plugin-client').start(false);\n}"
}, },
{ {
"type": "autoSave" "type": "autoSave"

View File

@ -56,7 +56,7 @@ main.floors.MT17=
"12,6": [ "12,6": [
{ {
"type": "function", "type": "function",
"function": "function(){\nif (core.status.hero.hp - flags.hphphp >= 150000) {\n\tMota.require('achievement_r').completeAchievement('normal', 1);\n}\ndelete flags.hphphp;\n}" "function": "function(){\nif (core.status.hero.hp - flags.hphphp >= 150000) {\n\t//Mota.require('achievement_r').completeAchievement('normal', 1);\n}\ndelete flags.hphphp;\n}"
} }
] ]
}, },

View File

@ -70,7 +70,7 @@ main.floors.MT41=
"那我就送你回到标题界面吧!", "那我就送你回到标题界面吧!",
{ {
"type": "function", "type": "function",
"function": "function(){\nMota.require('achievement_r').completeAchievement('explore', 0);\n}" "function": "function(){\n//Mota.require('achievement_r').completeAchievement('explore', 0);\n}"
}, },
{ {
"type": "restart" "type": "restart"

View File

@ -124,7 +124,7 @@ main.floors.MT6=
"4,12": [ "4,12": [
{ {
"type": "function", "type": "function",
"function": "function(){\nif (core.status.hero.hp === 1) {\n\tMota.require('achievement_r').completeAchievement('normal', 0);\n}\n}" "function": "function(){\nif (core.status.hero.hp === 1) {\n\t//Mota.require('achievement_r').completeAchievement('normal', 0);\n}\n}"
} }
] ]
}, },

View File

@ -123,7 +123,7 @@ main.floors.tower7=
"下面,就让我们开始吧!", "下面,就让我们开始吧!",
{ {
"type": "function", "type": "function",
"function": "function(){\nMota.require('replay_g').readyClip();\n}" "function": "function(){\nMota.require('@user/legacy-plugin-data').readyClip();\n}"
} }
] ]
}, },