Сказ о двух вьюпортах — часть вторая

Наконец дошли руки до второй части статьи Питера-Пауля Коха (Peter-Paul Koch) A tale of two viewports — part two. Далее, собственно, перевод.

В этом мини-цикле статей я объясню, как работают вьюпорт и ширина различных важных элементов страницы, к примеру, таких как <html> элемент, а также, как они соотносятся с окном браузера и экраном монитора.

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

Ссылки по теме:

Большое спасибо Grace Kloba (Google), David Storey и Anne van Kesteren (Opera), Mike O’Malley (Microsoft), Kartikaya Gupta и George Staikos (RIM), а также Mark Finkle (Mozilla) за просмотр предыдущих версий этой части и обеспечением меня ценной обратной связью.

Проблема мобильных браузеров

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

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

Хотя, промежуточное звено из планшетных устройств, таких как iPad или устройств на базе HP WebOS и заполняет пробел между настольными и мобильными устройствами, но от этого основная проблема не меняется. Сайты должны работать и на мобильных устройствах, поэтому мы должны научить их отображаться правильно на маленьких экранах.

Наиболее важные проблемы сконцентрированы на CSS, особенно размеры области просмотра. Если бы мы скопировали десктопную модель один в один, наш CSS начал бы жутко сбоить.

Давайте вернемся к нашей колонке шириной в 10%. Если бы мобильные браузеры действовали также как и настольные, они, в большинстве случаев, сделали бы этот элемент шириной в 40 пикселей, чего было бы невероятно мало. Ваша резиновая раскладка выглядела бы ужасно сжатой.

Одним из путей решения этой проблемы является создание специальной версии сайта для мобильных браузеров. Помимо фундаментального вопроса: «Должны ли вы вообще это делать?», на практике проблема состоит в том, что только немногие владельцы сайтов в достаточной мере осведомлены о специфике поддержки мобильных устройств.

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

Два вьюпорта

Итак, вьюпорт слишком узок, чтобы служить базой для вашей CSS-раскладки. Очевидное решение — сделать вьюпорт шире. Однако, это требует разделения понятия вьюпорта на: визуальный вьюпорт и вьюпорт страницы.

Лучше всего базовую концепцию объясняет Джордж Камминс (George Cummins) здесь на Stack Overflow:

Представьте вьюпорт страницы как большое изображение с неизменными размерами и формой. Теперь представьте меньшее по размеру окошко, через которое вы смотрите на это большое изображение. Ваше окошко окружено непроницаемым материалом, который не дает вам увидеть большое изображение полностью, за исключением отдельных его частей. Часть большого изображения, которая видна через окошко, и есть визуальный вьюпорт. Вы можете отойти с окошком от большого изображения (уменьшение масштаба), чтобы увидеть сразу все изображение, или вы можете подойти поближе (увеличение масштаба), чтобы рассмотреть только часть изображения. Вы также можете менять ориентацию окошка (на портретную или ландшафтную, прим. перев.), но размер и форма большого изображения (вьюпорта страницы) всегда остается неизменной.

Посмотрите также это объяснение, приведенное Крисом (Chris)

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

Визуальный вьюпорт

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

Таким образом, <html>-элемент изначально принимает ширину вьюпорта страницы и ваш CSS интерпретируется как если бы экран был намного шире экрана телефона. Это гарантирует, что раскладка вашего сайта будет вести себя так, как и в десктопном браузере.

Какой же ширины вьюпорт страницы? Это зависит от браузера. Safari iPhone использует 980px, Opera — 850px, Android WebKit — 800px, а IE — 974px.

Некоторые браузеры ведут себя по-особенному:

Symbian WebKit пытается приравнять вьюпорт страницы к визуальному вьюпорту и да, это означает, что элементы с процентной шириной могут вести себя странным образом. Однако, если страница не умещается в визуальном вьюпорте из-за абсолютных величин (т.е. размеры макета указаны не в относительных или процентных, а в абсолютных величинах, прим. перев.) браузер растягивает вьюпорт страницы до 850-ти пикселей максимум.

Samsung WebKit (вуаля!) делает вьюпорт страницы равным по ширине самого широкого элемента.

