В веб-разработке нередки моменты, когда требуется отслеживать что именно видит на своём устройстве пользователь. В первую очередь это относится, конечно, к масштабу устройства.
Я работаю над встраиваемой мини-CRM и понятия не имею как будет выглядеть сайт. Внизу страницы плавает элемент управления, всегда занимающий комфортную для касания часть экрана. Вряд ли пользователь мобильного телефона будет увеличивать масштаб чтобы оценить эстетику шрифта на нашем виджете, а вот чтобы разобрать текст неоптимизированного для мобильных устройств сайта — запросто. Поэтому, чтобы не перекрывать пользователю содержимое сайта, виджет прячется при масштабировании.
Сейчас задача определения масштаба решается, мягко говоря, непросто, и появление нормального API было бы весьма кстати.
Если вкратце, нормального метода для определения масштаба попросту говоря нет. Все существующие основаны на замере отношения линейных размеров элемента с шириной 100% к размеру экрана, что-то вроде
var scale = div1.сlientWidth / document.documentElement.clientWidth;
Но работает это не всегда и с оговорками. Например, скроллбар может сильно спутать карты.
Ещё один вариант — отношение window.innerWidth / window.outerWidth. Мобильные браузеры смеются над этим приёмом. Большинство. А для некоторых работает.
Вообще всем известно, что Javascript-разработчик должен страдать. Чтобы понять актуальность темы предлагаю вам ознакомиться с этим вопросом на Stackoverflow
Выход visualViewport API решает все подобные проблемы. К сожалению, совместимость пока что никакая, хотя все крупные браузеры планируют поддерживать эту фичу. Тем не менее, лично я счёл необходимым поменять код проекта таким образом, чтобы текущие методы проверки срабатывали только если браузер не поддерживает visualViewport. Очень надеюсь, что ревертить его не станут.
Интерфейс умеет немного, но очень важного и полезного. Рассмотрим его подробнее.
Все свойства, как вы наверное догадались, read-only. Никаких способов набезобразничать разработчики не предоставили. Итак, общий вид:
visualViewport = {
double offsetLeft;
double offsetTop;
double pageLeft;
double pageTop;
double width;
double height;
double scale;
}
Иллюстрация номер один, гипотетический сайт парада андроидов:
Немного не по порядку, зато по важности. Свойства width и height отвечают за линейные размеры изображения с учётом масштаба, то есть сколько пикселей рендера на устройстве поместилось в ширину и высоту окна с учётом масштабирования пользователя.
Два параметра pageLeft и pageTop это координаты верхнего левого угла изображения на экране устройства. Вимание, без учёта масштаба! Возможно понятнее будет такая формулировка: pageLeft и pageTop это координаты пикселя, отображаемого верхним левым в окне, без учёта масштаба окна.
Чтобы объяснить свойства offsetLeft и offsetTop придётся снова посмотреть на парад. Мы переместили экран на группу андроидов и решили увеличить их. На экране зелёным я отметил прямоугольник до масштабирования, красным — то, что видим в окне браузера.
Свойства offsetLeft и offsetTop указывают смещение в пикселях левого верхнего угла масштабированного отображения относительно немасштабированного. Если вы поняли, это с первого раза то могу только поаплодировать. Если не поняли, то просто понаблюдайте за изменением этих свойств при изменении масштаба и точки просмотра.
И последнее свойство, scale. Я не смог подобрать нужную иллюстрацию, просто потому, что не знаю как это проиллюстрировать.Свойство scale возвращает отношение размера окна при width=device-width к его размеру при текущем масштабе.
Объект поддерживает интуитивно понятные события scroll и resize. Приведу пример кода чтобы лучше запомнилось:
window.visualViewport.addEventListener('scroll', () => {alert("Was scrolled!")});
Стоит отметить один нюанс — scroll, по видимому, отслеживает без каких-либо условий изменение pageLeft и pageTop, так что при масштабировании это событие тоже почти всегда происходит.
Вот собственно и всё. Долгие рассуждения про семь свойств и два события развести не получится.
К моему удивлению, появление visualViewport прошло практически незамеченным. Надеюсь что теперь вы обратите своё внимание на этот полезный инструмент.
Комментарии (9)
nizkopal
04.10.2017 09:52Автор, спасибо. Обратил внимание. :)
Интересно, можно ли это будет адекватно интегрировать с автотестами.
Electrohedgehog Автор
04.10.2017 10:24Воистину я властитель дум! Уже два человека обратило внимание!
День прожит не зря)
Пожалуйста. Лично мне такого плана статьи кажутся полезными на хабре, рад что моё мнение разделяют.
Насчёт интеграции с автотестами я особых проблем не вижу, вероятно потому, что разбираюсь в них как свинья в апельсинах. Тестирование имеет свои особенности обращения к различным API или вы имеете в виду какие-то особенности функционирования фреймворков?
Grubergen
04.10.2017 15:57Неужели появился способ определять размеры тач-клавиатуры, когда сайт использует полноэкранный режим.
Ранее она не изменяла никакие параметры, тихо перекрывая область обзора, вместе с полем ввода.
Спасибо автору, иначе бы наверное не скоро узнал про такую фичу)Electrohedgehog Автор
04.10.2017 16:01Увы, но нет. Только что попробовал.
В принципе ожидаемо, клавиатура не является частью браузера.
MrCheater
04.10.2017 21:13Представляется ли возможным на костылях даже самых страшных и ужасных заполифилить visualViewport — написать код, который всеми правдами и неправдами вернет такой же объект в старых браузерах?
Electrohedgehog Автор
05.10.2017 05:52Совершенно точно нет. Есть удачные реализации получениия размеров и масштаба, но без доступа к нативному коду они просто физически не могут работать абсолютно правильно.
Zenitchik
Ещё бы. Когда поддерживаешь тонну легаси, за новинками следишь чисто для информации.
Спасибо. Обратил. Теперь буду следить за поддержкой. Когда нибудь в отдалённом светлом будущем смогу его применить.
MTonly
Electrohedgehog Автор
Прекрасно вас понимаю. Тоже вынужден учитывать древние атавизмы вроде поддержки webView четвёртого Андроида. Одна из немогих действительно хороших инициатив Гугла — обновление по умолчанию всех браузеров и плавное перетягивание их на один движок. Хотя достаточно было бы просто всем придерживаться стандартов… Хотя это я замечтался, да.