Title: Three.js Советы
Description: Небольшие вопросы, которые могут появится, используя three.js
TOC: #
Эта статья представляет собой набор небольших проблем, с которыми вы можете столкнуться при использовании three.js, которые кажутся слишком маленькими, чтобы иметь собственную статью.
---
# Делаем скриншот холста
В браузере фактически есть 2 функции, которые сделают скриншот.
Старая
[`canvas.toDataURL`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL)
и новая улучшенная
[`canvas.toBlob`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob)
Таким образом, вы думаете, что было бы легко сделать снимок экрана, просто добавив такой код, как
```html
+
```
```js
const elem = document.querySelector('#screenshot');
elem.addEventListener('click', () => {
canvas.toBlob((blob) => {
saveBlob(blob, `screencapture-${canvas.width}x${canvas.height}.png`);
});
});
const saveBlob = (function() {
const a = document.createElement('a');
document.body.appendChild(a);
a.style.display = 'none';
return function saveData(blob, fileName) {
const url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
};
}());
```
Вот пример из [статьи об отзывчивости](threejs-responsive.html)
с добавленным выше кодом и некоторым CSS для размещения кнопки
{{{example url="../threejs-tips-screenshot-bad.html"}}}
Когда я попробовал это, я получил этот скриншот
Да, это просто черное изображение.
Возможно, это сработало для вас в зависимости от вашего браузера / ОС, но в целом это вряд ли сработает.
Проблема заключается в том, что по соображениям производительности и совместимости
браузер по умолчанию очищает буфер рисования WebGL-холста после его отрисовки.
Решение состоит в том, чтобы вызвать ваш код рендеринга непосредственно перед захватом.
В нашем коде нам нужно настроить несколько вещей. Сначала давайте выделим код рендеринга.
```js
+const state = {
+ time: 0,
+};
-function render(time) {
- time *= 0.001;
+function render() {
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
cubes.forEach((cube, ndx) => {
const speed = 1 + ndx * .1;
- const rot = time * speed;
+ const rot = state.time * speed;
cube.rotation.x = rot;
cube.rotation.y = rot;
});
renderer.render(scene, camera);
- requestAnimationFrame(render);
}
+function animate(time) {
+ state.time = time * 0.001;
+
+ render();
+
+ requestAnimationFrame(animate);
+}
+requestAnimationFrame(animate);
```
Теперь этот `render` касается только фактического рендеринга, мы можем вызвать его непосредственно перед захватом холста.
```js
const elem = document.querySelector('#screenshot');
elem.addEventListener('click', () => {
+ render();
canvas.toBlob((blob) => {
saveBlob(blob, `screencapture-${canvas.width}x${canvas.height}.png`);
});
});
```
И теперь это должно работать.
{{{example url="../threejs-tips-screenshot-good.html" }}}
Для другого решения см. Следующий пункт.
---
# Предотвращение очистки холста
Допустим, вы хотели, чтобы пользователь рисовал анимированный объект.
Вам нужно передать `preserveDrawingBuffer: true` при создании `WebGLRenderer`.
Это мешает браузеру очистить холст. Вы также должны сказать three.js не очищать холст.
```js
const canvas = document.querySelector('#c');
-const renderer = new THREE.WebGLRenderer({canvas});
+const renderer = new THREE.WebGLRenderer({
+ canvas,
+ preserveDrawingBuffer: true,
+ alpha: true,
+});
+renderer.autoClearColor = false;
```
{{{example url="../threejs-tips-preservedrawingbuffer.html" }}}
Обратите внимание, что если вы серьезно относитесь к созданию программы для рисования,
это не будет решением, так как браузер будет очищать холст каждый раз, когда мы меняем его разрешение.
Мы меняем разрешение в зависимости от размера дисплея. Размер дисплея изменяется при изменении размера окна.
Это включает в себя, когда пользователь загружает файл, даже в другой вкладке, и браузер добавляет строку состояния.
Это также включает в себя, когда пользователь поворачивает свой телефон и браузер переключается с портретного на альбомный.
Если вы действительно хотите создать программу для рисования, вы должны .
[визуализировать текстуру, используя цель визуализации](threejs-rendertargets.html).
---
# Ввод с клавиатуры
В этих уроках мы часто прикрепляли слушателей событий `canvas`.
Хотя многие события работают, по умолчанию не работают события клавиатуры.
Чтобы получить события клавиатуры, установите для холста [`tabindex`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/tabIndex)
значение 0 или более. Например.
```html
```
Это в конечном итоге вызывает новую проблему, хотя. Все, что имеет установленный `tabindex` будет выделено, когда оно будет в фокусе.
Чтобы исправить это, установите фокус CSS `outline:none`.
```css
canvas:focus {
outline:none;
}
```
Для демонстрации здесь представлены 3 холста
```html
```
и немного CSS только для последнего холста
```css
#c3:focus {
outline: none;
}
```
Давайте прикрепим к ним всех слушателей событий
```js
document.querySelectorAll('canvas').forEach((canvas) => {
const ctx = canvas.getContext('2d');
function draw(str) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(str, canvas.width / 2, canvas.height / 2);
}
draw(canvas.id);
canvas.addEventListener('focus', () => {
draw('has focus press a key');
});
canvas.addEventListener('blur', () => {
draw('lost focus');
});
canvas.addEventListener('keydown', (e) => {
draw(`keyCode: ${e.keyCode}`);
});
});
```
Обратите внимание, что вы не можете получить первый холст,
чтобы принять ввод с клавиатуры. Второе полотно вы можете,
но оно подсвечивается. На третьем холсте применяются оба решения.
{{{example url="../threejs-tips-tabindex.html"}}}
---
# Делаем холст прозрачным
По умолчанию THREE.js делает холст непрозрачным. Если вы хотите, чтобы холст был прозрачным,
передайте [`alpha:true`](WebGLRenderer.alpha) при создании `WebGLRenderer`
```js
const canvas = document.querySelector('#c');
-const renderer = new THREE.WebGLRenderer({canvas});
+const renderer = new THREE.WebGLRenderer({
+ canvas,
+ alpha: true,
+});
```
Вы, вероятно, также хотите сказать, что ваши результаты не используют предварительно умноженную альфа
```js
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({
canvas,
alpha: true,
+ premultipliedAlpha: false,
});
```
Three.js по умолчанию использует холст с использованием
[`premultipliedAlpha: true`](WebGLRenderer.premultipliedAlpha) но по умолчанию используется для материалов, которые выводят
[`premultipliedAlpha: false`](Material.premultipliedAlpha).
Если вы хотите лучше понять, когда и когда не следует использовать предварительно умноженную альфу,
[ вот хорошая статья об этом](https://developer.nvidia.com/content/alpha-blending-pre-or-not-pre).
В любом случае давайте настроим простой пример с прозрачным холстом.
Мы применили настройки выше к примеру из [статьи об отзывчивости](threejs-responsive.html).
Давайте также сделаем материалы более прозрачными
```js
function makeInstance(geometry, color, x) {
- const material = new THREE.MeshPhongMaterial({color});
+ const material = new THREE.MeshPhongMaterial({
+ color,
+ opacity: 0.5,
+ });
...
```
И давайте добавим немного HTML-контента
```html
+
+
+
Cubes-R-Us!
+
We make the best cubes!
+
+
```
а также немного CSS, чтобы поставить холст впереди
```css
body {
margin: 0;
}
#c {
width: 100%;
height: 100%;
display: block;
+ position: fixed;
+ left: 0;
+ top: 0;
+ z-index: 2;
+ pointer-events: none;
}
+#content {
+ font-size: 7vw;
+ font-family: sans-serif;
+ text-align: center;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
```
обратите внимание, что `pointer-events: none` делает холст невидимым для мыши и сенсорных событий, поэтому вы можете выделить текст под ним.
{{{example url="../threejs-tips-transparent-canvas.html" }}}
---
# Создание анимированного фона в three.js
Распространенный вопрос - как сделать анимацию three.js фоном веб-страницы.
Есть 2 очевидных способа.
* Установите у холста CSS `position` `fixed` как в
```css
#c {
position: fixed;
left: 0;
top: 0;
...
}
```
Вы можете в основном увидеть это точное решение на предыдущем примере. Просто установите `z-index` на -1, и кубы появятся за текстом.
Небольшим недостатком этого решения является то, что ваш JavaScript должен интегрироваться со страницей,
и если у вас сложная страница, вам нужно убедиться, что ни один из JavaScript в вашей визуализации
three.js не конфликтует с JavaScript, выполняющим другие действия на странице.
* Используйте `iframe`
Это решение используется на
[главной странице этого сайта ](/).
На вашей веб-странице просто вставьте iframe, например
```html