На BlackBerry при масштабе в 100% вьюпорт страницы равен визуальному вьюпорту. Это неизменно.

Масштабирование

Оба вьюпорта измеряются в CSS-пикселях. Но во время масштабирования размеры визуального вьюпорта меняются (если вы увеличиваете масштаб, на экране умещается меньшее количество CSS-пикселей), в то время как размеры вьюпорта страницы остаются неизменными. (Если бы это было не так, браузер постоянно совершал бы reflow страницы (перестраивание разметки страницы, прим. перев.) из-за пересчета процентной ширины.)

Осмысление вьюпорта страницы

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

Дело в том, что размеры вьюпорта страницы браузеров полностью совпадают с экраном при максимально уменьшенном масштабе и поэтому равны визуальному вьюпрту.

визуальный вьюпорт равен вьюпорту страницы при наименьшем масштабе

Таким образом, ширина и высота вьюпорта страницы равна всему тому, что бы ни было отображено на экране при наименьшем масштабе. При увеличении масштаба пользователем эти размеры остаеются неизменными.

вьюпорт страницы

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

визуальный вьюпорт равен вьюпорту страницы

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

вьюпорт страницы при ландшафтной ориентации экрана

Измерения вьюпорта страницы

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

Свойства document.documentElement.clientWidth и -Height содержат размеры вьюпорта страницы.

Свойства document.documentElement.clientWidth/Height

  • Значение: размеры вьюпорта страницы
  • Измеряется в: CSS-пикселях
  • Полностью поддерживается: Opera, iPhone, Android, Symbian, Bolt, MicroB, Skyfire, Obigo
  • Проблемы:
    • с размерами визуального вьюпорта в Iris
    • Samsung WebKit сообщает корректные значения только если на странице задан тег <meta viewport>, иначе значением будут размеры <html>-элемента
    • в Firefox возвращаются размеры экрана в пикселях устройства
    • IE возвращает 1024×768. Однако эта информация хранится в document.body.clientWidth/Height, это согласуется с десктопным IE6
    • в браузере NetFront значения корректны только при 100% масштабе
    • в Symbian WebKit 1 (старше устройств S60v3) не поддерживаются эти свойства
  • Не поддерживается: BlackBerry

Размеры вьюпорта страницы

Ориентация экрана имеет значение для высоты, для ширины она не важна.

Размеры вьюпорта страницы

Измерения визуального вьюпорта

Что касается визуального вьюпорта, он измеряется с помощью свойства window.innerWidth/Height. Очевидно, что размеры изменяются, когда пользователь уменьшает или увеличивает масштаб, поскольку в экран вписывается большее или меньшее количество CSS-пикселей.

Свойства window.innerWidth/Height

  • Значение: размеры визуального вьюпорта
  • Измеряется в: CSS-пикселях
  • Полностью поддерживается: iPhone, Symbian, BlackBerry
  • Проблемы:
    • Opera и Firefox возвращают ширину экрана в пикселях устройства
    • Android, Bolt, MicroB, and NetFront возвращают размеры вьюпорта страницы в CSS-пикселях
    • IE не поддерживает, но с помощью document.documentElement.offsetWidth/Height можно узнать размеры визуального вьюпорта
    • Samsung WebKit также в зависимости от наличия либо отсутствия метатега <meta viewport>, сообщает либо размеры вьюпорта страницы либо <html>-элемента соответственно
    • в браузере NetFront значения корректны только при 100% масштабе
    • Не все ясно с: Iris, Skyfire, Obigo

Размеры визуального вьюпорта

К несчастью, существуют проблемы с несовместимостью, немало браузеров все еще должны добавить поддержку измерения визуального вьюпорта. До сих пор ни один из браузеров не хранит эти измерения в какой-то другой паре свойств, таким образом, я полагаю, window.innerWidth/Height является стандартом, хотя и плохо поддерживаемым.

Экран

Как и в десктопных браузерах, screen.width/height отдает размер экрана в пикселях устройства. Как и в случае с десктопными браузерами, вам, как веб-разработчику эта информация никогда не понадобится. Вам не важен физический размер экрана, важно количество CSS-пикселей, которое на нем умещается.

