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

I. Понятие viewport

Проще говоря, viewport на мобильном устройстве — это область на экране устройства, которую можно использовать для отображения нашей веб-страницы. Точнее, это часть браузера (или webview в приложении), используемая для отображения веб-страницы. Однако viewport не ограничивается размером видимой области браузера; он может быть больше или меньше видимой области браузера. По умолчанию, вообще говоря, viewport на мобильном устройстве больше видимой области браузера. Это связано с тем, что разрешение мобильных устройств относительно невелико по сравнению с настольными компьютерами. Поэтому, чтобы корректно отображать традиционные сайты, предназначенные для настольных браузеров, на мобильных устройствах, браузеры на мобильных устройствах устанавливают свой viewport по умолчанию равным 980px или 1024px (или другим значениям, определяемым самим устройством). Но следствием является то, что в браузере появится горизонтальная полоса прокрутки, потому что ширина видимой области браузера меньше ширины этого viewport по умолчанию. На следующем рисунке перечислены значения ширины viewport по умолчанию для браузеров на некоторых устройствах.


II. 1px в CSS не равен 1px устройства

В CSS мы обычно используем px в качестве единицы измерения. В настольных браузерах 1 CSS-пиксель часто соответствует 1 физическому пикселю на экране компьютера. Это может создавать иллюзию, что пиксели в CSS — это физические пиксели устройства. Но в действительности это не так. Пиксели в CSS — всего лишь абстрактная единица. На разных устройствах или в разных средах количество физических пикселей устройства, которым соответствует 1px в CSS, различается. На веб-страницах, предназначенных для настольных браузеров, нам не нужно об этом беспокоиться, но на мобильных устройствах мы должны понимать этот момент. На более ранних мобильных устройствах плотность пикселей экрана была относительно низкой, например у iPhone 3, разрешение которого составляло 320x480. На iPhone 3 один CSS-пиксель действительно был равен одному физическому пикселю экрана. Позже, с развитием технологий, плотность пикселей экрана мобильных устройств становилась всё выше и выше. Начиная с iPhone 4, Apple представила так называемый экран Retina, разрешение которого было удвоено до 640x960, но размер экрана не изменился. Это означает, что на экране того же размера пикселей стало вдвое больше. В этом случае один CSS-пиксель равен двум физическим пикселям. То же самое верно и для мобильных устройств других брендов. Например, Android-устройства можно разделить на разные уровни, такие как ldpi, mdpi, hdpi, xhdpi, в зависимости от плотности пикселей экрана, и разрешения также бывают разными. То, скольким физическим пикселям экрана соответствует один CSS-пиксель на Android-устройстве, также зависит от устройства, и определённого ответа нет.

Ещё один фактор, который также вызывает изменения px в CSS, — это масштабирование пользователем. Например, когда пользователь увеличивает страницу на 100%, количество физических пикселей, которое представляет 1px в CSS, также удваивается; и наоборот, когда страница уменьшается вдвое, количество физических пикселей, которое представляет 1px в CSS, также уменьшается вдвое. Этот момент будет рассмотрен позже в статье.

В мобильных браузерах и некоторых настольных браузерах у объекта window есть свойство devicePixelRatio. Его официальное определение: отношение физических пикселей устройства к независимым от устройства пикселям, то есть devicePixelRatio = физические пиксели / независимые пиксели. px в CSS можно рассматривать как независимый от устройства пиксель, поэтому через devicePixelRatio мы можем узнать, сколько физических пикселей представляет один CSS-пиксель на этом устройстве. Например, на iPhone с экраном Retina значение devicePixelRatio равно 2, то есть 1 CSS-пиксель эквивалентен 2 физическим пикселям. Однако следует отметить, что devicePixelRatio всё ещё имеет некоторые проблемы совместимости в разных браузерах, поэтому пока мы не можем полностью на него полагаться. Для конкретных случаев можно посмотретьэту статью。

Результаты тестирования devicePixelRatio:


III. Теория PPK о трёх вьюпортах

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

