Skip to content

Commit e9e339a

Browse files
committed
feat: 使用 WebWorker 进行离屏渲染,避免严重的内存泄漏问题
1 parent 6443abb commit e9e339a

File tree

8 files changed

+521
-86
lines changed

8 files changed

+521
-86
lines changed

template-creator/src/App.vue

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Preview from "@/components/sidebar/Preview.vue";
1313
import RatioSelector from "@/components/sidebar/RatioSelector.vue";
1414
import RectList from "@/components/sidebar/RectList.vue";
1515
import Toolbar from "@/components/sidebar/Toolbar.vue";
16+
import NewPanel from "./components/NewPanel.vue";
1617
import { CameraType } from "@/dtos/enums";
1718
import { Project } from "@/dtos/Project";
1819
import { selectFile } from "@/funcs/files";
@@ -76,6 +77,7 @@ function duplicateROI(uuid: string) {
7677
*/
7778
function switchCamera() {
7879
camera.value = camera.value === CameraType.CAMERA_ROI ? CameraType.CAMERA_RECT : CameraType.CAMERA_ROI;
80+
if (project.value) project.value.cameraType = camera.value;
7981
}
8082
8183
async function createNewProject() {
@@ -85,6 +87,7 @@ async function createNewProject() {
8587
await newProject.loadScreenImage();
8688
newProject.filename = '*';
8789
project.value = newProject;
90+
nextTick();
8891
setTitle(newProject.name);
8992
}
9093
@@ -137,17 +140,12 @@ async function loadProject() {
137140
}
138141
139142
async function openConfirm() {
140-
// const result = await promptModal<boolean>(DialogConfirm)
141-
// return result;
142143
const result = await modal.value?.openPrompt<boolean>(DialogConfirm);
143144
return result;
144145
}
145146
146147
async function changeProjectName() {
147148
if (!project.value) return;
148-
// const result = await promptModal<string | null>(ProjectNamePrompt);
149-
// if (result === null) return;
150-
// project.value.name = result;
151149
const result = await modal.value?.openPrompt<string>(ProjectNamePrompt);
152150
if (typeof result === 'string') {
153151
project.value.name = result;
@@ -229,7 +227,8 @@ onUnmounted(() => {
229227
</template>
230228
</Sidebar>
231229

232-
<Panel v-if="project"></Panel>
230+
<!-- <Panel v-if="project" /> -->
231+
<NewPanel v-if="project" />
233232
</div>
234233
<!-- <JenesiusModalContainer /> -->
235234
<Modal ref="modal" />
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
<template>
2+
<div class="panel">
3+
<div class="background" ref="bgDiv"></div>
4+
<!-- <canvas ref="rBgCanvasElement" width="100" height="100"></canvas> -->
5+
<canvas ref="rCanvasElement" width="100" height="100"></canvas>
6+
</div>
7+
</template>
8+
9+
<script setup lang="ts">
10+
import { projectInjectKey } from "@/utils/injects";
11+
import { inject, onBeforeUnmount, onMounted, ref, watch } from "vue";
12+
import RendererWorker from '../core/renderer-worker?worker';
13+
14+
if (window.myWorker) {
15+
window.myWorker.terminate();
16+
}
17+
window.myWorker = new RendererWorker();
18+
19+
const project = inject(projectInjectKey);
20+
const bgDiv = ref<HTMLDivElement>();
21+
// const rBgCanvasElement = ref<HTMLCanvasElement>();
22+
const rCanvasElement = ref<HTMLCanvasElement>();
23+
let mCtx: CanvasRenderingContext2D | null = null;
24+
let count = 0;
25+
26+
window.myWorker.addEventListener('message', (e) => {
27+
// console.log('message', e.data);
28+
const bitmap: ImageBitmap = e.data;
29+
mCtx?.clearRect(0, 0, rCanvasElement.value!.width, rCanvasElement.value!.height);
30+
mCtx?.drawImage(bitmap, 0, 0);
31+
bitmap.close();
32+
count++;
33+
});
34+
35+
36+
37+
function initDrawable() {
38+
if (project?.value) {
39+
const { screenHeight: h, screenWidth: w } = project.value
40+
if (rCanvasElement.value) {
41+
rCanvasElement.value.width = w;
42+
rCanvasElement.value.height = h;
43+
}
44+
if (bgDiv.value) {
45+
bgDiv.value.style.aspectRatio = `${w}/${h}`
46+
if (w > h) {
47+
bgDiv.value.style.width = '100%';
48+
bgDiv.value.style.height = 'auto';
49+
} else {
50+
bgDiv.value.style.width = 'auto';
51+
bgDiv.value.style.height = '100%';
52+
}
53+
bgDiv.value.style.backgroundImage = `url(${project.value.image?.src})`;
54+
}
55+
window.myWorker?.postMessage({
56+
type: 'init',
57+
width: w,
58+
height: h,
59+
drawable: project.value.toDrawable()
60+
});
61+
}
62+
}
63+
64+
watch(() => project?.value?.uuid, () => initDrawable());
65+
watch(() => project?.value, (p) => {
66+
if (p) {
67+
window.myWorker?.postMessage({
68+
type: 'draw',
69+
drawable: p.toDrawable()
70+
});
71+
}
72+
});
73+
74+
let intervalId: number | null = null;
75+
onMounted(() => {
76+
mCtx = rCanvasElement.value!.getContext('2d')!;
77+
initDrawable();
78+
intervalId = window.setInterval(() => {
79+
if (project?.value) {
80+
window.myWorker?.postMessage({
81+
type: 'update',
82+
drawable: project.value.toDrawable()
83+
});
84+
}
85+
}, 20);
86+
});
87+
88+
onBeforeUnmount(() => {
89+
window.myWorker?.terminate();
90+
clearInterval(intervalId!);
91+
});
92+
</script>
93+
94+
<style scoped lang="scss">
95+
div.panel {
96+
position: relative;
97+
flex: 1;
98+
display: flex;
99+
align-items: center;
100+
justify-content: center;
101+
102+
canvas {
103+
position: absolute;
104+
display: block;
105+
flex: none;
106+
max-width: 100%;
107+
max-height: 100%;
108+
background-color: transparent;
109+
}
110+
111+
div.background {
112+
position: absolute;
113+
display: block;
114+
flex: none;
115+
max-width: 100%;
116+
max-height: 100%;
117+
background-color: transparent;
118+
background-size: contain;
119+
}
120+
121+
div.image {
122+
position: relative;
123+
124+
img {
125+
max-width: 100%;
126+
max-height: 100%;
127+
margin-left: 1px;
128+
}
129+
130+
div.safe-area {
131+
position: absolute;
132+
top: 50%;
133+
border: 1px solid red;
134+
width: 100%;
135+
margin-top: -50%;
136+
aspect-ratio: 9/16;
137+
}
138+
}
139+
140+
}
141+
</style>

0 commit comments

Comments
 (0)