翻译《threejsfundamentals》离屏渲染+web-worker一篇
Three.js OffscreenCanvas
OffscreenCanvas是一种相对较新的浏览器功能,目前仅在Chrome中可用,但显然也即将适用于其他浏览器。
OffscreenCanvas使web worker能够渲染canvas。这是一种办法把繁重的工作(如渲染复杂的3D场景)交给web worker,为了避免降低浏览器的响应速度。这还意味着数据是在worker中加载和解析的,因此在页面加载时可能会减少卡顿(jank)
初步的使用非常简单,让我们移植3旋转立方体示例从the article on responsiveness。worker通常将他们的代码分离到另一个脚本文件中,而这个站点上的大多数示例都将他们的脚本嵌入到他们所在页面的HTML文件中。 在本例中,我们将创建一个名为offscreencanvas-cubes.js的文件,并将响应式example中的所有JavaScript复制到该文件中。然后,我们将进行必要的更改,使其在辅助进程中运行。
我们仍然需要写一些JavaScript代码在HTML页面中。我们需要做的第一件事是找到canvas,然后通过调用 canvas.transferControlToOffscreen 将该画布的控制权转移到屏幕外。
function main() {
const canvas = document.querySelector('#c');
const offscreen = canvas.transferControlToOffscreen();
...
然后,我们可以用新的 worker(pathToScript,{type:'module'} 启动我们的worker。并将 offscreen 对象传递给它。
function main() {
const canvas = document.querySelector('#c');
const offscreen = canvas.transferControlToOffscreen();
const worker = new Worker('offscreencanvas-cubes.js', {type: 'module'});
worker.postMessage({type: 'main', canvas: offscreen}, [offscreen]);
}
main();
需要注意的是,worker不能访问DOM。他们不能查看HTML元素,也不能接收鼠标事件或键盘事件。通常他们能做的唯一一件事就是响应发送给他们的消息,并将消息发送回页面。
要向worker发送消息,我们调用 worker.postMessage 并向其传递1或2个参数。第一个参数是一个JavaScript对象,它将被 clone 并发送给worker。第二个参数是一个可选的对象数组,它是第一个对象的一部分,第一个对象是我们想要传递给worker的对象。这些对象将不会被克隆。相反,它们将被转移,并将不再存在于主页中。“不存在”可能是错误的描述,相反,它们是中性的。只能传输某些类型的对象,而不能克隆。它们包括 OffscreenCanvas ,因此一旦将 OffscreenCanvas 传输回主页,就没有用了。
worker从他们的 onmessage 处理程序接收消息。我们传递给 postMessage 的对象在 event.data 传递给worker上的 onmessage 处理程序时到达。上面的代码在传递给worker的对象中声明了一个类型:“main”。此对象对浏览器没有意义。这完全是我们自己用的。我们将创建一个处理程序,他基于类型调用worker中不同函数。然后,我们可以根据需要添加函数,并从主页面轻松调用它们。
const handlers = {
main,
};
self.onmessage = function(e) {
const fn = handlers[e.data.type];
if (!fn) {
throw new Error('no handler for type: ' + e.data.type);
}
fn(e.data);
};
调用一个不同的函数,您可以在上面看到,我们只是根据类型查找处理程序,并将从主页发送的数据传递给它,因此,现在我们只需要开始更改我们从the article on responsiveness 粘贴到 offscreencanvas-cubes.js 中的 main 。
我们将从事件数据接收canvas,而不是从DOM中查找canvas。(canvas可以传递吗)
//function main() {
// const canvas = document.querySelector('#c');
function main(data) {
const {canvas} = data;
const renderer = new THREE.WebGLRenderer({canvas});
...
记住worker根本看不到DOM,我们遇到的第一个问题是 ResizeRenderToDisplaySize 不能查看 canvas.clientWidth 和 canvas.ClientHight ,因为它们是DOM值。这是原始代码
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
相反,我们需要在 size 变化时将其发送给worker。所以,让我们添加一些全局状态,并将宽度和高度放置在那里。
const state = {
width: 300, // canvas default
height: 150, // canvas default
};
然后,让我们添加一个 “size” 处理程序来更新这些值。
function size(data) {
state.width = data.width;
state.height = data.height;
}
const handlers = {
main,
size,
};
现在我们可以将 ResizeRenderToDisplaySize 更改为使用 state.width 和 state.height 。
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
// const width = canvas.clientWidth;
// const height = canvas.clientHeight;
const width = state.width;
const height = state.height;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
在我们计算aspect 的地方,我们需要类似的变化
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
// camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.aspect = state.width / state.height;
camera.updateProjectionMatrix();
}
...
回到主页,我们将在页面大小更改时发送大小事件。
const worker = new Worker('offscreencanvas-picking.js', {type: 'module'});
worker.postMessage({type: 'main', canvas: offscreen}, [offscreen]);
function sendSize() {
worker.postMessage({
type: 'size',
width: canvas.clientWidth,
height: canvas.clientHeight,
});
}
window.addEventListener('resize', sendSize);
sendSize();
我们还可以调用它一次,以发送初始大小。
仅做了这些改动,假设您的浏览器完全支持Offscreencanvas,它应该可以正常工作。在我们运行它之前,让我们检查一下浏览器是否真的支持OffscreenCanvas,如果不支持,则显示一个错误。首先,让我们添加一些HTML来显示错误。
<body>
<canvas id="c"></canvas>
<div id="noOffscreenCanvas" style="display:none;">
<div>no OffscreenCanvas support</div>
</div>
</body>
和一些css:
#noOffscreenCanvas {
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
background: red;
color: white;
}
然后我们可以检查 transferControlToOffscreen 是否存在,以查看浏览器是否支持 OffscreenCanvas
function main() {
const canvas = document.querySelector('#c');
if (!canvas.transferControlToOffscreen) {
canvas.style.display = 'none';
document.querySelector('#noOffscreenCanvas').style.display = '';
return;
}
const offscreen = canvas.transferControlToOffscreen();
const worker = new Worker('offscreencanvas-picking.js', {type: 'module});
worker.postMessage({type: 'main', canvas: offscreen}, [offscreen]);
...
如果你的浏览器支持Offscreencanv,那么这个例子就可以了(官方页面中有,我没粘过来,在博客园弄js不太方便)
click here to open in a separate window
这个例子很不错,但因为目前不是每个浏览器都支持OffscreenCanvas,所以让我们将代码更改为同时使用 OffscreenCanvas 和 canvas ,如果不支持 OffscreenCanvas ,则退回到主页中使用 canvas ,就像正常情况一样
顺便说一句,如果你需要OffscreenCanvas来让你的页面更具响应性,那么现在还不清楚回退的意义何在。可能基于您是否最终在主页面上运行或在worker中运行,您可能会调整完成的工作量,以便在worker中运行时可以比在主页面上运行时做得更多。你做什么真的取决于你自己。
我们应该做的第一件事可能是将three.js代码与特定于worker的代码分开。这样我们就可以在主页和工作页面上使用相同的代码。换句话说,我们现在将有3个文件
- our html file: threejs-offscreencanvas-w-fallback.html
- a JavaScript that contains our three.js code: shared-cubes.js
- our worker support code: offscreencanvas-worker-cubes.js
shared-cubes.js 和 offscreencanvas-worker-cubes.js 基本上是我们之前 offscreencanvas-cubes.js 文件的拆分。首先,我们将 offscreencanvas-cubes.js 复制到 shared-cube.js 。然后我们将 main 重命名为 init ,因为我们的HTML文件中已经有一个 main ,我们需要导出 init 和 state
import * as THREE from './resources/threejs/r132/build/three.module.js';
const state = {
export const state = {
width: 300, // canvas default
height: 150, // canvas default
};
function main(data) {
export function init(data) {
const {canvas} = data;
const renderer = new THREE.WebGLRenderer({canvas});
(课业太忙了,暂时没时间写了,未完待续。。。)
翻译《threejsfundamentals》离屏渲染+web-worker一篇的更多相关文章
- web worker 扫盲篇
什么是woker 官方的解释是这样的: worker是一个对象,通过构造函数Worker创建,参数就是一个js文件的路径:文件中的js代码将运行在主线程之外的worker线程: var jsFileU ...
- 离屏Canvas — 使用Web Worker提高你的Canvas运行速度
离屏Canvas — 使用Web Worker提高你的Canvas运行速度 原文链接: developers.google.com 现在因为有了离屏Canvas,你可以不用在你的主线程中绘制图像了! ...
- 新兴的API(fileReader、geolocation、web计时、web worker)
requestAnimationFrame() 每次浏览器重绘之前会调用这个方法!!! 它接收一个参数,就是回调函数: 它可以保证在最佳的间隔时间调用传入的回调函数,以达到让屏幕产生最流畅的动画效果. ...
- Web Worker javascript多线程编程(一)
什么是Web Worker? web worker 是运行在后台的 JavaScript,不占用浏览器自身线程,独立于其他脚本,可以提高应用的总体性能,并且提升用户体验. 一般来说Javascript ...
- HTML5 Web Worker的使用
Web Workers 是 HTML5 提供的一个javascript多线程解决方案,我们可以将一些大计算量的代码交由web Worker运行而不冻结用户界面. 一:如何使用Worker Web Wo ...
- Web Worker 是什么鬼?
前言 前端工程师们一定有过这样的体验,当一个页面加载了大量的 js 文件时,用户界面可能会短暂地"冻结".这很好理解,因为 js 是单线程的语言.我们再走的极端点,一段 js 中出 ...
- 腾讯优测优分享 | 探索react native首屏渲染最佳实践
腾讯优测是专业的移动云测试平台,旗下的优分享不定时提供大量移动研发及测试相关的干货~ 此文主要与以下内容相关,希望对大家有帮助. react native给了我们使用javascript开发原生app ...
- 深入理解JS异步编程四(HTML5 Web Worker)
>Web Workers 是 HTML5 提供的一个javascript多线程解决方案,我们可以将一些大计算量的代码交由web Worker运行而不冻结用户界面. 一:如何使用Worker We ...
- 探索react native首屏渲染最佳实践
文 / 腾讯 龚麒 0.前言 react native给了我们使用javascript开发原生app的能力,在使用react native完成兴趣部落安卓端发现tab改造后,我们开始对由react n ...
- 通过一次实验来了解HTML5的 Web Worker
web worker 是运行在后台的 JavaScript,不会影响页面的性能. 当在 HTML 页面中执行脚本时,页面的状态是不可响应的,直到脚本已完成. web worker 是运行在后台的 Ja ...
随机推荐
- 【初赛】CSP 2020 第一轮(初赛)模拟记录
感觉初赛不过关,洛谷上找了一套没做过的来练习. 顺便写了详细的题解. 试题用时:1h 单项选择: 第 1 题 十进制数 114 的相反数的 8 位二进制补码是: A.10001110 B.100011 ...
- 手记系列之三 ----- 关于使用Nginx的一些使用方法和经验
前言 本篇文章主要介绍的关于本人在使用Nginx的一些使用方法和经验~ Nginx介绍 介绍 Nginx("engine x")是一款是由俄罗斯的程序设计师Igor Sysoev所 ...
- Java 同步锁ReentrantLock与抽象同步队列AQS
AbstractQueuedSynchronizer 抽象同步队列,它是个模板类提供了许多以锁相关的操作,常说的AQS指的就是它.AQS继承了AbstractOwnableSynchronizer类, ...
- Seata 1.5.2 源码学习(Client端)
在上一篇中通过阅读Seata服务端的代码,我们了解到TC是如何处理来自客户端的请求的,今天这一篇一起来了解一下客户端是如何处理TC发过来的请求的.要想搞清楚这一点,还得从GlobalTransacti ...
- TreeUtils工具类一行代码实现列表转树【第三版优化】 三级菜单 三级分类 附视频
一.序言 在日常一线开发过程中,总有列表转树的需求,几乎是项目的标配,比方说做多级菜单.多级目录.多级分类等,有没有一种通用且跨项目的解决方式呢?帮助广大技术朋友给业务瘦身,提高开发效率. 本文将基于 ...
- float16与float32转换
// based on https://gist.github.com/martin-kallman/5049614 // float32 // Martin Kallman // // Fast h ...
- 多线程/GIL全局锁
目录 线程理论 创建线程的两种方式 线程的诸多特性 GIL全局解释器 验证GIL存在 同一个进程下多线程是否有优势 死锁现象 信号量 Event事件 线程理论 进程 进程其实是资源单位 标示开辟一块内 ...
- Day34.2:Calendar详解
Calendar 1.1 概述 Date类中很多方法被Calendar所取代,Calendar类提供了获取和设置各种日历的方法. 1.2 方法 构造方法:Calendar类的构造器被protected ...
- ModuleNotFoundError: No module named 'MySQLdb'
执行命令 python manage.py makemigrations时抛出以下错误 Traceback (most recent call last): File "D:\Program ...
- 红袖添香,绝代妖娆,Ruby语言基础入门教程之Ruby3基础语法,第一次亲密接触EP01
书接上回,前一篇我们在全平台构建好了Ruby3的开发环境,现在,可以和Ruby3第一次亲密接触了. Ruby是一门在面向对象层面无所不用其极的解释型编程语言. 我们可以把编写Ruby代码看作是一场行为 ...