SVG і медіа запити

34

Від автора: одна з дивовижних можливостей SVG дозволяє робити зображення адаптивними. У даній статті ми попрацюємо з нею.

Подивимося на наступний код:

circle {
fill: green;
}
@media (min-width: 100px) {
circle {
fill: blue;
}
}

Але коли в такому випадку коло буде синім? У специфікації говориться, що значення min-width має дорівнювати ширині екрану, але…

Будь екрану?

SVG і медіа запити
SVG і медіа запити

…as above…

Який з кодів вище намалює синій коло в 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:

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, але якщо зображення задана ширина SVG і медіа запити, то видима область буде шириною в 100px.

Я вважаю, це ідеальна поведінка. Так ви можете активувати медіа запити для ширини незалежно від отрисовываемой ширини. Також такий підхід добре співвідноситься з адаптивними зображеннями. Коли ви малюєте SVG і медіа запити на canvas, всі браузери кажуть, що намальоване зображення повинно бути відображенням поточного тега img.

От і все!

Я подав заявку на виправлення помилки в специфікації для поведінки браузера Edge, а також запропонував доповнити createImageBitmap, щоб видиму область можна було ставити в скрипті. Сподіваюся, поведінка у всіх браузерах стане трохи краще! Дані я збирав звідси, а за цим посиланням можна подивитися результати.