在 ios16.4
版本中已经开始支持了 OffscreenCanvas
,那看样子,是时候再把Three做一波优化了
背景介绍
在之前的项目经验中,如果使用threejs加载比较大的3d场景,那么在创建 threejs
的对象和绘制的时候,会占用浏览器线程执行一个大时长的任务,导致页面卡住,不能交互。
那有什么即可以绘制 canvas
又不占用主线程的方法吗?
今天它来了(其实已经来了很久了)
使用WebWorker
+ OffscreenCanvas
就可以实现在另外的线程中绘制canvas
,从而做到不影响主线程。
本文不会主要介绍 WebWorker
和 Threejs
基础知识,只是一篇实操(辛酸史),但是在必要的时候会提供相关的链接
WebWorker
可以在后台启动一个线程执行js脚本,并且不会影响到主线程。
关于Webworker: 使用 Web Workers - Web API 接口参考 | MDN
OffscreenCanvas
是一个可以脱离屏幕渲染的canvas
对象,在串口环境和 WebWorker
环境都可以使用
关于OffscreenCanvas: OffscreenCanvas - Web API 接口参考 | MDN
项目开始
接下来就实践一下WebWorker
+ Threejs
渲染3d场景,并使用 OrbitControls
实现人机交互
Demo使用
vue3
开发
app.vue
<template>
<canvas ref="canvas"></canvas>
</template>
<script setup>
import { ref } from 'vue'
canvas = ref()
</script>
在 WebWorker
中只能使用 OffscreenCanvas
,不能直接操作 DOM
,所以需要把 canvas
元素转成 OffscreenCanvas
对象,在传递给 WebWorker
中使用
在 vue
的组件 onMounted
生命周期中,可以访问 DOM
元素
App.vue
import {
// ...other
onMounted
} from 'vue'
import Worker from './worker?worker'
const worker = new Worker()
onMounted(() =>{
const offCanvas = canvas.value.transferControlToOffscreen()
})
和 WebWorker
的通信通过 postMessage
和 onmessage
worker.postMessage({
type: 'init',
data: {
offCanvas
}
}, [offCanvas])
在 webworker
中接收传入的 OffscreenCanvas
对象
worker.js
self.onmessage = function ({
data }) {
switch (data.type) {
case 'init':
init(data.data)
break;
}
}
使用传入的 OffscreenCanvas
对象做threejs的渲染
import {
OrthographicCamera,
Scene,
WebGLRenderer,
BoxGeometry,
MeshLambertMaterial,
Mesh,
AmbientLight
} from 'three'
self.onmessage =