Во-первых, браузер на мобильном устройстве считает, что должен позволять всем сайтам отображаться нормально, даже тем, которые не предназначены для мобильных устройств. Но если видимая область браузера используется как viewport, то из-за того, что экраны мобильных устройств не очень широкие, сайты, предназначенные для настольных браузеров, при отображении на мобильных устройствах неизбежно будут сжаты из-за слишком узкого viewport, и даже макет может развалиться. Возможно, кто-то спросит: разве сейчас нет множества телефонов с очень большим разрешением, например 768x1024 или 1080x1920? Такие телефоны должны без проблем отображать сайты, предназначенные для настольных браузеров, не так ли? Как мы говорили ранее, 1px в CSS не соответствует 1px на экране. Чем больше ваше разрешение, тем больше физических пикселей представляет 1px в CSS и тем больше значение devicePixelRatio. Это легко понять, потому что ваше разрешение увеличилось, но размер экрана увеличился не сильно. Нужно сделать так, чтобы 1px в CSS представлял больше физических пикселей, чтобы размер объекта в 1px на экране был похож на размер на устройствах с низким разрешением; иначе он будет слишком маленьким, чтобы его можно было ясно увидеть. Поэтому на устройстве вроде 1080x1920 по умолчанию вам, возможно, нужно всего лишь задать ширину div чуть больше 300px (в зависимости от значения devicePixelRatio), чтобы получить полную ширину экрана. Вернёмся к теме: если видимая область браузера на мобильном устройстве задана как viewport, некоторые сайты будут отображаться неправильно, потому что viewport слишком узкий. Поэтому эти браузеры решают по умолчанию задать для viewport более широкое значение, например 980px, чтобы даже сайты, предназначенные для настольных устройств, могли нормально отображаться в мобильных браузерах. ppk называет этот viewport, устанавливаемый браузером по умолчанию, layout viewport. Ширину этого layout viewport можно получить через document.documentElement.clientWidth.

Однако ширина вьюпорта макета больше ширины видимой области браузера, поэтому нам также нужен вьюпорт, представляющий размер видимой области браузера. ppk называет этот вьюпорт визуальным вьюпортом. Ширину визуального вьюпорта можно получить через window.innerWidth, но её нельзя корректно получить в Android 2, Opera Mini и UC 8.

Теперь у нас уже есть два вьюпорта: вьюпорт макета и визуальный вьюпорт. Но браузер считает, что этого недостаточно, потому что всё больше и больше сайтов разрабатываются отдельно для мобильных устройств, поэтому должен быть также вьюпорт, который может идеально адаптироваться к мобильным устройствам. Так называемая идеальная адаптация означает: во-первых, без необходимости масштабирования пользователем или горизонтальной прокрутки можно нормально просматривать всё содержимое сайта; во-вторых, отображаемый размер текста подходящий. Например, абзац текста размером 14px не будет слишком маленьким, чтобы его нельзя было чётко прочитать на экране с высокой плотностью пикселей. В идеале, независимо от плотности экрана или разрешения, размер этого текста 14px должен быть примерно одинаковым. Конечно, не только текст, но и другие элементы, например изображения, подчиняются тому же принципу. ppk называет этот вьюпорт идеальным вьюпортом, который является третьим вьюпортом — идеальным вьюпортом мобильных устройств.

Идеальный вьюпорт не имеет фиксированного размера; у разных устройств разные идеальные вьюпорты. У всех iPhone ширина идеального вьюпорта равна 320px, независимо от того, равна ли ширина их экрана 320 или 640. То есть в iPhone 320px в CSS представляет ширину экрана iPhone.

Но с Android-устройствами всё сложнее. Бывают 320px, 360px, 384px и так далее. Что касается идеальной ширины вьюпорта разных устройств, можно зайти наhttp://viewportsizes.comи посмотреть; там собраны идеальные ширины многих устройств.

Подведём итог ещё раз:ppk делит viewport на мобильных устройствах на три типа: layout viewport, visual viewport и ideal viewport. Среди них ideal viewport — наиболее подходящий viewport для мобильных устройств. Ширина ideal viewport равна ширине экрана мобильного устройства. Если задать ширину элемента в CSS равной ширине ideal viewport (в px), ширина элемента будет равна ширине экрана устройства, то есть эффект 100% ширины. Значение ideal viewport в том, что независимо от разрешения экрана сайты, разработанные для ideal viewport, могут идеально отображаться пользователям без необходимости ручного масштабирования или горизонтальных полос прокрутки.


