Three.js
April 6, 2022

Полноэкранный режим и изменение размера

Перед тем как начать:

В предыдущем уроке мы разобрались с тем, как можно настроить пользовательские элементы управление, а так же разобрались со встроенными элементами управления three.js. Если ты это пропустил, то советую вернуться!

Сейчас наш canvas имеет фиксированный размер 600x600. В этом уроке мы разберем с вами как настраивать его размер под экран пользователя, научимся делать так, чтобы он занимал все пространство и перестраивался при изменении размера окна, а так же узнаем как работает полноэкранный режим!

Код на начало урока вы можете найти ->тут<-

Запустив приложение вы должны увидеть следующее:

Подстраиваемся под viewport

Чтобы canvas идеально помещался во viewport, вместо использования фиксированных чисел в переменной размеров используйте window.innerWidth и window.innerHeight:

// ...

// Размеры
const sizes = {
    width: window.innerWidth,
    height: window.innerHeight,
};

// ...

Вы можете видеть, что canvas теперь имеет ширину и высоту экрана. К сожалению, есть белое поле и полоса прокрутки (попробуйте прокрутить, если вы не видите никакой полосы прокрутки).

Проблема в том, что все браузеры имеют стили по умолчанию.

Наш script.js уже связан с CSS-файлом в src/style.css. Это может показаться странным, если вы не привыкли к Webpack, но файл CSS импортируется непосредственно из script.js:

import './style.css'

Хорошей вещью, которую нужно сделать в первую очередь, было бы удалить любой тип отступов для всех элементов, используя селектор *:

* {
    margin: 0;
    padding: 0;
}

Затем мы можем зафиксировать canvas в левом верхнем углу, используя его класс canvas-js:

.canvas-js {
    position: fixed;
    top: 0;
    left: 0;
}

Вам не нужно указывать ширину или высоту canvas, потому что Three.js уже заботится об этом, когда вы вызываете метод renderer.setSize(...).

Можно еще исправить небольшую проблему нашего canvas. Возможно, вы заметили синий контур на нем при перетаскивании. В основном это происходит в последних версиях Chrome. Чтобы исправить это, мы можем просто добавить outline: none; на .canvas-js:

.canvas-js {
    position: fixed;
    top: 0;
    left: 0;
    outline: none;
}

А еще, если вы хотите удалить любой тип прокрутки даже на сенсорных экранах, вы можете добавить overflow: hidden как в html, так и в body:

html,
body {
    overflow: hidden;
}

Теперь вы можете наслаждаться своим WebGL во всей его красе. К сожалению, если вы измените размер окна, canvas не будет менять размер, и нам нужно с этим разобраться.

Обрабатываем изменение размера

Чтобы изменить размер canvas, нам сначала нужно знать, когда изменяется размер окна. Для этого вы можете навесить на событие изменения размера окна колбек:

window.addEventListener('resize', () => {
    console.log('window has been resized');
});

Теперь, когда мы запускаем функцию при изменении размера окна, нам нужно кое-что обновить в нашем коде.

Во-первых, мы должны обновить переменную размеров:

window.addEventListener('resize', () => {
    // Обновляем размеры
    sizes.width = window.innerWidth;
    sizes.height = window.innerHeight;
});

Во-вторых, мы должны обновить соотношение сторон камеры, изменив ее свойство aspect:

window.addEventListener('resize', () => {
    // ...

    // Обновляем соотношение сторон камеры
    camera.aspect = sizes.width / sizes.height;
});

Когда вы меняете свойства камеры, такие как aspect, вам также необходимо обновить матрицу проекции с помощью camera.updateProjectionMatrix(). Мы поговорим о матрицах позже:

window.addEventListener('resize', () => {
    // ...

    camera.updateProjectionMatrix();
});

Наконец, мы должны обновить renderer. Обновление renderer автоматически обновит ширину и высоту холста:

window.addEventListener('resize', () => {
    // ...

    // Обновляем renderer
    renderer.setSize(sizes.width, sizes.height);
});

Все вместе:

window.addEventListener('resize', () => {
    // Обновляем размеры
    sizes.width = window.innerWidth;
    sizes.height = window.innerHeight;

    // Обновляем соотношение сторон камеры
    camera.aspect = sizes.width / sizes.height;
    camera.updateProjectionMatrix();

    // Обновляем renderer
    renderer.setSize(sizes.width, sizes.height);
});

Теперь вы можете изменить размер окна по своему усмотрению, canvas должен закрывать область просмотра без какой-либо полосы прокрутки или переполнения.

Соотношение пикселей

