SVG маски и вау-эффекты: о магии простыми словами +61



О существовании SVG знают все, кто занимается фронтендом. Этой технологии уже не один год, про нее уже не раз писали на хабре. Но есть один момент. Частенько на разных ресурсах, в том числе и на тостере, начинающие задают вопросы о создании определенного семейства анимаций на сайте и получают довольно странные ответы от “бывалых” разработчиков. Возникает ощущение, что в такие моменты все думают в контексте HTML+CSS+JS и просто забывают о существовании SVG, предлагая все рисовать на canvas и попутно давая обещания дать тому, кто это придумал, клавиатурой по голове. Но этот путь (рисование на canvas) зачастую слишком сложен относительно решаемой задачи. В предыдущей статье мы обсуждали идеи создания частичных вау-эффектов, а в этой поговорим о масках и посмотрим пару анимаций, которые с их помощью можно сделать.



Что вообще такое эти маски?


В нашем контексте маска – это дополнительный слой над контентом, который частично его скрывает. На первый взгляд может показаться, что кроме обрезания каких-нибудь картинок в форме шестиугольника применений этому нет. Ведь обычно именно такие примеры приводят в статьях о масках. Но если посмотреть немного шире и добавить движение, то все начинает играть новыми красками.

В базовом варианте маска создается примерно так:

<svg>
    <defs>
        <mask id=’mask-1’>
            . . .
        </mask>
    </defs>
    <image mask=’url(#mask-1)’ />
</svg>

Вместо картинки может быть что угодно. Маску вполне можно применять ко всему, что мы там нарисуем. Сама она также может состоять из каких угодно компонентов, мы можем сделать ее из картинки, использовать многоугольники или кривые.
Удобно делать маску черно-белой. Точнее сказать, делать так, чтобы она состояла из оттенков серого, которые будут работать как прозрачность. Крайние значения, черный и белый – это полная прозрачность и непрозрачность, а оттенки серого – что-то посередине. Формально это не обязательно (будет работать и цветной вариант), но довольно удобно.
В большинстве случаев мы используем двухцветные черно-белые схемы, чтобы делать маски с четкими границами, но стоит помнить и о том, что можно добавить прозрачность при необходимости.

В целом это все, что нужно знать. Маски – это очень простой инструмент в нашем арсенале, но польза от него может быть довольно внушительной. Рассмотрим пару примеров.

Текст с заливкой


Этот эффект относительно часто встречается, но тем не менее будет хорошим примером для начинающих. Идея следующая: текст (пусть будет белый) на фоне картинки трансформируется в текст, состоящий из картинки, на белом фоне. Или то же самое, но в обратную сторону. На словах сложно, но взгляните на иллюстрацию:



Вместо картинки может быть какой-либо градиент (например в горошек) или анимированные волны — здесь все зависит от вас. Для того, чтобы реализовать эффект перехода, нужно взять маску, состоящую из черного текста на белом фоне, а затем поменять цвет текста на черный, а цвет фона на белый.

<svg viewBox='0 0 100 100'>
    <defs>
        <mask id='mask-1' x='0' y='0' width='100' height='100'>
            <!-- Тут меняем #000 на #fff -->
            <rect id='main-rect' x='0' y='0' width='100' height='100' fill='#000' />
            <!-- Тут меняем #fff на #000 -->
            <text id='text' x='50' y='80' fill='#fff'>SVG</text>
        </mask>
    </defs>
    <image width='100' height='100' xlink:href='. . .' mask='url(#mask-1)' />
</svg>

Это все, что нужно для создания подобного перехода. В одном случае маска закрывает силуэт текста и пропускает картинку вокруг него, а во втором все происходит ровно наоборот. Просто и эффективно.



Если добавить пару дополнительных прямоугольников нужного цвета в маску и немного это все анимировать, то получится довольно привлекательный эффект:


Полезно обратить внимание на то, как текст выезжает из-за невидимой стенки в самом начале. Подобные действия часто применяют при создании различных анимаций.
В примере используется anime.js, кто-то скажет, что нужно использовать GSAP, потому, что это “стандарт”. Но, как мы знаем, на вкус и цвет фломастеры разные, так что используйте то, что вам нравится больше. Можно и на чистом JS все это делать. По сути все эти анимации – это постепенное изменение тех или иных атрибутов у элементов, так что никто не мешает взять requestAnimationFrame, querySelector, setAttribute и самостоятельно увеличивать или уменьшать необходимые значения. В первом примере меняются только координаты элементов в маске и их цвета.

Раскраски. Детский сад глазами верстальщика.




Одни говорят, что раскраски – это не для взрослых, другие – что они успокаивают. Но как бы там ни было, это очень хорошая фишка для сайтов, связанных с детскими развивающими играми, художественными курсами или еще чем-нибудь таким. При раскрашивании какого-либо объекта (опять же, прямоугольник, фотография – не важно, пусть будет сердечко), маска будет очень полезным инструментом.

Возьмем какой-нибудь векторный графический редактор и наваяем кривую линию, по которой будет двигаться наш воображаемый фломастер. Особый талант здесь не нужен, но если вы рисуете мышкой, то лучше немного сгладить эту линию:


Можно зарисовать только необходимую фигуру, а можно выйти немного за ее пределы – это простой способ добавить небольшие паузы в закрашивании.
А теперь мы сделаем линию потолще и прорисуем ее от начала до конца, анимируя свойство strokeDashoffset. Сделав это в маске мы получим эффект зарисовывания нашего объекта.


Все это реализовано буквально в несколько строк:

<svg viewBox='0 0 100 100'>
    <defs>
        <mask id='mask-1' x='0' y='0' width='100' height='100'>
            <path id='path-1'  d='. . .' fill='none' stroke='#fff' stroke-width='8' stroke-linecap='round' />
        </mask>
    </defs>
    
    <path d='. . .'  stroke='#AF1B3F' stroke-width='0' fill='#FF686B' mask='url(#mask-1)'/>
</svg>

И сама анимация:

var lineDrawing = anime({
    targets: '#path-1',
    strokeDashoffset: [anime.setDashoffset, 0],
    duration: 5000
});

Таким образом можно сделать и довольно занятный эффект рисования картины, когда берутся несколько слоев (условно набросок, подмалевок и прописка) и “зарисовываются” по очереди. В некоторых случаях этот подход может быть хорошей основой для презентации о создании продукта. Или перехода между изображениями в различных галереях, о чем и будет следующий пример.

Переходы для каруселей


Чаще всего мы видим переходы, которые основаны на сдвиге слайдов, как при плавном проматывании пленки, или основанные на увеличении/уменьшении прозрачности этих самых слайдов. Но это же скучно. Маски дают нам возможность делать куда более интересные переходы. Для примера возьмем вот такой вариант:



На чистом CSS+JS делать такие вещи сложно. Но если взять маски, состоящие из нескольких прямоугольников, которые двигаются в одном направлении или увеличиваются в размерах, то все становится куда проще:


WebKit имеет в себе небольшой плавающий баг, когда значения округляются не совсем в ту сторону, в которую мы ожидаем. В результате появляются дырки в 1px между элементами. Можно решать это с помощью calc из CSS, добавляя или вычитая 0.5px из нужных значений, а в нашем контексте – можно сделать фигуры немного больше, чем они должны быть по идее (помните о том, что на маленьких экранах нужно увеличивать сильнее — в примере это намеренно не сделано и при уменьшении размера окна можно заметить появление просветов между прямоугольниками). При правильно подобранных значениях для увеличения никто не заметит, что там что-то не так.
Точно таким же образом можно сделать “пузырьки”, чтобы следующий слайд расползался по предыдущему в виде увеличивающихся кругов или в виде кляксы, если вы ее нарисуете в виде замкнутой кривой, различные мозаики из многоугольников или пазлы, когда единственное, что нам нужно делать – это менять цвет заливки этих фигур в маске, и много других интересных эффектов — все зависит от вашей фантазии.

Заключение


Маски – это очень полезный инструмент в арсенале фронтендера. Тем, кто только начинает входить в эту тему, стоит уделить им определенное внимание, поскольку они не требуют долгого изучения и дают мощные результаты здесь и сейчас.

Если вы хотите почитать о простых идеях создании какого-либо другого семейства эффектов (в контексте фронтенда, разумеется) – оставляйте ваши предложения в комментариях.

Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (11):

  1. NeXTs_od
    /#10676232

    Спасибо за идею с раскраской. Мне бы сходу в голову такая идея не пришла.

  2. Sergey-Pimenov
    /#10676378

    Спасибо за статью, приятно видеть такое на русском языке. У меня к вам куча вопросов.

    • Какие техники и инструменты вы используете для морфинга в SVG или Canvas? Правда, это тема для статьи.
    • Как происходит работа с дизайнером/заказчиком. Делает ли дизайнер раскадровки и прототипы анимации? Или анимации проектируете сами?
    • Очень чувствителен вопрос производительности анимации. Хотелось бы разным устройствам отдавать разную анимацию, в зависимости от производительности. Есть ли какой-то реальный способ определения производительности устройства в контексте браузера? Если нет, то как следует поступать, ведь нужно же как-то сделать хорошо для разных классов устройств.

    • sfi0zy
      /#10676654 / +1

      Правда, это тема для статьи.

      Да, про это лучше статью написать.

      Как происходит работа с дизайнером/заказчиком

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

      вопрос производительности

      Обычно достаточно не запускать более одной анимации в единицу времени, а на маленьких экранах уменьшать количество элементов в них (например в последнем примере можно не 10 прямоугольников делать, а 3-5). Для сложных анимаций на canvas иногда считают fps и что-то меняют в зависимости от полученного значения.

  3. Ne01eX
    /#10676658

    Есть мнение, что некоторые вещи проще делать в самом svg-файле вообще не прибегая к JS.

    Например, это:

    • triton
      /#10676696 / +1

      Не хочу вас огорчать, но там внутри SVG-файла тоже JS
      function setClock()
      {
      var date = new Date()
      var h = parseInt(date.getHours())
      h = h > 12 ? h-12: h
      var m = parseInt(date.getMinutes())
      var s = parseInt(date.getSeconds())
      ...

      • Ne01eX
        /#10677044

        Я не совсем корректно выразился. При использовании скрипта внутри svg-файла он остаётся рабочим и при использовании расширений для браузеров отключающих скрипты на диалектах ECMAScript. Ну и самодостаточность такого решения, опять же.

        А статья мне понравилась, ага.

      • KeySVG
        /#10681140

        Вот чистоган без js — правда голая анимация без синхрона с часиками компа или сервера.
        image

      • isuo
        /#10681142

        Но JS использован только для синхронизации времени, анимация родная svg

  4. andreyorst
    /#10676704

    Помню лет 13 назад, когда в вебе интерактив преимущественно держался на флеше, я «фрилансил» (и слова такого тогда не знал), создавая баннеры с такого рода анимациями. Благо, в той же Macromedia Flash можно было, сочетая маски и Motion Tween анимацию, делать практически всё, что сейчас делают моушн дизайнеры. Правда не так удобно и быстро, как в современных инструментах.

  5. narekchang
    /#10676760

    Когда-то давно загорелся идеей сделать все иконки на сайте через наложение маски иконки поверх цвета. Отказался от этой мысли, так как в FF поддержки масок толком не было. Сама статья хорошая, но и сейчас ситуация с масками не очень понятна. Возможно, рановато использовать.

    caniuse.com/#search=mask

    • sfi0zy
      /#10676780

      Вы говорите про использование масок в CSS, там и правда все печально с поддержкой браузерами. А так они были уже очень давно и в пределах svg элементов работают во всех современных браузерах.