IV. Использование мета-тега для управления viewport

По умолчанию viewport на мобильных устройствах — это layout viewport, который шире экрана. Но при разработке сайтов для мобильных устройств нам нужен ideal viewport. Так как же получить ideal viewport? Здесь на помощь приходит мета-тег.

При разработке мобильных сайтов одно из самых распространённых действий — скопировать следующее в наш тег head:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">

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

Что именно содержит этот мета-тег с name="viewport", и что делает каждая из его частей?

Мета-тег viewport был впервые представлен Apple в её браузере Safari для решения проблемы viewport на мобильных устройствах. Позже Android и основные производители браузеров последовали этому примеру и добавили поддержку meta viewport, и он оказался очень полезным.

В спецификации Apple meta viewport имеет 6 атрибутов (пока будем называть элементы в content атрибутами и значениями), как показано ниже:

width Задаетlayout viewportwidth, положительное целое число или строку "width-device"
initial-scale Задает начальное значение масштаба страницы, число, которое может быть десятичным.
minimum-scale Минимальное значение масштаба, разрешенное пользователю, число, которое может быть десятичным.
maximum-scale Максимальное значение масштаба, разрешенное пользователю, число, которое может быть десятичным.
height Задаетlayout viewportheight, этот атрибут для нас не важен и редко используется.
user-scalable Разрешено ли пользователю масштабирование, значение — "no" или "yes"; no означает запрещено, yes означает разрешено.

Эти атрибуты можно использовать одновременно, по отдельности или в комбинации. При использовании нескольких атрибутов одновременно просто разделяйте их запятыми.

Кроме того, Android также поддерживает приватное свойство target-densitydpi, которое указывает уровень плотности целевого устройства и определяет, сколько физических пикселей представляет 1px в CSS.

target-densitydpi  Значением может быть число или одна из строк high-dpi, medium-dpi, low-dpi, device-dpi.

Обратите особое внимание, что когда target-densitydpi=device-dpi, 1px в CSS равен 1 физическому пикселю.

Поскольку это свойство поддерживается только Android, а Android решил признать устаревшимtarget-densitydpiэто свойство, нам следует избегать его использования.


V. Установка ширины текущего viewport равной ширине идеального viewport

Чтобы получить идеальный viewport, необходимо задать ширину layout viewport по умолчанию равной ширине экрана мобильного устройства. Поскольку width в meta viewport может управлять шириной layout viewport, нам нужно только задать для width специальное значение width-device.

<meta name="viewport" content="width=device-width">

На следующем рисунке показаны результаты тестирования этой строки кода в основных мобильных браузерах:

Видно, что с width=device-width все браузеры могут превратить текущую ширину viewport в идеальную ширину viewport. Однако обратите внимание, что на iPhone и iPad, независимо от того, в портретной или альбомной ориентации, ширина равна ширине идеального viewport в портретном режиме.

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

Но вы, вероятно, не знаете, что

<meta name="viewport" content="initial-scale=1">

эта строка кода также может дать тот же эффект, что и предыдущая, превращая текущий вьюпорт в идеальный вьюпорт.

Ха, удивлены? Потому что теоретически эта строка кода лишь запрещает масштабирование текущей страницы, то есть страница остаётся в своём естественном размере. Так почему же она даёт тот же эффект, что и width=device-width?

Чтобы понять это, сначала нужно выяснить, относительно чего происходит масштабирование. Поскольку значение масштаба здесь равно 1, то есть масштабирование отсутствует, но при этом достигается эффект идеального вьюпорта, ответ может быть только один: масштабирование происходит относительно идеального вьюпорта. Когда идеальный вьюпорт масштабируется до 100%, то есть значение масштаба равно 1, разве вы не получаете идеальный вьюпорт? На практике это действительно так. На следующем рисунке показаны результаты тестирования в основных мобильных браузерах при установкеможет ли <meta name="viewport" content="initial-scale=1"> превратить ширину текущего вьюпорта в ширину идеального вьюпорта.

Результаты тестирования показывают, что initial-scale=1 также может превратить ширину текущего вьюпорта в ширину идеального вьюпорта. Но на этот раз IE на Windows Phone устанавливает ширину равной ширине идеального вьюпорта в портретном режиме независимо от того, используется ли портретная или альбомная ориентация. Однако этот небольшой недостаток уже не важен.