Некоторые из вас могут увидеть своего рода размытый рендеринг и артефакты в форме лестниц по краям (называемые сглаживанием. Если вы это видите, то это потому, что вы тестируете на экране с соотношением пикселей больше 1.

Соотношение пикселей соответствует тому, сколько физических пикселей у вас есть на экране на одну единицу пикселя в программной части.

Немного истории

Несколько лет назад все экраны имели соотношение пикселей 1, и все было просто отлично. Но когда вы внимательно смотрели на свой экран, вы могли видеть эти пиксели, и это было ограничением для того, насколько точными могли быть изображения и насколько тонкими могли быть шрифты.

Компания, которая сделала для этого больше всего, была Apple. Apple увидела возможность и начала создавать экраны с соотношением пикселей 2, называемые retina. Сейчас это делают многие конструкторы, и вы можете видеть экраны с еще более высоким соотношением пикселей.

Хотя это хорошо сказывается на качестве изображения, соотношение пикселей 2 означает, что для рендеринга требуется в 4 раза больше пикселей. А соотношение пикселей 3 означает, что для рендеринга требуется в 9 раз больше пикселей.

И знаешь что? Самое высокое соотношение пикселей обычно наблюдается на самых слабых устройствах — мобильных телефонах.

Обрабатывать соотношение пикселей

Чтобы получить соотношение пикселей экрана, вы можете использовать window.devicePixelRatio, а чтобы обновить соотношение пикселей renderer, вам просто нужно вызвать renderer.setPixelRatio(...)

У вас может возникнуть соблазн просто отправить соотношение пикселей устройства этому методу, но в конечном итоге у вас возникнут проблемы с производительностью на устройствах с высоким соотношением пикселей.

Иметь соотношение пикселей больше 2 - это в основном маркетинг. Ваши глаза почти не увидят разницы между 2 и 3, но это создаст проблемы с производительностью и быстрее разрядит аккумулятор. Что вы можете сделать, так это ограничить соотношение пикселей до 2. Чтобы сделать это, вы можете использовать Math.min():

renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

Существуют методы получения уведомлений об изменении соотношения пикселей, но это касается только пользователей, имеющих несколько экранов с разным соотношением пикселей, и они обычно изменяют размер своего окна при переходе с одного экрана на другой. Вот почему мы просто добавим этот метод и в обратный вызов resize:

window.addEventListener('resize', () => {
    // Обновляем размеры
    sizes.width = window.innerWidth;
    sizes.height = window.innerHeight;

    // Обновляем соотношение сторон камеры
    camera.aspect = sizes.width / sizes.height;
    camera.updateProjectionMatrix();

    // Обновляем renderer
    renderer.setSize(sizes.width, sizes.height);
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
});

Обрабатывать полноэкранный режим

Теперь, когда у нас есть canvas, занимающий все доступное пространство с правильным соотношением пикселей, пришло время добавить поддержку полноэкранного режима.

Во-первых, нам нужно решить, какое действие вызовет полноэкранный режим. Это может быть кнопка HTML, но вместо этого мы собираемся использовать двойной щелчок.

Когда произойдет двойной щелчок, мы переключим полноэкранный режим — это означает, что если окно не находится в полноэкранном режиме, двойной щелчок включит полноэкранный режим, а если окно уже находится в полноэкранном режиме, двойной щелчок приведет к выходу из полноэкранного режима.

Для начала нам нужно обрабатывать событие двойного щелчка, и мы можем сделать это с помощью события dblclick:

window.addEventListener('dblclick', () => {
    console.log('double click');
});

Это событие будет работать в большинстве современных браузеров, за исключением Chrome Android:

Теперь, когда у нас есть наш обработчик, нам нужно 3 вещи:

  • Способ узнать, находится ли он уже в полноэкранном режиме
  • Способ перехода в полноэкранный режим
  • Способ выхода из полноэкранного режима

Чтобы узнать, находимся ли мы уже в полноэкранном режиме или нет, мы можем использовать document.fullscreenElement:

window.addEventListener('dblclick', () => {
    if(!document.fullscreenElement) {
        console.log('открыть fullscreen');
    } else {
        console.log('закрыть fullscreen');
    }
});

Метод запроса полноэкранного режима связан с элементом. Это потому, что вы можете выбрать, что будет отображаться в полноэкранном режиме. Это может быть целая страница, любой элемент DOM или canvas.

Мы будем использовать canvas и вызывать для него метод requestFullscreen():

window.addEventListener('dblclick', () => {
    if(!document.fullscreenElement) {
        canvas.requestFullscreen();
    } else {
        console.log('закрыть fullscreen');
    }
});

Способ выхода из полноэкранного режима доступен непосредственно в документе:

window.addEventListener('dblclick', () => {
    if(!document.fullscreenElement) {
        canvas.requestFullscreen();
    } else {
        document.exitFullscreen()
    }
});

Вы можете проверить результат, дважды щелкнув в любом месте, чтобы переключить полноэкранный режим. К сожалению, это не будет работать в Safari этот браузер не торопится официально поддерживать простые функции, такие как полноэкранный режим, и нам нужно использовать версии с префиксами, чтобы заставить его работать для document.fullscreenElement, canvas.requestFullscreen и document.exitFullscreen:

window.addEventListener('dblclick', () => {
    const fullscreenElement = document.fullscreenElement || document.webkitFullscreenElement;

    if(!fullscreenElement) {
        if(canvas.requestFullscreen) {
            canvas.requestFullscreen();
        }
        else if(canvas.webkitRequestFullscreen) {
            canvas.webkitRequestFullscreen();
        }
    } else {
        if(document.exitFullscreen) {
            document.exitFullscreen();
        }
        else if(document.webkitExitFullscreen) {
            document.webkitExitFullscreen();
        }
    }
});

Все должно работать нормально во всех современных браузерах.

На этом сегодня все, надеюсь вам это было полезно!

🤖 Чтобы не пропустить новые уроки подпишись на телеграм канал!

🚀 Код для данного урока вы можете найти тут.



Геометрия ->