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个文件

    1. our html file: threejs-offscreencanvas-w-fallback.html
    2. a JavaScript that contains our three.js code: shared-cubes.js
    3. 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一篇的更多相关文章

  1. web worker 扫盲篇

    什么是woker 官方的解释是这样的: worker是一个对象,通过构造函数Worker创建,参数就是一个js文件的路径:文件中的js代码将运行在主线程之外的worker线程: var jsFileU ...

  2. 离屏Canvas — 使用Web Worker提高你的Canvas运行速度

    离屏Canvas — 使用Web Worker提高你的Canvas运行速度 原文链接: developers.google.com 现在因为有了离屏Canvas,你可以不用在你的主线程中绘制图像了! ...

  3. 新兴的API(fileReader、geolocation、web计时、web worker)

    requestAnimationFrame() 每次浏览器重绘之前会调用这个方法!!! 它接收一个参数,就是回调函数: 它可以保证在最佳的间隔时间调用传入的回调函数,以达到让屏幕产生最流畅的动画效果. ...

  4. Web Worker javascript多线程编程(一)

    什么是Web Worker? web worker 是运行在后台的 JavaScript,不占用浏览器自身线程,独立于其他脚本,可以提高应用的总体性能,并且提升用户体验. 一般来说Javascript ...

  5. HTML5 Web Worker的使用

    Web Workers 是 HTML5 提供的一个javascript多线程解决方案,我们可以将一些大计算量的代码交由web Worker运行而不冻结用户界面. 一:如何使用Worker Web Wo ...

  6. Web Worker 是什么鬼?

    前言 前端工程师们一定有过这样的体验,当一个页面加载了大量的 js 文件时,用户界面可能会短暂地"冻结".这很好理解,因为 js 是单线程的语言.我们再走的极端点,一段 js 中出 ...

  7. 腾讯优测优分享 | 探索react native首屏渲染最佳实践

    腾讯优测是专业的移动云测试平台,旗下的优分享不定时提供大量移动研发及测试相关的干货~ 此文主要与以下内容相关,希望对大家有帮助. react native给了我们使用javascript开发原生app ...

  8. 深入理解JS异步编程四(HTML5 Web Worker)

    >Web Workers 是 HTML5 提供的一个javascript多线程解决方案,我们可以将一些大计算量的代码交由web Worker运行而不冻结用户界面. 一:如何使用Worker We ...

  9. 探索react native首屏渲染最佳实践

    文 / 腾讯 龚麒 0.前言 react native给了我们使用javascript开发原生app的能力,在使用react native完成兴趣部落安卓端发现tab改造后,我们开始对由react n ...

  10. 通过一次实验来了解HTML5的 Web Worker

    web worker 是运行在后台的 JavaScript,不会影响页面的性能. 当在 HTML 页面中执行脚本时,页面的状态是不可响应的,直到脚本已完成. web worker 是运行在后台的 Ja ...

随机推荐

  1. java学习之spring基础

    0x00前言 spring框架应用的是ioc模式,ioc模式是指控制反转模式,本质是你不去创建对象让spring框架给你创建对象你去使用对象.多种开发模式通过配置文件和注解的方式去开发的都很值得去学习 ...

  2. 工作总结:kafka踩过的坑

    餐饮系统每天中午和晚上用餐高峰期,系统的并发量不容小觑.公司规定各部门都要轮流值班,防止出现线上问题时能够及时处理. 后厨显示系统属于订单的下游业务. 用户点完菜下单后,订单系统会通过发 Kafka ...

  3. 修复 docker build 错误 "ERROR: No build stage in current context"

    若 docker build 时遇到了错误 "ERROR: No build stage in current context",则有可能是没有将 FROM 命令语句放置在第一行. ...

  4. Spring Cloud Circuit Breaker 使用示例

    Spring Cloud Circuit Breaker 使用示例 作者: Grey 原文地址: 博客园:Spring Cloud Circuit Breaker 使用示例 CSDN:Spring C ...

  5. 在 Solidity 中 ++i 为什么比 i++ 更省 Gas?

    前言 作为一个初学者,"在 Solidity 中 ++i 为什么比 i++ 更省 Gas?" 这个问题始终在每个寂静的深夜困扰着我.也曾在网上搜索过相关问题,但没有得到根本性的解答 ...

  6. 真正“搞”懂HTTP协议05之What's HTTP?

    前面几篇文章,我从纵向的空间到横向的时间,再到一个具体的小栗子,可以说是全方位,无死角的覆盖了HTTP的大部分基本框架,但是我聊的都太宽泛了,很多内容都是一笔带过,再加上一句后面再说就草草结束了.并且 ...

  7. 手动安装Mysql数据库,适合重装系统和绿色安装。

    依次输入命令: d:cd D:\MySQL Server 5.6\bin 根据自己的目录设置 mysqld --install mysql4001 --defaults-file="D:\m ...

  8. 记录一次从linux移动一个项目到windows遇到的问题

    前言 这几天在linux平台写了一个垃圾软件,浪费了我10多天的时间,感觉很垃圾,然后我想在windows平台打包这个软件,然后出现了一个项目中有相同文件名的问题,导致一些文件相互覆盖 问题描述 我把 ...

  9. ajax 跨域请求jsonp

    最近一段时间为这个事情走了不少弯路,现将成功经验分享,避免后来人再绕远路,不过也是第一次使用中间有什么问题大家可以留言探讨. ajax的跨域请求jsonp主要运用于不同系统的交互,一个系统想通过该种方 ...

  10. Halo 主题 Redemption 首发版

    Redemption 一款专注阅读.写作的 Halo 博客主题.主要设计思想即是专注阅读.写作,是一款极简类型的博客主题. Redemption 部分设计灵感借鉴 Halo 博客 Zozo 主题,感谢 ...