Мне нравится свойство background-size. Благодаря ему можно делать с изображениями удивительные вещи! Но так ли гладко обстоят дела с тегом img? Как оказалось, да. Скоро мы сможем не сходить с ума, прикидывая что такого ещё придумать, чтобы наши картинки выглядели на всех устройствах одинаково. Ну, хорошо, пусть не одинаково… однако, точно так, как задумано. И поможет нам в этом свойство object-fit.

Значения object-fit

Какие значения принимает свойство object-fit?

значениечто делает
fillзаполняет всё пространство контейнера, не учитывает пропорции
containмасштабирует изображение с сохранением пропорций так, чтобы оно целиком поместилось внутрь блока
coverмасштабирует изображение с сохранением пропорций так, чтобы его ширина или высота равнялась ширине или высоте блока
noneне масштабирует размер изображения

Пробуем применить всё это на практике.

Например, нужно разделить родительский контейнер ещё на два, один из которых будет содержать изображение, а второй — текст.

<!-- html -->
<a class="news clear" href="#">
  <div class="image"><img alt="img" src="img.jpg"/></div>
  <div class="desc">
    <time datetime="2014-12-12">12 декабря 2014</time>
    <p>Резкая боль в пояснице после неудачного поворота</p>
  </div>
</a>

Вот как могут при этом выглядеть стили:

/* LESS */
.news {
    display: block;
    text-decoration: none;
    color: #333;
    height: 102px;
    border: 1px solid #ddd;
    padding: 5px;
    .image {
        width: 40%;
        float: left;
        img {
            object-fit: cover;
            width: 100%;
            height: 90px;
        }
    }
    .desc {
        width: 55%;
        float: right;
    }
}

В целом, смотрится нормально.

aside image

А теперь так часто встречающаяся вещь: уравнение с одной неизвестной. Нам не дано знать какого размера и с каким соотношением сторон изображение будет впоследующем загружено владельцем ресурса. Однако, какой бы она ни была, картинка не имеет права сжиматься до состояния «я вас не вижу» и не должна выходить за пределы родительского контейнера. Короче: заполняет собой всё пространство.

В обычной версии это выглядит так:

full screen

<!-- begin recent-post -->
<a href="#" class="recent-post">
  <img alt="recent post" src="img/3.jpg"/>
  <time datetime="2015-12-22">22 декабря 2015</time>
  <p>Спондилоартроз является формой остеоартроза</p>
</a>
<!-- end recent-post -->
/* LESS */
.recent-post {
    text-decoration: none;
    width: 100%;
    display: inline-block;
    padding: 5px;
    margin-bottom: 20px;
    border: 1px solid @light-gray;
    &:not(:last-child){
        margin-right: 2.8%;
    }
    &:hover, &:focus { box-shadow: @bx;}
    img {
        display: block;
        width: 100%;
        object-fit: cover;
    }
    @media screen and (min-width: @break-point){
        width: 31%;
        margin-bottom: 0;
    }
}

А для экранов с небольшим разрешением… Та-да!!! В общем, тоже неплохо выглядит.

full screen

А что, если поэкспериментировать с нашим новым другом object-fit и задать значение none?

full screen

Ну, такую картину мы можем получить и без всяких фокусов. Не интересно.

Ложка дёгтя

Как обычно, всё не может быть настолько просто и легко. Жизнь бывает сладкой лишь наполовину: Firefox понимает нас только с версии 36. Но это ничего, можно немного повременить с использованием новшеств в боевых условиях.

А окончательно подпортить погоду как всегда спешит Internet Explorer (товарищи, не используйте браузер от Microsoft по привычке или в надежде, что когда-нибудь он станет лучше: не станет).

Синий ослик не понимает object-fit. В принципе не понимает. Ну вот вообще.

Ладно, хорошо, есть ещё одна положительная новость: всё уже создано до нас. И в данном случае мы можем использовать полифилл. Рассказываю как.

