От автора: если внимательно присмотреться к плееру YouTube, можно заметить, что кнопка play не просто переключается на иконку паузы по клику, а плавно и быстро перетекает между двумя состояниями. Сделать это можно с помощью SVG под управлением JavaScript. Сегодня я покажу мою интерпретацию данного UI шаблона.
Создаем целевые состояния для плавного перехода
Основное условие выполнения плавного перехода из одного состояния в другое в SVG – количество вершин в обоих состояниях должно совпадать. Также в SVG нельзя разбить замкнутый элемент и превратить его в два. То есть в случае с кнопкой play в SVG нам придется прятать некоторые вершины состояния паузы (8 или более вершин) в самой кнопке (для которой нужно всего 3 вершины). Также это значит, что кнопка play должна будет состоять из двух отдельных частей, но выглядеть как одно целое.
Я создал треугольник в Adobe Illustrator на листе размером 50 х 50 пикселей. Привязав треугольник к слою, поверх него я создал два отдельных элемента, повторяющих его форму.
Два элемента накладываются друг на друга в середине. Обратите внимание, что правый треугольник состоит из 5 точек, а не из 4, чтобы сохранить форму стрелки. Самые правые три вершины имеют свою позицию, но могут перекрывать друг друга. После экспорта в SVG и зачистки кода разметка выглядит следующим образом:
1 2 3 4 5 |
<svg xmlns="//www.w3.org/2000/svg" viewBox="0 0 50 50" id="playpause" xmlns:xlink="//www.w3.org/1999/xlink"> <title>Play</title> <polygon points="12,0 25,11.5 25,39 12,50" id="leftbar" /> <polygon points="25,11.5 39.7,24.5 41.5,26 39.7,27.4 25,39" id="rightbar" /> </svg> |
Каждый polygon нужно анимировать в новое состояние. Перетащив существующие точки и превратив иконку в два вертикальных прямоугольника, результат можно экспортировать и добавить в предыдущий SVG в качестве значений тегов animate. SVG теперь стал таким:
1 2 3 4 5 6 7 8 9 |
<svg xmlns="//www.w3.org/2000/svg" viewBox="0 0 50 50" id="playpause" xmlns:xlink="//www.w3.org/1999/xlink"> <title>Play</title> <polygon points="12,0 25,11.5 25,39 12,50" id="leftbar" /> <polygon points="25,11.5 39.7,24.5 41.5,26 39.7,27.4 25,39" id="rightbar" /> <animate to="7,3 19,3 19,47 7,47" id="lefttopause" xlink:href="#leftbar" attributeName="points" dur=".3s" begin="indefinite" fill="freeze" /> <animate to="31,3 43,3 43,26 43,47 31,47" id="righttopause" xlink:href="#rightbar" attributeName="points" dur=".3s" begin="indefinite" fill="freeze" /> </svg> |
В предыдущей статье по SVG морфизму я вас уже познакомил с двумя новыми атрибутами из этого кода. Атрибут begin=»indefinite» запрещает моментальный старт анимации (для теста можете просто удалить этот атрибут). Атрибут fill=»freeze» – то же самое, что animation-direction: forward в CSS (анимация проигрывается один раз и застывает в конечном состоянии). Также теги animate я расположил после полигонов, а не внутри них, а ссылки на анимируемые элементы задал с помощью xlink:href.
После перехода SVG в состояние паузы необходимо вернуть его обратно в режим play. К сожалению, в SVG нет простой инверсии движений, нам придется создать второе целевое состояние:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<svg xmlns="//www.w3.org/2000/svg" viewBox="0 0 50 50" id="playpause" xmlns:xlink="//www.w3.org/1999/xlink"> <title>Play</title> <polygon points="12,0 25,11.5 25,39 12,50" id="leftbar" /> <polygon points="25,11.5 39.7,24.5 41.5,26 39.7,27.4 25,39" id="rightbar" /> <animate to="7,3 19,3 19,47 7,47" id="lefttopause" xlink:href="#leftbar" attributeName="points" dur=".3s" begin="indefinite" fill="freeze" /> <animate to="12,0 25,11.5 25,39 12,50" id="lefttoplay" xlink:href="#leftbar" attributeName="points" dur=".3s" begin="indefinite" fill="freeze" /> <animate to="31,3 43,3 43,26 43,47 31,47" id="righttopause" xlink:href="#rightbar" attributeName="points" dur=".3s" begin="indefinite" fill="freeze" /> <animate to="25,11.5 39.7,24.5 41.5,26 39.7,27.4 25,39" id="righttoplay" xlink:href="#rightbar" attributeName="points" dur=".3s" begin="indefinite" fill="freeze" /> </svg> |
Каждой анимации я присвоил свой ID (id: lefttopause и lefttoplay).
Расставляем все на места
Для упрощенного доступа SVG помещен в тег button:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<div id="atlanticlight"> <video controls> <source src="atlantic-light.webm"> <source src="atlantic-light.mp4"> </video> <button> <svg xmlns="//www.w3.org/2000/svg" viewBox="0 0 50 50" id="playpause" xmlns:xlink="//www.w3.org/1999/xlink"> <title>Play</title> <polygon points="12,0 25,11.5 25,39 12,50" id="leftbar" /> <polygon points="25,11.5 39.7,24.5 41.5,26 39.7,27.4 25,39" id="rightbar" /> <animate to="7,3 19,3 19,47 7,47" id="lefttopause" xlink:href="#leftbar" attributeName="points" dur=".3s" begin="indefinite" fill="freeze" /> <animate to="12,0 25,11.5 25,39 12,50" id="lefttoplay" xlink:href="#leftbar" attributeName="points" dur=".3s" begin="indefinite" fill="freeze" /> <animate to="31,3 43,3 43,26 43,47 31,47" id="righttopause" xlink:href="#rightbar" attributeName="points" dur=".3s" begin="indefinite" fill="freeze" /> <animate to="25,11.5 39.7,24.5 41.5,26 39.7,27.4 25,39" id="righttoplay" xlink:href="#rightbar" attributeName="points" dur=".3s" begin="indefinite" fill="freeze" /> </svg> </button> </div> |
Настройки включают в себя сокрытие SVG по умолчанию. Появляться кнопка будет при помощи JS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
* { box-sizing: border-box; } #atlanticlight { position: relative; font-size: 0; width: 100%; } #atlanticlight, #atlanticlight video, #atlanticlight button { width: 100%; height: auto; } #atlanticlight button svg { width: 50%; margin: 0 auto; fill: #fff; padding: 3rem; transition: .6s opacity; } #atlanticlight video, #atlanticlight button { position: absolute; top: 0; } #atlanticlight button { background: transparent; outline: none; border: none; cursor: pointer; } #playpause { display: none; } .playing { opacity: 0; } |
Свойство transition на теге svg, примененное через класс, заставит кнопку плавно появляться и исчезать. Добавлять и удалять класс мы будем через classList.
Приводим все в движение
Чтобы кнопка play/pause заработала, необходимо определить элементы, с которыми мы будем работать:
1 2 3 4 5 6 |
var atlanticlight = document.querySelector("#atlanticlight video"), playpause = document.getElementById("playpause"), lefttoplay = document.getElementById("lefttoplay"), righttoplay = document.getElementById("righttoplay"), lefttopause = document.getElementById("lefttopause"), righttopause = document.getElementById("righttopause"); |
Применяя технику прогрессивного улучшения, мы удаляем стандартные кнопки из видео и показываем наш собственный UI:
1 2 |
mountain.removeAttribute("controls"); vidcontrols.style.display = "block"; |
Теперь необходимо ловить событие клика на кнопку button. Мы не будем отслеживать состояние элемента, мы будем смотреть на состояние видео:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
atlanticlight.removeAttribute("controls"); playpause.style.display = "block"; playpause.addEventListener(’click’,function(){ if (atlanticlight.paused) { atlanticlight.play(); playpause.classList.add("playing"); lefttopause.beginElement(); righttopause.beginElement(); } else { atlanticlight.pause(); lefttoplay.beginElement(); righttoplay.beginElement(); playpause.classList.remove("playing"); } },false); |
Будущие наработки
Если открыть консоль в Chrome и запустить пример выше, вы получите следующее сообщение: «SVG SMIL анимация (animate, set и т.д.) устарела и будет удалена. Пожалуйста, воспользуйтесь CSS или веб-анимацией.»
К счастью или нет, это правда: в скором времени Chrome полностью откажется от SMIL (язык на основе SVG, который мы до сих пор использовали для анимации) в угоду Web Animations API. К сожалению, в новой спецификации пока что ничего не сказано про морфизм, что ставит этот код в затруднительное положение на довольно долгий промежуток времени. Наш код будет в подвешенном состоянии, пока не будет разработана отдельная JS библиотека, или пока Web Animations API не расширится. На этот переходной период мы можем использовать библиотеку анимации Greensock в качестве фолбэка, более подробно о ней я расскажу в следующей статье.
Источник: //thenewcode.com/
Редакция: Команда webformyself.
Комментарии (1)