Свойства screen.width и screen.height

  • Значение: размер экрана
  • Измеряется в: пикселях устройства
  • Полностью поддерживается: Opera Mini, Android, Symbian, Iris, Firefox, MicroB, IE, BlackBerry
  • Проблемы:
    • Opera Mobile на Windows Mobile возвращают только размеры ландшафтной ориентации экрана. Opera Mobile на S60 возвращает все верно
    • Samsung WebKit также в зависимости от наличия либо отсутствия метатега <meta viewport>, сообщает либо размеры вьюпорта страницы либо <html>-элемента соответственно
    • iPhone и Obigo отдают размеры только портретной ориентации экрана
    • NetFront возвращает только размеры ландшафтной ориентации экрана
    • Не все ясно с: Bolt, Skyfire

Свойства screen.width и screen.height

Уровень масштабирования

Непосредственно вычислить уровень масштабирования не представляется возможным, но вы можете его получить разделив screen.width на window.innerWidth. Конечно же это работает, только если эти два свойства гарантированно поддерживаются.

К счастью, уровень масштабирования не очень важен. На самом деле вам нужно знать сколько CSS-пикселей в данный момент умещается на экране. И вы можете получить эту информацию с помощью window.innerWidth, если это свойство корректно поддерживается.

Величина смещения прокручиваемой области страницы

Также вам необходимо знать текущее положение визуального вьюпорта по отношению к вьюпорту страницы. Это величина смещения прокручиваемой области страницы. И также, как и в десктопных браузерах, ее значение хранится в window.pageX/YOffset.

window.pageX/YOffset

  • Значение: величина смещения прокручиваемой области страницы, то же, что и текущее положение визуального вьюпорта по отношению к вьюпорту страницы
  • Измеряется в: CSS-пикселях
  • Полностью поддерживается: iPhone, Android, Symbian, Iris, MicroB, Skyfire, Obigo
  • Проблемы:
    • Opera, Bolt, Firefox, и NetFront всегда возвращают 0.
    • Samsung WebKit возвращает корректные значения только если на странице присутствует метатег <meta viewport>
  • Не поддерживается в: IE, BlackBerry. IE хранит значения в document.documentElement.scrollLeft/Top

величина смещения прокручиваемой области страницы

<html>-элемент

Как и в случае с десктопными браузерами, document.documentElement.offsetWidth/Height дает общий размер <html>-элемента в CSS-пикселях.

document.documentElement.offsetWidth/Height

  • Значение: размер <html>-элемента
  • Измеряется в: CSS-пикселях
  • Полностью поддерживается: Opera, iPhone, Android, Symbian, Samsung, Iris, Bolt, Firefox, MicroB, Skyfire, BlackBerry, Obigo
  • Проблемы:
    • Значения в NetFront корректны только при масштабе в 100%
    • IE использует эту пару свойств для хранения размеров визуального вьюпорта. Для IE смотрите document.body. clientWidth/Height для получения корректных значений

размер <html>-элемента

Медиа-запросы

Медиа-запросы работают здесь также как и в десктопных браузерах. width/height относится к вьюпорту страницы и измеряется в CSS-пикселях, device-width/height относится к экрану и измеряется в пикселях устройства.

Другими словами, width/height отражают значения document.documentElement.clientWidth/Height, тогда как device-width/height отражают значения screen.width/height. (Так происходит фактически во всех браузерах, даже если отражаемые значения некорректны.)

document.documentElement.offsetWidth/Height

  • Значение: измерение ширины <html>-элемента (в CSS-пикселях) или ширины экрана устройства (в пикселях устройства)
  • Полностью поддерживается: Opera, iPhone, Android, Symbian, Samsung, Iris, Bolt, Firefox, MicroB
  • Не поддерживается в: Skyfire, IE, BlackBerry, NetFront, Obigo
  • Примечание: в данном случае я проверял, берут ли браузеры данные из корректных пар свойств. Вопрос о том, будут ли эти свойства возвращать корректные значения — не входил в условия теста.

width/height и device-width/height

Измерения чего в данном случае наиболее полезны для веб-разработчиков? Дело в том, что я не знаю.

