|
@@ -0,0 +1,270 @@
|
|
|
+Title: Three.js Oтзывчивый Дизайн
|
|
|
+Description: Как приспособить three.js под дисплеи разного размера.
|
|
|
+
|
|
|
+Это вторая статья в серии статей о three.js.
|
|
|
+Первая была [об основах](threejs-fundamentals.html).
|
|
|
+Если вы еще не читали это, вы можете начать с основ.
|
|
|
+
|
|
|
+Эта статья о том, как заставить ваше приложение three.js
|
|
|
+реагировать на любую ситуацию. Создание адаптивной веб-страницы
|
|
|
+обычно означает, что страница хорошо отображается на экранах
|
|
|
+разных размеров - от настольных компьютеров до планшетов и телефонов.
|
|
|
+
|
|
|
+Для three.js нужно рассмотреть еще больше ситуаций. Например,
|
|
|
+3D-редактор с элементами управления слева, справа, сверху или
|
|
|
+снизу - это то, что мы можем захотеть обработать. Динамическая диаграмма
|
|
|
+в середине документа является еще одним примером.
|
|
|
+
|
|
|
+В последнем примере мы использовали простой холст без указания CSS и размера
|
|
|
+
|
|
|
+```
|
|
|
+<canvas id="c"></canvas>
|
|
|
+```
|
|
|
+
|
|
|
+Этот холст по умолчанию имеет размер 300x150 CSS пикселей.
|
|
|
+
|
|
|
+В вебе рекомендуемый способ установить размер чего-либо - использовать CSS.
|
|
|
+
|
|
|
+Прим. переводчика: Далее идет слишком подробное описание банальностей.
|
|
|
+
|
|
|
+Давайте растянем холст на всю страницу:
|
|
|
+
|
|
|
+```
|
|
|
+<style>
|
|
|
+html, body {
|
|
|
+ margin: 0;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+#c {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ display: block;
|
|
|
+}
|
|
|
+</style>
|
|
|
+```
|
|
|
+
|
|
|
+HTML body по умолчанию имеет margin в 5 пикселей, поэтому установка в 0 удаляет margin.
|
|
|
+Установка высоты html и body на 100% заставляет их заполнить окно. В противном случае
|
|
|
+они будут такими же большими, как контент, который их наполняет.
|
|
|
+
|
|
|
+Далее мы говорим, что `id=c` элемент будет занимать 100% его контейнера,
|
|
|
+который в данном случае является телом документа.
|
|
|
+
|
|
|
+Наконец, мы установили его `display` режим на `block`. Режим отображения canvas
|
|
|
+по умолчанию `inline`. Inline элементы могут в конечном итоге добавить пробел к тому,
|
|
|
+что отображается. Устанавливая canvas'у `display: block` эта проблема исчезает.
|
|
|
+
|
|
|
+Вот результат
|
|
|
+
|
|
|
+{{{example url="../threejs-responsive-no-resize.html" }}}
|
|
|
+
|
|
|
+Вы можете видеть, что холст сейчас заполняет страницу, но есть 2 проблемы.
|
|
|
+1. Кубы растягиваются. Они не кубики, они больше похожи на прямоугольные параллелепипеды.
|
|
|
+Слишком высокие или слишком широкие. Откройте пример в его отдельном окне и измените
|
|
|
+его размер. Вы увидите, как кубы растягиваются то вширь, то ввысь.
|
|
|
+
|
|
|
+<img src="../resources/images/resize-incorrect-aspect.png" width="407" class="threejs_center">
|
|
|
+
|
|
|
+2. Они выглядят пиксельно и размыто.
|
|
|
+Растяните окно больше, и вы действительно увидите проблему.
|
|
|
+
|
|
|
+<img src="../resources/images/resize-low-res.png" class="threejs_center">
|
|
|
+
|
|
|
+Давайте сначала исправим проблему растяжения. Для этого нам нужно установить соотношение
|
|
|
+сторон (aspect) камеры в соответствии с размером холста.
|
|
|
+Мы можем сделать это, задав холсту свойства
|
|
|
+`clientWidth` и `clientHeight`.
|
|
|
+
|
|
|
+Мы обновим наш цикл отрисовки следующим образом
|
|
|
+
|
|
|
+```
|
|
|
+function render(time) {
|
|
|
+ time *= 0.001;
|
|
|
+
|
|
|
++ const canvas = renderer.domElement;
|
|
|
++ camera.aspect = client.clientWidth / client.clientHeight;
|
|
|
++ camera.updateProjectionMatrix();
|
|
|
+
|
|
|
+ ...
|
|
|
+```
|
|
|
+
|
|
|
+Теперь кубики должны перестать быть искаженными.
|
|
|
+
|
|
|
+{{{example url="../threejs-responsive-update-camera.html" }}}
|
|
|
+
|
|
|
+Откройте пример в отдельном окне и измените размер окна, и вы увидите, что кубы больше
|
|
|
+не растянуты по высоте или ширине. Они остаются правильными, независимо от размера окна.
|
|
|
+
|
|
|
+<img src="../resources/images/resize-correct-aspect.png" width="407" class="threejs_center">
|
|
|
+
|
|
|
+Теперь давайте исправим пиксильность.
|
|
|
+
|
|
|
+Canvas элемент имеет 2 размера. Один размер - это размер холста,
|
|
|
+отображаемый на странице. Это то, что мы устанавливаем с помощью CSS.
|
|
|
+Другой размер - это количество пикселей на холсте.
|
|
|
+Это ничем не отличается от изображения. Например, у нас может быть
|
|
|
+изображение размером 128x64 пикселей, а с помощью css мы можем отобразить
|
|
|
+как 400x200 пикселей.
|
|
|
+
|
|
|
+```
|
|
|
+<img src="some128x64image.jpg" style="width:400px; height:200px">
|
|
|
+```
|
|
|
+
|
|
|
+Внутренний размер холста, его разрешение часто называют размером рисованного
|
|
|
+буфера (drawingbuffer size). В three.js мы можем установить размер буфера
|
|
|
+рисования холста, вызывая `renderer.setSize`. Какой размер мы должны выбрать?
|
|
|
+Самый очевидный ответ - "тот же размер, что отображается на холсте".
|
|
|
+Опять же, чтобы сделать это, мы можем посмотреть на `clientWidth` и `clientHeight`
|
|
|
+свойства.
|
|
|
+
|
|
|
+Давайте напишем функцию, которая проверяет, совпадает ли размер рисованного буфера
|
|
|
+с размером, на котором он отображается, и, если это так, зададим холсту этот размер.
|
|
|
+
|
|
|
+```
|
|
|
+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;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+Обратите внимание, что мы проверяем, нужно ли изменять размер холста.
|
|
|
+Изменение размера холста - интересная часть спецификации холста,
|
|
|
+и лучше не устанавливать тот же размер, если он уже соответствует желаемому.
|
|
|
+
|
|
|
+Как только мы узнаем, нужно ли нам изменить размер или нет, мы вызываем
|
|
|
+`renderer.setSize` и передаем новую ширину и высоту. Важно передать `false` в конце.
|
|
|
+`render.setSize` по умолчанию устанавливает размер CSS холста, но это не то,
|
|
|
+что нам нужно. Мы хотим, чтобы браузер продолжал работать так же, как и
|
|
|
+для всех других элементов, то есть использовать CSS для определения размера
|
|
|
+отображения элемента. Мы не хотим, чтобы холсты, используемые three.js,
|
|
|
+отличались от других элементов.
|
|
|
+
|
|
|
+Обратите внимание, что наша функция возвращает true, если размер холста был изменен.
|
|
|
+Мы можем использовать это, чтобы проверить, есть ли другие вещи, которые мы
|
|
|
+должны обновить. Давайте изменим наш цикл отрисовки, чтобы использовать
|
|
|
+новую функцию.
|
|
|
+
|
|
|
+```
|
|
|
+function render(time) {
|
|
|
+ time *= 0.001;
|
|
|
+
|
|
|
++ if (resizeRendererToDisplaySize(renderer)) {
|
|
|
++ const canvas = renderer.domElement;
|
|
|
++ camera.aspect = client.clientWidth / client.clientHeight;
|
|
|
++ camera.updateProjectionMatrix();
|
|
|
++ }
|
|
|
+
|
|
|
+ ...
|
|
|
+```
|
|
|
+
|
|
|
+Поскольку apsect будет меняться только в случае изменения размера холста, мы
|
|
|
+устанавливаем соотношение сторон камеры, только если `resizeRendererToDisplaySize`
|
|
|
+вернёт `true`.
|
|
|
+
|
|
|
+{{{example url="../threejs-responsive.html" }}}
|
|
|
+
|
|
|
+Теперь он должен отображаться с разрешением,
|
|
|
+соответствующим размеру изображения на холсте.
|
|
|
+
|
|
|
+Чтобы сделать так, чтобы CSS позволял обрабатывать изменение размера,
|
|
|
+давайте возьмем наш код и поместим его в [отдельный `.js` файл](../../resources/threejs-responsive.js).
|
|
|
+Вот еще несколько примеров, где мы позволяем CSS выбирать размер, без написания кода.
|
|
|
+
|
|
|
+Давайте поместим наши кубики в середине абзаца текста.
|
|
|
+
|
|
|
+{{{example url="../threejs-responsive-paragraph.html" startPane="html" }}}
|
|
|
+
|
|
|
+и вот наш тот же код, используемый в макете стиля редактора,
|
|
|
+где область управления справа может менять размер.
|
|
|
+
|
|
|
+{{{example url="../threejs-responsive-editor.html" startPane="html" }}}
|
|
|
+
|
|
|
+Важная часть, на которую следует обратить внимание - отсутствие изменений прежнего кода.
|
|
|
+Только HTML и CSS изменились.
|
|
|
+
|
|
|
+## Обработка дисплеев HD-DPI
|
|
|
+
|
|
|
+HD-DPI - дисплеи с высокой плотностью точек на дюйм. Сейчас они у большинства
|
|
|
+компьютеров Mac, многих компьютеров с Windows, а также почти всех смартфонов.
|
|
|
+
|
|
|
+То, как это работает в браузере, заключается в том, что они используют
|
|
|
+пиксели CSS для установки размеров, которые должны быть одинаковыми,
|
|
|
+независимо от того, насколько высоким является разрешение дисплея.
|
|
|
+Браузер будет просто отображать текст с большей детализацией, но с
|
|
|
+таким же физическим размером.
|
|
|
+
|
|
|
+Существуют различные способы обработки HD-DPI с помощью three.js.
|
|
|
+
|
|
|
+Первый - просто не делать ничего особенного. Это, пожалуй,
|
|
|
+самый распространенный. Рендеринг 3D-графики занимает много
|
|
|
+вычислительной мощности графического процессора. Мобильные
|
|
|
+графические процессоры имеют меньшую мощность, чем настольные
|
|
|
+компьютеры, по крайней мере на 2018 год, и все же мобильные
|
|
|
+телефоны часто имеют дисплеи с очень высоким разрешением.
|
|
|
+Нынешние топовые телефоны имеют соотношение HD-DPI 3x, означающее,
|
|
|
+что для каждого пикселя с дисплея без HD-DPI эти телефоны имеют 9
|
|
|
+пикселей. Это означает, что они должны сделать 9-кратный рендеринг.
|
|
|
+
|
|
|
+Вычисление 9x пикселей - большая работа, поэтому, если мы просто
|
|
|
+оставим код таким, какой он есть, мы вычислим 1x пикселей,
|
|
|
+а браузер просто нарисует его в 3x размере (3x на 3x = 9x пикселей).
|
|
|
+
|
|
|
+Для любого тяжелого приложения three.js это, вероятно, то, что вам
|
|
|
+нужно, иначе вы, вероятно, получите медленную частоту кадров (FPS).
|
|
|
+
|
|
|
+Тем не менее, если вы действительно хотите рендерить с разрешением
|
|
|
+устройства, есть три способа сделать это в three.js.
|
|
|
+
|
|
|
+Один из них заключается в том, чтобы сообщить Three.js множитель
|
|
|
+разрешения, используя `renderer.setPixelRatio`.
|
|
|
+Вы спрашиваете браузер, каков множитель пикселей CSS
|
|
|
+для пикселей устройства, и передаете его в three.js.
|
|
|
+
|
|
|
+ renderer.setPixelRatio(window.devicePixelRatio);
|
|
|
+
|
|
|
+После этого любые вызовы `renderer.setSize` будут магически
|
|
|
+использовать размер, который вы запрашиваете,
|
|
|
+умноженный на любое количество пикселей, которое вы передали.
|
|
|
+
|
|
|
+Другой способ - сделать это самостоятельно, когда вы измените размер холста.
|
|
|
+
|
|
|
+```
|
|
|
+ function resizeRendererToDisplaySize(renderer) {
|
|
|
+ const canvas = renderer.domElement;
|
|
|
+ const pixelRatio = window.devicePixelRatio;
|
|
|
+ const width = canvas.clientWidth * pixelRatio;
|
|
|
+ const height = canvas.clientHeight * pixelRatio;
|
|
|
+ const needResize = canvas.width !== width || canvas.height !== height;
|
|
|
+ if (needResize) {
|
|
|
+ renderer.setSize(width, height, false);
|
|
|
+ }
|
|
|
+ return needResize;
|
|
|
+ }
|
|
|
+```
|
|
|
+
|
|
|
+Я предпочитаю этот второй способ. Зачем? Потому что это означает, что я
|
|
|
+получаю то, что я прошу. При использовании three.js существует много случаев,
|
|
|
+когда нам нужно знать фактический размер canvas's drawingBuffer. Например,
|
|
|
+при создании фильтра пост-обработки, или если мы создаем шейдер,
|
|
|
+который получает доступ `gl_FragCoord` и прочее... Делая это самостоятельно,
|
|
|
+мы всегда знаем, что используемый размер - это размер,
|
|
|
+который мы запрашивали. Не существует особого случая,
|
|
|
+когда магия происходит за кулисами.
|
|
|
+
|
|
|
+Вот пример использования кода выше.
|
|
|
+
|
|
|
+{{{example url="../threejs-responsive-hd-dpi.html" }}}
|
|
|
+
|
|
|
+Может быть трудно увидеть разницу, но если у вас есть дисплей HD-DPI
|
|
|
+и вы сравниваете этот образец с приведенными выше,
|
|
|
+вы должны заметить, что края более четкие.
|
|
|
+
|
|
|
+Эта статья охватывает очень основную, но фундаментальную тему. Далее давайте быстро
|
|
|
+[пройдемся по основным примитивам, которые предоставляет three.js](threejs-primitives.html).
|
|
|
+
|