Но что, если width и initial-scale=1 появляются вместе и конфликтуют? Например:

<meta name="viewport" content="width=400, initial-scale=1">

width=400 означает установку ширины текущего вьюпорта равной 400px, тогда как initial-scale=1 означает установку ширины текущего вьюпорта равной ширине идеального вьюпорта. Так какой команде должен подчиняться браузер? Той, что написана позже? Нет. В этом случае браузер выбирает большее из двух значений. Например, когда width=400, а ширина идеального вьюпорта равна 320, он выбирает 400; когда width=400, а ширина идеального вьюпорта равна 480, он выбирает ширину идеального вьюпорта. (Примечание: в браузере UC9, когда initial-scale=1, независимо от значения атрибута width, ширина вьюпорта всегда равна ширине идеального вьюпорта.)

Итак, подведём итог: чтобы установить текущую ширину viewport равной идеальной ширине viewport, можно использовать либо width=device-width, либо initial-scale=1, но у каждого есть небольшой недостаток: iPhone, iPad и IE не различают портретную и альбомную ориентации, всегда используя идеальную ширину viewport для портретной ориентации. Поэтому наиболее совершенный подход — указать оба вместе. Таким образом, initial-scale=1 решает проблему iPhone/iPad, а width=device-width решает проблему IE:

<meta name="viewport" content="width=device-width, initial-scale=1">

VI. Дополнительные сведения о meta viewport

1. О масштабировании и значении по умолчанию initial-scale

Сначала обсудим вопрос масштабирования. Как упоминалось ранее, масштабирование выполняется относительно идеального viewport. Чем больше значение масштабирования, тем меньше текущая ширина viewport, и наоборот. Например, на iPhone идеальная ширина viewport равна 320px. Если задать initial-scale=2, ширина viewport станет всего 160px. Это легко понять: при увеличении в 1x исходный 1px становится 2px. Но то, что 1px становится 2px, не означает, что исходные 320px становятся 640px; скорее, при неизменной фактической ширине 1px становится такой же длины, как исходные 2px. Так что после увеличения в 2x ширина, для заполнения которой изначально требовалось 320px, теперь требует всего 160px. Следовательно, можно вывести формулу:

visual viewport宽度 = ideal viewport宽度  / 当前缩放值

当前缩放值 = ideal viewport宽度  / visual viewport宽度

ps:Ширина visual viewport — это ширина видимой области браузера.

Большинство браузеров соответствуют этой теории, но у нативного браузера в Android и у IE есть некоторые проблемы. Встроенный WebKit-браузер Android ведёт себя нормально только когда initial-scale=1 и не задан атрибут width; это означает, что эта теория к нему в основном неприменима. IE, напротив, полностью игнорирует атрибут initial-scale; что бы вы ни задали, initial-scale всегда ведёт себя как 1.

Хорошо, теперь давайте поговорим о проблеме значения по умолчанию initial-scale. То есть, когда этот атрибут не указан, каким будет его значение по умолчанию? Очевидно, это не 1, потому что при initial-scale = 1 текущая ширина layout viewport будет установлена равной ширине ideal viewport. Но, как упоминалось ранее, значения по умолчанию для ширины layout viewport в разных браузерах обычно составляют 980, 1024, 800 и т. п. Ни один браузер не начинает с ширины ideal viewport, поэтому значение по умолчанию initial-scale определённо не равно 1. На устройствах Android значение по умолчанию initial-scale, похоже, невозможно получить, или, возможно, у него просто нет значения по умолчанию; оно вступает в силу только когда вы явно его прописываете. Не будем об этом беспокоиться. Здесь мы сосредоточимся на значении по умолчанию initial-scale на iPhone и iPad.

Согласно тестам, на iPhone и iPad можно сделать вывод: независимо от того, какую ширину вы задаёте для layout viewport, если вы не указываете значение начального масштаба, iPhone и iPad автоматически вычисляют значение initial-scale, чтобы текущая ширина layout viewport была равна ширине видимой области браузера после масштабирования, то есть чтобы горизонтальная полоса прокрутки не появлялась. Например, на iPhone, если мы не задаём никакого viewport meta tag, в этот момент ширина layout viewport составляет 980px, но мы видим, что браузер не показывает горизонтальную полосу прокрутки; по умолчанию браузер сжимает страницу. Согласно приведённой выше формуле,значение текущего масштаба = ширина ideal viewport / ширина visual viewport, можно сделать вывод:

当前缩放值 = 320 / 980

То есть текущее значение по умолчанию initial-scale должно быть около 0.33. Когда вы указываете значение для initial-scale, это значение по умолчанию больше не действует.

Короче говоря, просто запомните этот вывод: на iPhone и iPad, какую бы ширину вы ни задавали для viewport, если значение масштаба по умолчанию не указано, iPhone и iPad автоматически вычислят это значение масштаба, чтобы предотвратить появление горизонтальной полосы прокрутки на текущей странице (или, иными словами, сделать ширину viewport равной ширине экрана).

2. Динамическое изменение тега meta viewport

Первый способ

Вы можете использовать document.write для динамического вывода тега meta viewport, например:

document.write('<meta name="viewport" content="width=device-width,initial-scale=1">')

Второй способ

Измените его через setAttribute

<meta id="testViewport" name="viewport" content="width = 380">
<script>
var mvp = document.getElementById('testViewport');
mvp.setAttribute('content','width=480');
</script>

Баг во встроенном браузере Android 2.3

<meta name="viewport" content="width=device-width">

<script type="text/javascript">
alert(document.documentElement.clientWidth); //弹出600,正常情况应该弹出320
</script>

<meta name="viewport" content="width=600">

<script type="text/javascript">
alert(document.documentElement.clientWidth); //弹出320,正常情况应该弹出600
</script>

Идеальная ширина viewport протестированного телефона составляет 320px. Первое значение во всплывающем окне — 600, но это значение должно быть результатом второй строки meta-тегов; затем второе значение во всплывающем окне — 320, что является эффектом, достигнутым первой строкой meta-тегов. Поэтому во встроенном браузере Android 2.3 (возможно, во всех версиях 2.x) переопределение или изменение тега meta viewport может давать очень запутанные результаты.


VII. Заключение

Сказав так много ерунды, всё же стоит подытожить в конце что-то полезное.

Во-первых, если тег meta viewport не установлен, значение ширины по умолчанию в браузерах мобильных устройств составляет 800px, 980px, 1024px и т. д. Короче говоря, оно больше ширины экрана. Единица px, используемая здесь для ширины, относится к px в CSS, что не то же самое, что px, обозначающий фактические физические пиксели экрана.

Во-вторых, у каждого браузера мобильного устройства есть идеальная ширина. Эта идеальная ширина относится к ширине в CSS и не имеет ничего общего с физической шириной устройства. В CSS эта ширина эквивалентна ширине, представленной 100%. Мы можем использовать тег meta, чтобы установить ширину viewport равной этой идеальной ширине. Если вы не знаете идеальную ширину этого устройства, можно использовать специальное значение device-width. В то же время initial-scale=1 также даёт эффект установки ширины viewport равной идеальной ширине. Итак, мы можем использовать

<meta name="viewport" content="width=device-width, initial-scale=1">

чтобы получить идеальный viewport (то есть идеальный viewport, упомянутый ранее).

Почему нам нужен идеальный viewport? Например, телефон с разрешением 320x480 имеет идеальную ширину viewport 320px, а другой телефон с тем же размером экрана, но разрешением 640x960 также имеет идеальную ширину viewport 320px. Почему идеальная ширина телефона с более высоким разрешением должна быть такой же, как у телефона с более низким разрешением? Это потому, что только так один и тот же сайт может выглядеть одинаково или похоже на устройствах с разными разрешениями. На самом деле, хотя на рынке так много телефонов разных типов, брендов и разрешений, их идеальные ширины viewport можно свести всего к нескольким значениям, таким как 320, 360, 384, 400 и т. д., все они очень близки. Похожие идеальные ширины означают, что сайт, разработанный для идеального viewport определённого устройства, не будет сильно отличаться по поведению или даже будет работать одинаково на других устройствах.

Исходный адрес: https://www.cnblogs.com/2050/p/3877280.html