Я начал размышлять о том, что device-width более важна, поскольку дает нам некоторую информацию об устройстве, которую мы, возможно могли бы использовать. К примеру, вы могли бы изменять ширину вашей раскладки, подогнав ее под ширину устройства. Однако, вы можете это сделать и с помощью метатега <meta viewport>. Совершенно необязательно использовать для этого device-width медиа-запрос.

Тогда может быть важнее медиа-запрос width? Возможно. Это дает некоторое представление относительно того, какую ширину вебсайта производитель браузера считает оптимальной для данного устройства. Но это допольно неопределенно, да и медиа-запрос width на самом деле не дает нам никакой другой информации.

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

Как-то так.

Координаты событий

Координаты событий здесь работают примерно также как и в десктопных браузерах. К несчастью, из двенадцати протестированных браузеров, только в двух, Symbian WebKit и Iris, удалось получить все три пары координат верно. Во всех остальных браузерах существуют более-менее серьезные проблемы.

pageX/Y по-прежнему относится к странице и измеряется в CSS-пикселях являясь, безусловно, самой полезной из трех пар свойств, как впрочем и в десктопных браузерах.

Координаты событий

  • Значение: см. основной текст статьи
  • Измеряется см. основной текст статьи
  • Полностью поддерживается: Symbian, Iris
  • Проблемы:
    • Opera Mobile отдает значения pageX/Y во всех трех парах свойств, но иногда приверает, если вы слишком много прокрутите
    • В iPhone, Firefox и BlackBerry значения clientX/Y равны значениям pageX/Y
    • В Android и MicroB значения screenX/Y равны значениям clientX/Y (в CSS-пикселях, другими словами)
    • В Firefox значения screenX/Y неверны.
    • IE, BlackBerry и Obigo не поддерживают pageX/Y
    • В NetFront значения всех трех пар свойств равны значениям screenX/Y
    • В Obigo clientX/Y — это screenX/Y
    • Samsung WebKit всегда возвращает pageX/Y
  • Не тестировано в: Opera Mini, Bolt, Skyfire

event.pageX/Y

clientX/Y относится к визуальному вьюпорту и измеряется в CSS-пикселях. Это имеет смысл, хотя я не совсем определился, для чего именно.

screenX/Y относится к экрану и измеряется в пикселях устройства. Несомненно, screenX/Y относится к тому же, к чему и clientX/Y, но пиксели устройства для нас бесполезны. Поэтому нам не стоит беспокоиться насчет screenX/Y. Эта пара свойств совершенно бесполезна, также как и в десктопных браузерах.

event.screenX/Y и event.clientX/Y

Meta viewport

Meta viewport

  • Значение: устанавливает ширину вьюпорта страницы
  • Измеряется в CSS-пикселях
  • Полностью поддерживается: Opera Mobile, iPhone, Android, Iris, IE, BlackBerry, Obigo
  • Не поддерживается: Opera Mini, Symbian, Bolt, Firefox, MicroB, NetFront
  • Проблемы:
    • Skyfire смог обработать мою страницу.
    • Если на странице присутствует <meta viewport> в Samsung WebKit некоторые свойства изменяют значение
    • Opera Mobile, iPhone, Samsung и BlackBerry не позволяют пользователю уменьшить масштаб

Напоследок, давайте обсудим <meta name="viewport" content="width=320" viewport>. По происхождению, — это расширение компании Apple, но при этом, скопированное многими браузерами. Оно предназначено для изменения вьюпорта страницы. Для понимания необходимости этой конструкции, давайте вернемся немного назад.

Предположим, вы сверстали простую страничку и не определили ее элементам ширину. Сейчас они растянуты на 100% вьюпорта страницы. Большинство браузеров уменьшат масштаб, чтобы уместить весь вьюпорт страницы на экране, в итоге получится вот такой эффект:

Браузер уменьшил масштаб, чтобы уместить страницу на экране

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

Текст трудно читается при увеличении масштаба

(Значительным исключением здесь является Android WebKit, который на самом деле уменьшает размер содержащих текст элементов, таким образом, чтобы они уместились на экране. Это великолепно, и я думаю все остальные браузеры должны скопировать подобное поведение. Я напишу об этом позже)

