От автора: одна из удивительных возможностей SVG позволяет делать изображения адаптивными. В данной статье мы поработаем с ней.
Посмотрим на следующий код:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<svg width="100" height="100" xmlns="//www.w3.org/2000/svg"> <style> circle { fill: green; } @media (min-width: 100px) { circle { fill: blue; } } </style> <circle cx="50" cy="50" r="50"/> </svg> |
Но когда в таком случае круг будет синим? В спецификации говорится, что значение min-width должно быть равно ширине экрана, но…
Какого экрана?
1 2 3 4 5 6 |
<img src="circle.svg" width="50" height="50"> <img src="circle.svg" width="100" height="100"> <iframe src="circle.svg" width="50" height="50"></iframe> <svg width="50" height="50"> ...as above... </svg> |
Какой из кодов выше нарисует синий круг в HTML (возможно обрезанный)? А также какое разрешение экрана нужно использовать? Будет ли это:
размер CSS документа;
атрибуты width/height/viewBox тега svg;
атрибуты width/height тега img;
размер CSS макета тега img.
Большинство браузеров скажут…
SVG масштабируется под размеры тега img, а CSS размеры img составляют видимую область в SVG. Таким образом, у первого img видимая ширина равна 50, а у второго – 100. То есть второе изображение img «подхватит» медиа запрос и станет синего цвета, а первое нет.
Для тега iframe видимой областью в SVG будет видимая область встраиваемого документа. Так в верхнем примере видимая ширина составляет 50 пикселей CSS, так как это ширина iframe.
В инлайновом SVG нет своей собственной видимой области, она является частью родительского документа. Это значит, что стили принадлежат родительскому документу и не распространяются на SVG. Когда я первый раз использовал инлайновый SVG, меня застало это врасплох, но смысл понятен и хорошо описан в спецификации.
А что скажет Firefox?
Firefox думает не совсем так. Этот браузер ведет себя точно так же с отдельными исключениями. Для тега img видимая область – это размер области рендеринга в пикселях устройства. Видимая область меняется в зависимости от плотности изображения. Первое изображение из демо будет зеленым на 1х экранах, и синим на 2х и выше. Тут возникает проблема, так как большинство ноутбуков и мобильных телефонов имеют плотность отображения больше 1.
Это похоже на баг, особенно когда Firefox не применяет ту же логику к iframe. Но тут стоит немного пожалеть Firefox, так как в спецификации ничего не говорится про масштабирование SVG в img, не говоря уже о том, как обрабатывать медиа запросы.
Я подал запрос на исправление ошибки в спецификации, надеюсь, ситуация прояснится. Но все становится еще сложнее, когда дело доходит до…
Рисование SVG в canvas
В canvas также можно рисовать теги img:
1 |
canvas2dContext.drawImage(img, x, y, width, height); |
Но когда круг будет синим? В этот раз видимых областей немного больше. Будет ли это:
CSS размер окна;
атрибуты width/height/viewBox тега svg;
атрибуты width/height тега img;
размеры CSS макета тега img;
размеры canvas в пикселях;
размеры CSS макета тега canvas;
width/height заданные в drawImage;
width/height заданные в drawImage, умноженные на контекст 2D трансформаций.
Как вы думаете? И опять в спецификации нет точного ответа, а браузеры пойдут своими путями. Насколько я могу судить, браузеры будут делать следующее.
Chrome
Chrome возьмет атрибуты width/height из SVG документа. То есть если документ SVG говорит, что ширина width=”50”, вы получите медиа запросы на видимую область в 50px. Если же вы хотите рисовать круг с помощью медиа запросов на видимую область шириной 100px, вам не повезло. Не важно, какого размера вы нарисуете круг на canvas, он будет отрисовываться с медиа запросами под ширину 50px.
Однако если в SVG задан viewBox, а не фиксированная ширина, Chrome возьмет ширину canvas в пикселях в качестве видимой области. Вы можете сказать, что это похоже на работу с инлайновым SVG, где видимая область совпадает со всем окном браузера, но разница в поведении с viewBox действительно странная. Chrome ведет себя хуже всех.
Safari
Как и Chrome, Safari использует размер из SVG документа, что означает, что данный браузер подвержен тем же недостаткам. Однако если в SVG используется viewBox, а не фиксированная ширина, браузер высчитывает ширину из viewBox. То есть viewBox=»50 50 200 200″ даст нам ширину 150. Лучше Chrome, но очень много ограничений.
Firefox
Firefox использует width/height из drawImage, умноженные на контекстные трансформации. То есть если вы рисуете SVG на canvas шириной 300 пикселей, видимая область будет в ширину 300px.
Браузер отражает странное поведение img, основанное на отрисованных пикселях. То есть вы получите те же несостыковки в плотности отображения, если умножите ширину и высоту canvas на devicePixelRatio (и обратно уменьшите с помощью CSS), что вам сделать потребуется, чтобы изображение не было мутным на экранах с высокой плотностью пикселей.
Логика тут есть, но это означает, что ваши медиа запросы связаны с отрисованными пикселями.
Microsoft Edge
Edge для определения видимой области использует размеры макета тега img. Если у img нет макета (display: none или отсутствует в дереве документа), тогда браузер берет атрибуты width/height. Если и их нет, Edge берет внутренние размеры img.
То есть вы можете рисовать SVG на холсте 1000х1000, но если у изображения задана ширина <img width=»100″>, то видимая область будет шириной в 100px.
Я считаю, это идеальное поведение. Так вы можете активировать медиа запросы для ширины вне зависимости от отрисовываемой ширины. Также такой подход хорошо соотносится с адаптивными изображениями. Когда вы рисуете <img srcset=»…» sizes=»…»> на canvas, все браузеры говорят, что нарисованное изображение должно быть отображением текущего тега img.
Вот и все!
Я подал заявку на исправление ошибки в спецификации для поведения браузера Edge, а также предложил дополнить createImageBitmap, чтобы видимую область можно было задавать в скрипте. Надеюсь, поведение во всех браузерах станет немного лучше! Данные я собирал отсюда, а по этой ссылке можно посмотреть результаты.
Автор: Jake Archibald
Источник: //jakearchibald.com/
Редакция: Команда webformyself.