Забрать polyfill можно со страницы https://github.com/anselmh/object-fit. Если читатель использует в работе grunt или gulp, то утановка может происходить как через npm, так и через bower:

$ bower install --save object-fit
$ npm install --save object-fit

А дальше, следуя инструкции, осталось лишь подключить нужные стили и скрипт и выбрать как именно обращаться с картинками. Например:

<!doctype html>
<html lang="ru">
  <head>
    <link rel="stylesheet" href="css/style.css">
    <link rel="stylesheet" href="css/polyfill.object-fit.css">
    <script src="js/polyfill.object-fit.min.js"></script>
    <script>
    objectFit.polyfill({
      selector: '.news img', // здесь может быть любой CSS селектор
      fittype: 'cover' // или contain, cover, fill, none
    });
    </script>
  </head>
</html>

Да, надо сказать, что IE10 больше не ведётся на конструкции а-ля

<!--[If IE]>мой хак<![endif]-->

и бороться с этим — беда ещё та (улыбаемся и машем, и посылаем лучи добра ребятам из Редмонда).

В общем, есть и лучший способ. Это modernizr.js. Разумеется, для начала понадобится загрузить саму библиотеку. А затем сделать примерно следующее:

<!doctype html>
<html lang="ru">
  <head>
  <script src="js/modernizr.js"></script>
  <script>
  Modernizr.load({
    test: Modernizr['object-fit'],
    nope: ['css/polyfill.object-fit.css', 'js/polyfill.object-fit.min.js'],
    complete: function(){
      if (!Modernizr['object-fit']) {
        objectFit.polyfill({
          selector: '.news img', // this can be any CSS selector
          fittype: 'cover' // either contain, cover, fill or none
        });
      }
    }
  })
</script>
  </head>
  <body></body>
</html>

Эффект будет тем же самым. Кроме того, modernizr может попутно подправить ещё что-нибудь. Если вы захотите, конечно. Тем, кто пока не в курсе его сверхспособностей, рекомендую ознакомиться: лишним не будет.

Другие трюки

Теоретически при помощи полифилла можно заставить firefox < 36 работать с object-fit, однако на практике вы рискуете встретиться с некоторыми проблемами. Например, при использовании сторонних CDN вывалится ошибка «SecurityError: The operation is insecure». Всё держите локально и не используете CDN? Ладно, тогда к вашим услугам «CSS2Properties doesn’t have an indexed property setter in FF».

В общем, пока ждём. Как вариант можно использовать временный костыль с background-image:

<!-- html -->
<a class="news" href="#">
  <div class="image" style="background-image: url('img/1.jpg')"></div>
  <div class="desc">
    <time datetime="2014-12-24">12 декабря 2014</time>
    <p>Резкая боль в пояснице после неудачного поворота</p>
  </div>
</a>

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

Да, и по-другому тоже костыльно :)

<!-- html -->
<a class="news clear" href="#">
  <div class="image" style="background-image: url('img/1.jpg')">
    <img alt="img" src="img/1.jpg"><!-- скрываем через display: none -->
  </div>
  <div class="desc">
    <time datetime="2014-12-24">12 декабря 2014</time>
    <p>Резкая боль в пояснице после неудачного поворота</p>
  </div>
</a>

Если коротко: в этом плане не может быть рекомендаций. Действовать надо, исходя из сложившейся ситуации. Городить огород с css, обрезать изображения каким-нибудь js-скриптом или что там ещё сейчас модно и наименее проблематично?

Примечание: кстати, все мы занимаемся ужасными вещами, пытаясь заставить IE понимать то, чего он понимать категорически не хочет. Тем самым мы своими руками поддерживаем жизнь в чудовище. Не надо придумывать хаки для Explorer’a: пользователи этого браузера должны страдать. Много. Чем больше, тем быстрее они уйдут на хорошие браузеры ;)

Послесловие

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