Теперь вы можете попробовать установить <html>-элементу ширину в 320 пикселей (html {width: 320px}). Теперь <html>-элемент сжимается, а с ним и все остальные элементы, которые теперь отсчитывают свои 100% от 320 пикселей. Это работает, когда пользователь увеличивает масштаб, но не вначале, когда он сталкивается со страницей в уменьшенном масштабе, на которой почти ничего нет.

Изначально страница кажется почти пустой

Именно для того, чтобы обойти эту проблему, компания Apple изобрела тег meta viewport. Когда вы определяете <meta name="viewport" content="width=320" viewport>, вы устанавливаете ширину вьюпорта страницы равную 320 пикселям. Теперь изначально страница выглядит корректно.

изначально страница выглядит корректно

Вы можете устанавливать любую ширину вьюпорта страницы, какую захотите, в том числе device-width. Эта пара свойств берет screen.width (в пикселях устройства) и в соответствии с этим значением изменяет вьюпорт страницы.

Однако, здесь есть некоторая загвоздка. Иногда формальная screen.width не имеет особого смысла, потому как число пикселей получается немного больше. К примеру, у Nexus One наминально ширина экрана составляет 480 пикселей, но инженеры Google решили, что отдавать вьюпорту страницы ширину в 480 пикселей при использовании device-width — слишком много. И сократили ее до 2/3, таким образом device-width дает вам ширину в 320 пикселей, также как и на iPhone.

По слухам, новый iPhone будет выделяться еще бо́льшим количеством пикселей (что вовсе не обязательно эквивалентно большему экрану!), я не удевлюсь, если Apple скопирует подобное поведение. Возможно в конце концов device-width будет просто означать 320px.

Будущее исследование

На этом, на данный момент я завершаю свое исследование. В будущем будут рассмотрены, связанные с этой, следующие темы:

  • position: fixed. Фиксированные элементы, как нам известно, позиционируются относительно вьюпорта. Но относительно которого? На самом деле я уже провел кое-какое исследование и в недалеком будущем его опубликую.
  • Остальные медиа-запросы: dpi, orientation, aspect-ratio. dpi — особенно катастрофическая область, не только потому, что все браузеры сообщают 96dpi, что обычно не так, но также потому, что я еще не совсем уверен, какое значение dpi должно интересовать веб-разработчиков.

  • Что происходит, когда элемент шире чем вьюпорт страницы/HTML? Скажите, вставлю ли я элемент шириной в 1500 пикселей в одну из моих тестовых страниц? Элемент вылезет за границы HTML-элемента (overflow: visible), но это будет значить, что фактический вьюпорт может стать шире вьюпорта страницы. Более того, Android (Nexus One) расширяет HTML-элемент, когда такое происходит. Хорошая ли это идея?

Комментарии

  1. ячмямс
    3 сентября 2012 в 16:10
    1

    Какой же ширины визуальный вьюпорт? Это зависит от браузера.

    Сравните с оригиналом:

    How wide is the layout viewport? That differs per browser.

    • Евгений Бескровный
      3 сентября 2012 в 19:52
      2

      Хм, сравнил еще раз, перевести по-другому все еще желания нет, смысл полностью передан. В разных браузерах, визуальный вьюпорт разнится. Что, собственно, подтверждается нижеследующим предложением. А вы бы как перевели?

      • Михаил
        2 августа 2013 в 19:25
        3

        Думаю, «ячмямс» указал на ошибку в перевод термина «layout viewport», а не фразы «That differs per browser». В данном абзаце Вы, видимо по ошибке, перевели его как «визуальный вьюпорт», а не «вьюпорт страницы». Абзацем выше данный термин Вы перевели правильно (перевели фразу «are calculated relative to the layout viewport» как «расчитывается относительно вьюпорта страницы«).

  2. Андрей
    9 мая 2014 в 05:29
    5

    Что-то они, на мой взгляд, насчет измерения размеров визуальной области просмотра и области просмотра страницы напутали. Одни и те же значения получаются за исключением толщины линеек прокрутки. Да и какой вьюпорт может быть у страницы? Вьюпорт (область просмотра) может быть только у программы просмотра. Что-то намудрили. CSS привязывается не к вьюпорту, а к пикселям CSS. IMHO

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*

Правила форматирования