Формат m4b — не предусмотренное стандартом расширение для медиаконтейнера mp4. Это аудио-файл формата AAC ( Advanced Audio Coding ) поддерживающий закладки. В основном этот формат используется для аудиокниг и подкастов онлайн-магазинов компании Apple ( iTunes Store ).
Какие программы есть
Под macOS существует хорошее приложение для создания \ конвертирования книг в формате m4b. Это приложение называется Audiobook Builder.
Приложение отличное - простое, интуитивно понятное и имеет в себе все, что необходимо, ничего лишнего.
Но есть одна загвоздка - чтобы пользоваться данным приложением, нужно его купить ( во-первых ); а во-вторых - нужно приобрести технику Apple ( iMac, macBook ).
Для пользователей Linux вариантов в плане приложений для создания формата m4b не так уж много. Есть много гиковских - консоль, консольные утилиты. Можете сами погуглить - вариантов выпадет много, но все они на любителя, что называется.
Единственным ( на момент написания статьи ) “человеческим” вариантом является приложение под названием m4Baker.
m4Baker — небольшое Python / QT ( PyQT ) графическое приложение для создания iPod-совместимых аудиокниг формата m4b, с главами и обложками. Приложение бесплатное. Лицензия - GNU General Public License v3 ( GPLv3 ).
Установка m4Baker
Приложения m4Baker нет в официальном репозитории дистрибутива Linux Mint. Программу нужно устанавливать вручную, набором нескольких команд.
Итак, открываем терминал и последовательно запускаем на выполнение команды:
Можно объединить все в одну команду, конечно, но я оставил все команды разделенными для большей ясности процесса.
Установку пакетов я производил под Linux Mint 18 Cinnamon. Это аналог Ubuntu 16.04.1 LTS (Xenial Xerus).
Все пакеты нашлись в репозитории, кроме последнего - libsox-fmt-mp3. Его пришлось скачивать вручную по ссылке - Xenial/libsox-fmt-mp3. И устанавливать пакет libsox-fmt-mp3_14.4.1-5_amd64.deb также вручную, локально.
После того, как все вышеперечисленные пакеты успешно установлены, необходимо установить пакет программы m4Baker:
скачиваем дистрибутив программы m4Baker из GitHub-репозитория по ссылке - GitHub m4Baker
открываем директорию m4baker-master в терминале и запускаем команду установки программы m4Baker:
Эта команда установит программу m4Baker со всеми ее зависимостями.
Теперь можно запустить m4Baker любым удобным способом - через Start Menu, из командной строки консоли. У меня m4Baker успешно запустился через лаунчер Synapse.
Обзор m4Baker
m4Baker благодаря использованию SoX, faac и mp4v2 поддерживает большинство популярных аудио-форматов, есть возможность изменения и добавления метаданных, добавления обложки.
Первый запуск m4Baker выглядит таким образом:
Справка, представленная в самой программе m4Baker, является самодостаточной - ее полностью хватает для пользования.
Для загрузки mp3-файлов нужно нажать кнопку “Add Audiobook” ( иконка книги ), откроется диалоговое окно выбора файлов.
После загрузки файлы добавятся в список обработки:
Стоит обратить внимание, что выборка должна быть “чистой” - только mp3-файлы и никакого другого “мусора”, иначе программа m4Baker не запустится для обработки файлов.
В создаваемой m4b-аудиокниге возможно автоматическое разделение на части, возможна сортировка исходных файлов по имени или номеру трека (ID3-тегу), создание маркеров глав, перемещение глав между аудиокнигами:
Нажатие на кнопку “Procell all” ( иконка шестеренки ) запустит процесс создания файла формата m4b:
Замечания
OS Linux
Я протестировал приложение m4Baker на рабочей машине Linux Mint 18 Cinnamon. Файлы mp3-формата аудиокниги были успешно сконвертированы в файл формата m4b. На Linux Desktop данный файл успешно прослушивался.
OS Android
На Android ( v4.0.3 ) файл запускался с некоторой задержкой, но прослушивался ( Smart AudioBook Player ).
Из заметных недостатков - не отображается обложка ( cover ) аудиокниги, хотя при конвертации в программе m4Baker я добавлял обложку в описание к создаваемому файлу ( данная обложка присутсвует на скриншотах ).
macOS
Самый главный минус и разочарование. Файл m4b, созданный в программе m4Baker, у меня не открылся вообще под macOS Sierra ( 10.12 ).
Плейер iTunes не открыл файл и скромно промолчал о данном факте. QuickTime Player также не смог открыть файл, но сообщил мне о данном факте.
OS Windows
О системе Windows ничего сказать не могу, так как не пользуюсь данной операционной системой.
Заключение
Приложение m4Baker можно использовать для создания аудиокниг формата m4b под операционной системой Linux. Приложение удобно и просто в использовании.
Однако, данное приложение несколько сложно в установке, имеет не полный функционал. Созданные в этой программе аудиокниги проиигрываются не под всеми операционными системами и плейерами.
В программе отсутсвуют возможности для добавления дополнительных метаданных, таких как “Narrator”, “Year”, “Description”, “Copyright”. К минусам можно отнести и отсутсвие программы такого класса в безграничных репозиториях Ubuntu.
Увы, как мне кажется, пока у программы Audiobook Builder нет достойной альтернативы.
Вторая часть официальной документации по Fabric. Речь идет об анимации объектов на Canvas, применении стандартных фильтров к изображениям или создании пользовательских фильтров.
Вычитал и поправил некоторые орфографические ошибки. Продолжает радовать факт, как автор детально и внятно описывает работу каждой строки кода в сниппетах. Эту статью можно продолжать читать просто как общий легкий курс по JavaScript. )
Это вторая часть серии статей об открытой Javascript Canvas библиотеке Fabric.js, которую мы используем на printio.ru для редактора дизайнов.
В первой части этой серии мы ознакомились с самыми базовыми аспектами canvas библиотеки Fabric.js. Мы узнали, чем может быть полезна Fabric, рассмотрели ее объектную модель и иерархию объектов; увидели, что существуют как простые фигуры (прямоугольник, треугольник, круг), так и сложные (SVG). Научились выполнять простые операции над этими объектами.
Ну вот, разобрались с азами, давайте приступать к более интересным вещам!
Анимация
Любая уважающая себя canvas-библиотека в наше время включает в себя средства работы с анимацией. Fabric — не исключение. Ведь мы имеем мощную объектную модель и гибкие графические возможности. Было бы грех не уметь это приводить в движение.
Вы наверное помните, как менять атрибут у объекта. Просто вызываем метод set, передавая соответствующее значение:
Анимировать объект можно по такому же принципу и с такой же легкостью. Каждый объект в Fabric имеет метод animate (наследуя от fabric.Object) который … анимирует этот объект:
Первый аргумент - это атрибут, который хотим менять. Второй аргумент — финальное значение этого атрибута.
Например, если прямоугольник находится под углом -15° и мы указываем 45°, то угол постепенно изменится с -15° до 45°.
Ну, а последний аргумент — опциональный объект для более детальных настроек (длительность, вызовы, easing и т. д. )
animate кстати имеет очень полезную функциональность — поддержку относительных значений. Например, если нужно подвинуть объект на 100px вправо, то сделать это очень просто:
По такому же принципу, для поворота объекта на 5 градусов против часовой стрелки:
Вы наверняка заметили, что мы постоянно указываем вызов onChange. Разве 3-й аргумент не опциональный? Да, именно так.
Дело в том, что как раз это вызывание canvas.renderAll на каждый кадр анимации позволяет видеть саму анимацию!
Mетод animate всего лишь изменяет значение атрибута в течении указанного времени и по определенному алгоритму (easing).
rect.animate(‘angle’, 45) изменяет значение угла, при этом не перерисовывая экран после каждого изменения. А перерисовка экрана нужна для того, чтобы увидеть анимацию.
Ну, а почему же animate не перерисовывает экран автоматически? Из-за производительности. Ведь на холсте могут находиться сотни или даже тысячи объектов.
Было бы довольно ужасно, если каждый из объектов перерисовывал экран при изменении. В таком случае лучше использовать, например, requestAnimationFrame для постоянной отрисовки холста, не вызывая renderAll для каждого объекта.
Однако, в большинстве случаев вы скорее всего будете использовать canvas.renderAll как onChange-вызов.
Возвращаясь к опциям для анимации - что же именно мы можем менять?
from: - позволяет менять начальное значение атрибута для анимации (если не хотим использовать текущее)
duration: - длительность анимации; по умолчанию - 500 ms
onComplete: - функция для вызова в конце анимации (callback)
easing: - функция easing (смягчение)
Все эти опции более менее очевидны, кроме наверное easing. Давайте посмотрим поближе.
По умолчанию, animate используют easeInSine-функцию для смягчения анимации. Если такой вариант не подходит, в Fabric имеется большой набор популярных easing-функций (доступных через объект fabric.util.ease).
Например, вот так можно подвинуть объект вправо, при этом “отпружинивая”” в конце:
Заметьте, что мы используем fabric.util.ease.easeOutBounce как опцию смягчения. Есть и другие популярные функции — easeInCubic, easeOutCubic, easeInElastic, easeOutElastic, easeInBounce, easeOutExpo и т. д.
Вот в принципе и все, что нужно знать о анимации. Теперь можно с легкостью делать интересные вещи — менять угол объекта, чтобы сделать его вращающимся; анимировать left/top чтобы его двигать; анимировать width/height для увеличения/уменьшения; анимировать opacity для появления/исчезания и т. д.
Фильтры изображений
В первой части этой серии мы узнали, как работать с изображениями в Fabric. Как вы наверное помните, для этого используется fabric.Image-конструктор, передавая в него -элемент.
Также есть метод fabric.Image.fromURL, с помощью которого можно создать объект прямо из строки URL. И конечно же эти fabric.Image-объекты можно кинуть на холст, где они отобразятся как и все остальное.
Работать с изображениями прикольно, а с фильтрами изображений — еще веселей! Fabric уже имеет несколько фильтров, а также позволяет легко определять свои.
Некоторые фильтры из Fabric вам наверное знакомы — удаление белого фона, перевод в черно-белый, негатив или яркость. А некоторые менее популярны — градиентная прозрачность, сепия, шум.
Так как же применить фильтр к изображению? Каждый fabric.Image-объект имеет filters-атрибут, который просто является массивом фильтров. Каждый элемент в этом массиве — или один из существующих в Fabric или собственный фильтр.
Ну вот, к примеру, сделаем картинку черно-белой:
А вот так можно сделать сепию:
С атрибутом filters можно делать все тоже что и с обычным массивом — удалить фильтр (с помощью pop, splice, или shift), добавить фильтр (с помощью push, splice, unshift) или даже соединить несколько фильтров.
Когда вызывается applyFilters, все фильтры в массиве применяются к картинке по очереди. Вот, например, давайте создадим картинку с увеличенной яркостью и с эффектом сепии:
Заметьте, что мы передали { brightness: 100 }-объект в Brightness-фильтр. Это потому, что некоторым фильтрам ничего дополнительного не нужно; а некоторым (например - grayscale, invert, sepia) надо указать определенные параметры.
Для фильтра яркости это собственно само значение яркости (0-255). У фильтра шума, это значение шума (0-1000). А у фильтра удаления белого фона (‘remove white’) есть порог (‘threshold’) и расстояние (‘distance’).
Ну вот разобрались с фильтрами; пора создать свой!
Образец для создания фильтров будет довольно прост. Нам нужно создать ‘класс’’ и написать метод applyTo. Опционально мы можем дать фильтру toJSON-метод (поддержка JSON-сериализации) и/или initialize (если фильтр имеет дополнительные параметры):
Не вникая сильно в подробности кода, стоит заметить, что самое главное происходит в цикле, где мы меняем зеленую (data[i+1]) и голубую (data[i+2]) компоненты каждого пикселя на 0, по сути дела удаляя их.
Красная компонента остается нетронутой, что и делает все изображение красным.
Примечание чтеца: для желающих - более подробно аналог представленного выше кода описан в моей статье Canvas - Raw Pixel.
Как видите, applyTo-метод получает в себя canvas-элемент, который представляет собой изображение. Имея такой canvas, мы можем пройтись по всем пикселям изображения (getImageData().data) изменяя их как нам угодно.
Цвета
Независимо от того, с чем вам удобней работать — hex, RGB, или RGBA-форматами цвета — Fabric упрощает утомительные операции и переводы из одного формата в другой.
Давайте посмотрим на несколько способов определить цвет в Fabric:
Перевод формата происходит очень просто. Метод toHex() переводит цвет в hex. Метод toRgb() — в RGB, а метод toRgba() — в RGB с альфа каналом (прозрачностью):
Кстати, можно делать не только перевод. Можно “накладывать” цвета один на другой или делать из них черно-белый вариант:
Градиенты
Еще более экспрессивный способ работы с цветами — используя градиенты. Градиенты позволяет плавно смешать один цвет с другим, открывая возможность довольно изумительным эффектам.
Fabric поддерживает градиенты с помощью метода setGradient (setGradientFill до 1.1.0 версии), который присутствует на всех объектах.
Вызывание setGradient( ‘fill’, { … } ) - это почти как выставление значения ‘fill’ у объекта, только вместо цвета используется градиент:
В этом примере мы создаем круг в точке 100/100 с радиусом в 50px. Потом выставляем ему градиент, идущий по всей высоте объекта, от черного к белому.
Как видите, метод получает в себя конфигурационный объект, в котором могут присутствовать 2 пары координат (x1/y1 и x2/y2) и объект ‘colorStops’.
Координаты указывают, где градиент начинается и где он заканчивается. colorStops указывают - из каких цветов он состоит.
Вы можете определить сколько угодно цветов; главное, чтобы их позиции находились в интервале от 0 до 1 (например 0, 0.1, 0.3, 0.5, 0.75, 1). 0 представляет начало градиента, 1 — его конец.
Чтобы понять, как мы определили координаты, посмотрим на изображение ниже:
Так как координаты относительны центру объекта, то верхней точкой является -circle.height / 2, а нижней - circle.height / 2. Координаты по ширине (x1/x2) определяем точно так же.
Вот пример красно-голубого градиента, идущего слева направо:
А вот 5-ти шаговый градиент-радуга, с цветами, занимающими по 20% всей длины:
А вы можете придумать что-нибудь интересное?
Текст
Что если нужно отобразить не только картинки и векторные формы на холсте, а еще и текст? Fabric умеет и это! Встречайте fabric.Text.
Перед тем как говорить о тексте, стоит отметить, зачем мы вообще предоставляем поддержку работы с текстом.
Ведь canvas имеет встроенные методы fillText и strokeText.
Во-первых, для того чтобы можно было работать с текстом как с объектами. Выстроенные canvas-методы — как обычно — позволяют вывести текст на очень низком уровне.
А вот создав объект типа fabric.Text, мы можем работать с ним как и с любым другим объектом на холсте — двигать его, масштабировать, менять атрибуты, и т. д.
Вторая причина — чтобы иметь более богатый функционал, чем то что дает нам canvas. Некоторые вещи, которые есть в Fabric, но нет в родных методах:
Многострочность - Родные методы позволяют написать только одну линию, игнорируя переходы строки
Выравнивание текста - Влево, по центру, вправо. Полезно во время работы с многострочным текстом
Фон текста - Фон выводится только под самим текстом, в зависимости от выравнивания
Декорация текста - Подчеркивание, надчеркивание, перечеркивание
Высота строки - Полезно во время работы с многострочным текстом
Ну что ж, давайте посмотрим на вездесущий “hello world”?
Вот и все! Для показа текста необходимо всего лишь добавить объект типа fabric.Text на холст, указывая нужную позицию.
Первый параметр необходим — это собственно сама строка текста. Второй аргумент — опционально конфигурация, как обычно; можно указать left, top, fill, opacity, и т. д.
Помимо обычных атрибутов, у текстовых объектов конечно же есть и свои, относящиеся к тексту. Вкратце, об этих атрибутах:
fontFamily
По умолчанию - “Times New Roman”. Позволяет менять семейство шрифта для текста:
fontSize
Контролирует размер текста. Заметьте, что в отличие от других объектов в Fabric, мы не можем менять размер текста с помощью width/height. Вместо этого как раз и используется fontSize и конечно же scaleX/scaleY:
fontWeight
Позволяет сделать текст жирнее или тоньше. Точно также как в CSS, можно использовать или слова (“normal”, “bold”) или номерные значения (100, 200, 400, 600, 800).
Важно понимать, что для определенной толщины нужно иметь соответствующий шрифт. Если в шрифте не присутствует “bold” (жирный) вариант, например, то жирный текст может не отобразиться:
textDecoration
Позволяет добавить тексту перечеркивание, надчеркивание или подчеркивание. Опять же, эта декларация работает также, как в CSS.
Однако Fabric умеет даже немного больше, позволяя использовать эти декорации вместе (например, подчеркивание И перечеркивание), просто перечисляя их через пробел:
shadow
До версии 1.3.0 этот атрибут назывался “textShadow”.
Тень для текста. Состоит из 4-х компонент: цвет, горизонтальный отступ, вертикальный отступ, и размер размытия.
Это все должно быть знакомо, если вы до этого работали с тенями в CSS. Меняя эти 4 опции, можно добиться многих интересных эффектов:
fontStyle
Стиль текста. Может быть только один из двух: normal или italic. Опять же, работает так же, как и в CSS:
stroke и strokeWidth
Соединяя stroke (цвет наружнего штриха) и strokeWidth (ширину наружнего штриха), можно достичь довольно интересных эффектов.
Вот пара примеров:
Стоит отметить, что stroke был назван strokeStyle до версии 1.1.6
textAlign
Выравнивание полезно при работе с многострочным текстом. В однострочном тексте, выравнивание не видно, потому как ширина самого текстового объекта такая же как и длина строки.
Возможные значения: left, center, right и justify:
lineHeight
Еще один атрибут, скорее всего знакомый из CSS — lineHeight (высота строки).
Позволяет менять расстояние между строк в многострочном тексте. Вот пример текста с ‘lineHeight 3’ и второй с ‘lineHeight 1’:
textBackgroundColor
И наконец, дать тексту фон можно с помощью textBackgroundColor. Заметьте, что фон заполняется только под самим текстом, а не на всю “коробку”.
Чтобы закрасить весь текстовый объект, можно использовать атрибут backgroundColor. Также видно, что фон зависит от выравнивания текста и lineHeight.
Если lineHeight очень большой, фон будет видно только под текстом:
События
События — незаменимый инструмент для создания сложных приложений. Для удобства пользования и более детальной настройки, Fabric имеет обширную систему событий; начиная от низкоуровневых событий мыши и вплоть до высокоуровневых событий объектов.
События позволяют нам “поймать” различные моменты, когда что-то происходит на холсте.
Хотим узнать, когда была нажата мышка? Следим за событием mouse:down.
Как насчет, когда объект был добавлен на холст? Для этого есть object:added.
Ну, а что насчет перерисовки холста? Используем after:render.
API событий очень прост и похож на то, к чему вы скорее всего привыкли в jQuery, Underscore.js или других популярных JS-библиотеках.
Есть метод on для инициализации слушателя событий и есть метод off для его удаления.
Давайте посмотрим на пример:
Мы добавили слушатель события mouse:down на canvas-объекте и указали обработчика, который будет записывать координаты, где произошло это событие.
Таким образом, мы можем видеть, где именно произошел клик на холсте. Обработчик событий получает options-объект с двумя параметрами: e — оригинальное событие и target — Fabric-объект на холсте, если он найден.
Первый параметр присутствует всегда, а вот target - только если клик произошел на объекте. Ну и конечно же, target передается только обработчикам тех событий, где это имеет смысл.
Например, для mouse:down но не для after:render (так как это событие не “имеет” никаких объектов, а просто обозначает что холст был перерисован):
Этот пример выведет ‘an object was clicked!’ если мы нажмем на объект. Также покажется тип этого объекта.
Какие еще события доступны в Fabric?
На уровне мышки, у нас есть mouse:down, mouse:move и mouse:up.
Из общих есть after:render.
Есть события, касающиеся выбора объектов: before:selection:cleared, selection:created, selection:cleared.
Ну и конечно же, события объектов: object:modified, object:selected, object:moving, object:scaling, object:rotating, object:added и object:removed.
Стоит заметить, что события типа object:moving (или object:scaling) происходят постоянно, во время движения или масштабирования объекта, даже если на один пиксель.
В то же время, события типа object:modified или selection:created происходят только в конце действия (изменение объекта, создание группы объектов, и т. д.).
В предыдущих примерах мы присоединяли слушателя на canvas объект ( canvas.on( ‘mouse:down’, … ) ).
Как вы наверное догадываетесь, это означает, что события распространяются только на тот холст, к которому мы их присоединили. Если у вас несколько холстов на странице, вы можете дать им разные слушатели. События на одном холсте не распространяются на другие холсты.
Для удобства, Fabric позволяет добавлять слушатели прямо на Fabric объекты!
В этом примере слушатели “присоединяются” прямо к прямоугольнику и кругу. Вместо object:selected мы используем событие selected.
По такому же принципу, можно использовать событие modified (object:modified когда “вешаем” на холст), rotating (аналог object:rotating) и т. д.
Вы можете ознакомиться с событиями поближе и прямо в реальном времени вот в этой демо.
На этом 2-я часть подошла к концу. Столько всего нового, но это еще не все! В 3-й части мы рассмотрим группы объектов, сериализацию/десериализацию холста и формат JSON, SVG-парсер, а также создание подклассов.
Вот набрел на интересную библиотеку для удобной работы с Canvas - Fabric.js. Не мог не пройти мимо нее, так как люблю Canvas и стараюсь узнать все о нем, по мере сил.
Ниже приведена полная версия первой части официальной документации по Fabric.js - Знакомимся с Fabric.js. Часть 1-я. Здесь я сделал ее вычитку и исправил кое-какие орфографические ошибки.
Статья понравилась еще тем, как автор объяснил чтение SVG-команд в файле.
Введение
Сегодня я хочу познакомить вас с Fabric.js — мощной Javascript-библиотекой для работы с HTML5 canvas.
Fabric включает в себя объектную модель, которой так не хватает при работе с canvas, а так же SVG-парсер, интерактивный слой и множество других незаменимых инструментов.
Это полностью открытая библиотека с MIT-лицензией и многими взносами разработчиков за последние несколько лет.
Работу над Fabric я начал 3 года назад, когда понял, насколько тяжело работать с обычным API Canvas.
В тот момент я создавал интерактивный редактор на printio.ru — мой стартап, где мы даем возможность создать дизайн и напечатать его на одежде или других товарах. Редактор хотелось сделать удобным и суперинтерактивным.
В то время такой функционал можно было создать только во Flash. Но Flash использовать я не хотел. Я предпочитаю JavaScript и был уверен, что с ним можно добиться многого. Получилось довольно неплохо. :)
Даже сейчас очень немногие визуальные редакторы могут делать то, что можно достичь с помощью Fabric.
Зачем это нужно?
В последнее время популярность Canvas растет и люди на нем делают довольно поразительные вещи. Проблема в том, что родной API Canvas ужасно низко-уровневый.
Одно дело, если нужно нарисовать несколько простых фигур или графиков и забыть о них. Другое — интерактивность, изменение картинки в какой-то момент или рисование более сложных фигур.
Вот именно для этого и нужна Fabric.js
Дело в том, что обычные canvas-методы позволяют нам вызывать только очень простые графические команды, вслепую меняя целый bitmap холста (canvas).
Нужно нарисовать прямоугольник? Используем fillRect(left, top, width, height). Нарисовать линию? Используем комбинацию moveTo(left, top) и lineTo(x, y). Как будто рисуем кисточкой по холсту, накладывая все больше и больше краски, почти без какого-либо контроля.
Fabric дает нам объектную модель поверх низко-уровневых методов canvas, хранит состояние холста и позволяет работать с обьектами напрямую.
Давайте посмотрим на разницу между canvas и Fabric. Допустим, нужно нарисовать красный прямоугольник. Используя API Canvas, это делается приблизительно так:
А вот тоже самое с Fabric:
Разницы в размере кода пока не видно. Однако, видно что, способ работы с canvas кардинально отличается.
В обычном API Canvas мы работаем с контекстом. Контекст — это объект, который по сути представляет из себя bitmap холста.
С Fabric мы управляем именно объектами — создаем и меняем параметры, добавляем их на canvas. Как видите, эти объекты — полноценные жители в Fabric (объекты первого класса).
Рисовать красный прямоугольник — это конечно не серьезно. Давайте сделаем с ним что-нибудь интересное. Например, повернем на 45 градусов.
Сначала, используя обычные методы:
… и теперь с помощью Fabric:
Что здесь происходит? Используя Fabric, все что надо было сделать, это добавить значение угла с 45 градусами. А вот с обычными методами все не так-то просто.
Во-первых, мы не можем управлять объектами напрямую. Вместо этого, приходится менять позицию и угол самого bitmap (ctx.translate, ctx.rotate). Потом рисуем прямоугольник, при этом не забывая отодвинуть bitmap соответственно (-10, -10), так, чтобы прямоугольник появился на 100/100. Еще надо не забыть перевести угол из градусов в радианы при повороте bitmap.
Теперь вам, наверное, становится понятно, зачем существует Fabric.
Давайте посмотрим на еще один пример — хранение состояния canvas.
Представим, что в какой-то момент нам нужно подвинуть этот красный прямоугольник в другое место. Как это сделать, не имея возможности управлять объектами? Вызывать fillRect еще раз?
Не совсем. Вызывая еще одну команду fillRect, прямоугольник рисуется прямо поверх всего bitmap. Именно поэтому я привел аналог кисти с краской.
Чтобы подвинуть фигуру, нам нужно сначала стереть предыдущий результат, а потом уже рисовать на новом месте:
А теперь с Fabric:
Заметьте очень важную разницу. Нам не пришлось абсолютно ничего стирать перед рисованием.
Просто продолжаем работать с объектами, меняя их атрибуты, а потом перерисовываем canvas, чтобы увидеть изменения.
Таким образом можно изменить десятки объектов и в конце одной командой обновить экран.
Объекты
Мы уже видели, как работать с прямоугольниками, используя fabric.Rectконструктор. Но, конечно же, Fabric предоставляет многие другие простые фигуры: круги, треугольники, эллипсы и т. д.
Все они доступны из fabric объектов, соответственно: fabric.Circle, fabric.Triangle, fabric.Ellipse и т. д.
Нужно нарисовать круг? Просто создаем соответствующий объект и добавляем его на холст. Тоже самое с другими формами:
… и вот уже на холсте красуется зеленый круг в точке 100/100 и синий треугольник в точке 50/50.
Управляем объектами
Создание визуальных фигур — это только цветочки. В какой-то момент наверняка понадобится их менять.
Возможно, какие-то действия пользователя должны сказываться на состоянии картинки (холста) или должна быть запущена анимация.
Или же нужно поменять атрибуты объектов (цвет, прозрачность, размер, позицию) в зависимости от движений мышки.
Fabric берет на себя заботу о состоянии холста и перерисовке. От нас требуется менять только сами объекты.
В предыдущем примере было видно, как метод set подвинул объект на новую позицию set({ left: 20, top: 50 }). Точно также можно менять любые другие атрибуты, которых доступно несколько.
Да, отобразить зеркально повернутую картинку в Fabric на удивление легко — просто присваиваем true в атрибут *flip**.
Чтение атрибутов происходит с помощью метода get, присваивание — с помощью метода set.
Давайте как-нибудь поменяем наш прямоугольник:
Мы выставили ‘fill’ значением ‘red’, меняя цвет объекта на красный. Затем поменяли ‘strokeWidth’ и ‘stroke’, что добавляет прямоугольнику 5-пиксельную рамку светло-зеленого цвета. И наконец, меняем атрибуты ‘angle’ и ‘flipY’. Заметьте, как три выражения используют слегка разный синтаксис.
Отсюда видно, что set() — довольно универсальный метод. Он предназначен для частого использования, поэтому заточен под удобство.
Ну, а как насчет чтения? Я уже упомянул, что есть общий get(), а также набор конкретных get()* методов.
Например, для получения ‘width’ объекта можно использовать get(‘width’) или getWidth(). Для ‘scaleX’ — get(‘scaleX’) или getScaleX(), и т. д.
Такие специальные методы, как getWidth() и getScaleX() существуют для всех ‘публичных’ атрибутов объекта (‘stroke’, ‘strokeWidth’, ‘angle’, и т. д.).
Вы наверное заметили, что в предыдущих примерах были использованы конфигурационные хэши, которые выглядели точно также, как и те, которые мы только что использовали в методе set. Это потому, что они действительно одинаковые. Объект может быть “сконфигурирован” в момент создания или позже, с помощью метода set.
Синтаксис при этом абсолютно одинаковый:
Атрибуты по умолчанию
У всех объектов в Fabric есть набор значений по умолчанию. Они используются, когда во время создания мы не задаем другие значения. Привожу пример.
Прямоугольник получил значения по умолчанию. Он находится в позиции 0/0, черного цвета, непрозрачный, не имеет ни рамок, ни габаритов (ширина и высота равны нулю).
Из-за этого мы его и не видим. Как только устанавливаем позитивные width/height, черный прямоугольник появляется в левом верхнем углу.
Иерархия и Наследование
Объекты Fabric не существуют сами по себе. Они формируют четкую иерархию. Большинство объектов наследуют от fabric.Object.
fabric.Object — это абстрактная 2-мерная фигура на плоскости. Она имеет left/top и width/height атрибуты, а также набор других визуальных параметров.
Те атрибуты, которые мы видели ранее (fill, stroke, angle, opacity, flip** и т. д.) принадлежат всем Fabric-объектам, которые наследуют от *fabric.Object.
Такое наследование очень удобно. Оно позволяет нам определить методы на fabric.Object, таким образом делая его доступным во всех “классах”-потомках.
Например, если нужен метод getAngleInRadians на всех объектах, просто создаем его на fabric.Object.prototype:
Как видите, метод теперь доступен всем объектам.
Разумеется, классы-потомки могут не только наследовать от fabric.Object, но и определять свои собственные методы и параметры.
Например, в fabric.Circle существует дополнительный атрибут ‘radius’. Или возьмем к примеру fabric.Image, с которым мы познакомимся подробнее чуть позже.
В нем имеются методы getElement/setElement, предназначенные для чтения\записи HTML элемента , на котором основан объект типа fabric.Image.
Canvas (холст)
Мы подробно рассмотрели объекты; давайте опять вернемся к canvas.
Как видно из примеров, первый шаг - это создание самого “холста” для рисования — new fabric.Canvas(‘…’).
fabric.Canvas - это, по сути, оболочка вокруг canvas-элемента, ответственная за управление всеми содержащимися на нем объектами. Конструктор берет id элемента и возвращает объект типа fabric.Canvas.
Теперь в него можно добавлять объекты ( add() ), а также их читать ( item(), getObjects() ) или удалять ( remove() ):
Как мы уже выяснили, главная задача объекта fabric.Canvas - это управление объектами, которые находятся на canvas.
Также его можно сконфигурировать через набор параметров. Такие настройки, как изменение фона холста, скрывание объектов по маске, изменение общей длины/ширины, включение/выключение интерактивности - эти и другие опции можно выставить прямо на fabric.Canvas как во время создания, так и позже:
.. или
Интерактивность
Одна из самых уникальных возможностей Fabric, встроенная прямо в ядро - это слой интерактивности. Он позволяет пользователю манипулировать объектной моделью, с которой мы только что ознакомились.
Объектная модель существует для програмного доступа. А что нужно, чтобы управлять объектами мышкой (или touchpad на мобильных устройствах)?
Для этого в Fabric заложен функционал пользовательского доступа. Как только мы создаем холст через new fabric.Canvas(‘…’), объекты, расположенные на нем, сразу же можно выделять, двигать, масштабировать, вращать и даже группировать вместе, управляя ими как одним целым!
Если мы хотим дать возможность пользователю управлять объектами на холсте - допустим, картинкой - нужно всего лишь создать холст и добавить на него объект. Больше не нужно никаких дополнительных настроек.
Управлять этой интерактивностью легко. Для этого есть ‘selection’ флаг на холсте, а также ‘selectable’ флаг на индивидуальных объектах.
А что делать, если интерактивность вовсе не нужна? Тогда просто меняем fabric.Canvas на fabric.StaticCanvas.
Синтаксис (конфигурация, методы) абсолютно идентичный, просто используем слово StaticCanvas вместо Canvas.
Это создает облегченную версию холста, без лишней логики для интерактивности и управления event-ами. Все остальное остается таким же. Мы получаем полную объектную модель, можем добавлять, удалять и менять объекты, ну и конечно же менять опции самого холста. Исчезает только управление внешними event-ами.
В дальнейшем, когда мы ознакомимся с возможностью кастомной сборки Fabric (custom build), вы увидете, что можно создать более легкую версию библиотеки под ваши нужды.
Это может быть полезно если, например, нужно просто отобразить статичный график, SVG-фигуру или картинки с фильтрами.
Картинки
Кстати, насчет картинок …
Все-таки работа с простыми фигурами не так интересна, как с более графически насыщенными картинками. Как вы наверное уже догадываетесь, в Fabric это очень просто.
Создаем fabric.Image объект, добавляем его на холст:
Заметьте, как мы передаем **-элемент в конструктор *fabric.Image*. Таким образом мы создаем объект типа *fabric.Image*, который представляет собой картинку из данного элемента.
Мы также выставляем left/top значения на 100/100, угол на 30 и прозрачность на 0.85. После добавления на холст картинка рендерится в позиции 100/100, повернутая на 30 градусов и слегка прозрачная! Неплохо …
А что же делать, если элемента картинки в документе не существует, если есть только ее адрес? Это не страшно. В таком случае можно использовать fabric.Image.fromURL:
Здесь никаких сюрпризов. Вызываем fabric.Image.fromURL передавая адрес картинки, а также функцию (callback), которую надо вызвать когда картинка загрузится.
Callback получает первым аргументом объект fabric.Image. В момент вызова, с ней можно делать что угодно - изменить или сразу добавить на холст для показа:
Path и PathGroup
Мы ознакомились с простыми фигурами и картинками. Теперь перейдем к более сложному контенту.
Встречайте мощную и незаменимую пару: Path и PathGroup.
Path (дословно переводится “путь”) в Fabric представляет из себя кривую фигуру, которая может быть залита цветом, иметь контур, быть измененной любым способом.
Она изображается набором команд, которые можно сравнить с рисованием ручкой от одной точки до другой. При помощи таких команд как ‘move’ (двинуть), ‘line’ (линия), ‘curve’ (кривая), или ‘arc’ (арка), Path могут воспроизводить удивительно сложные фигуры. А с помощью Path-групп (PathGroup), все становится возможным.
Paths в Fabric имеют сходство с SVG **-элементами. Они используют одинаковый набор команд, могут быть созданы из **-элементов и сериализованы в них.
О сериализации и SVG-парсинге мы поговорим позже. Сейчас стоит сказать, что работать с Path-объектами вы вряд-ли будете вручную. Вместо этого имеет смысл использовать SVG-parser, встроенный в Fabric. Чтобы понять, что же из себя представляют эти Path-объекты, давайте создадим один из них.
При создании объекта fabric.Path мы передаем строку с инструкциями “черчения” кривой. Выглядит эта инструкция, конечно, очень загадочно, но понять ее на самом деле довольно легко.
‘M’ означает ‘move’ (двинуть) и говорит невидимой ручке подвинуться в точку 0, 0. ‘L’ означает ‘line’ (линия) и рисует линию до точки 200/100. Затем команда ‘L’ рисует линию до 170/200. И наконец, ‘z’ заставляет невидимую ручку замкнуть текущий контур и завершить фигуру. Как результат, получается вот такая треугольная форма.
Объект fabric.Path такой же, как и остальные объекты в Fabric, поэтому мы легко изменили его параметры (left/top). Но можно изменить и большее:
Ради интереса, давайте посмотрим на еще один контур, на этот раз более сложный. Вы поймете, почему создание контуров вручную — не самое веселое занятие:
Ого-го, что же здесь происходит?! Давайте разбираться.
‘M’ все еще означает ‘move’ (двинуть) команду и вот невидимая ручка начинает свое путешествие от точки ‘121.32, 0’. Затем идет команда ‘L’, которая приводит ее к точке ‘44.58, 0’. Пока все просто.
А что следующее? Команда ‘C’ означает cubic bezier (кривая безье). Она принуждает ручку рисовать кривую в точку ‘36.67, 0’. Кривая использует ‘29.5, 3.22’ как точку контроля в начале линии и ‘24.31, 8.41’ как точку контроля в конце линии.
Далее следует целая мириада остальных кривых безье, что в итоге и создает финальную фигуру:
С такими “монстрами” вручную работать вы наверняка не будете. Вместо этого, можно использовать очень удобный метод fabric.loadSVGFromString или fabric.loadSVGFromURL, загружающий целый SVG-файл. Все остальное сделает parser Fabric, пройдя по всем SVG-элементам и создавая соответствующие Path-объекты.
Кстати, что касается SVG-документов, Path в Fabric обычно представляет SVG **-элемент, а вот наборы таких элементов, которые очень часто можно найти в SVG документах, обычно представлены через PathGroup (*fabric.PathGroup*-объекты).
PathGroup — это всего лишь группа Path-объектов. Так как fabric.PathGroup наследует от fabric.Object, такие объекты могут быть добавлены на холст как и любые другие объекты Fabric. Конечно же ими можно управлять, как и всем остальным.
Напрямую с ними работать скорее всего не придется. Если они вам попадутся во время работы с Fabric, просто имейте ввиду с чем имеете дело, и зачем они вообще нужны.
Послесловие
Мы затронули только самые базовые аспекты Fabric. Разобравшись с ними, вы с легкостью сможете создать как простые, так и сложные фигуры или картинки.
Их вы сможете показать на холсте, поменять (через атрибуты позиции, масштаба, угла, цвета, контура, прозрачности) и сделать с ними все, что душа пожелает.
В следующей части, мы поговорим о работе с группами, анимацией, текстом, SVG парсингом, рендерингом и сериализацией, управлением событиями, фильтрами картинок и остальными интересными вещами.
В canvas создаем круговую индикацию часов, минут и секунд.
Делает это будем при помощи canvas-метода arc():
В приведенном выше коде остановимся на некоторых моментах.
Первый момент - метод arc() принимает аргументы для угловых значений в радианах. Для человека этот формат непривычен, так как мы пользуемся градусами.
Поэтому для удобства создадим функцию degreeToRadian, которая преобразовывает градусы в радианы.
Второй момент - начальный угол (точка отсчета) по стандарту canvas (я не ошибся?) - это 0. Это соответствует трем часам (15:00) на часовом циферблате.
Конечно, нас это не устраивает, поэтому смешаем начальную точку на 270 градусов (по часовой стрелке) так, чтобы эта точка находилась в положении 12 часов (12:00) - degreeToRadian(270).
Второй аргумент - конечный угол - будет принимать значение угла динамически, из объекта now:
Из полученного значения нам необходимо вычесть 90 градусов потому, что хоть начальную точку мы и сместили на 270 градусов, отсчет продолжает вестись из начальной точки 0 (которая на 15:00). Почему так происходит, я так и не понял - но код работает.
Результирующее значение умножаем на 15 или 6 для кратности - эти величины взяты чисто эмпирически. В итоге получится красивые и “смотрибельные” окружности\стрелки.
Последний момент - сделаем для всего этого “дела” обертку-функцию renderTime и “запихнем” ее в тайминговую функцию setInterval, с частотой выполнения 40 миллисекунд.
Таким образом мы оформим анимацию отрисовки окружностей\стрелок и наши часы станут настоящими (почти) часами.
Пора взглянуть по получившийся результат, ибо иначе теряется “связь с реальностью”. Готовый код можно посмотреть здесь - JavaScript Canvas Clock - Start.
Небольшое примечание - для большей наглядности примера в код были добавлены толщина линий (lineWidth), цвет линий (strokeStyle), скругление концов линий (lineCap), а также фоновая заливка (fillStyle, fillRect) всего canvas:
И еще один момент - нужно обратить внимание на новую строку:
Что она делает? Она просто получает текущее значение миллисекунд, дробит это значение на еще меньшее значение ( miliSeconds / 1000 ) и прибавляет полученный результат к значению секунд (now.getSeconds).
Зачем это делается? С одной лишь целью - сделать отрисовку секундной окружности более плавной; иначе шаг прибавления будет слишком большим и резким - получится некрасиво.
В результате код для отрисовки секундной окружности будет таким:
Часы - дата и время
Для большей красоты и информативности можно продублировать дату и время, которые можно вывести в виде текста. В этом случае нужно воспользоваться методом fillText, а также воспользоваться методом font для настройки отображения шрифта:
Пример с часами можно существенно украсить, заменив простую фоновую заливку цветом на градиентную заливку.
В canvas существует два типа градиента - линейный и радиальный. В данном случае будет применяться радиальный градиент.
Градиенты в canvas добавляются совсем не так, как в CSS. Здесь это объект, у которого есть свой метод addColorStop(); создается градиент при помощи функции-конструктора createLinearGradient(x,y,x1,y1) или createRadialGradient(x,y,r,x1,y1,r1).
Изменим код для создания фоновой заливки из предыдущего примера таким образом:
Здесь цвет, идущий (позиция 0) из центра радиального градиента - это:
… а цвет, на котором радиальный градиент останавливается (позиция 1), это:
Не забываем изменить строку clock.fillStyle = ‘#000’; на строку clock.fillStyle = gradient; и посмотрим на готовый результат - JavaScript Canvas Clock - Radial Gradient.
В коде выше были использованы еще два метода canvas для создания тени - shadowBlur (размытие тени) и shadowColor (цвет тени). Так результат смотрится лучше.
canvas в image
Напоследок можно воспользоваться методом canvas под названием toDataURL. Этот метод может брать текущий отрисованный canvas и конвертировать его в картинку (в формате data URI).
Эту картинку можно использовать как угодно, но в том числе - вставлять на HTML-страницу. Этот подход можно применить и заменить картинкой canvas.
В результате canvas будет генерировать картинку с частотой 40 миллисекунд и с такой же частотой вставлять ее на страницу. А canvas убрать. Визуально подмены не будет заметно.
Можно проверить, правда ли на странице картинка, а не canvas. Элементарно - правый клик мыши на изображении и смотрим - в контекстном меню есть пункт “Save image as…”. Будь на этом месте canvas, такого “фокуса” бы не получилось.
Делается это в угоду браузеров, которые не понимают canvas. Правда, такой способ “подвешивает” браузер со страшной силой (по крайней мере, так дело обстоит у меня). Оно и понятно (если я не ошибаюсь) - попробуйте рисовать каждые 40 миллисекунд новую картинку )
Ниже - код:
Тут стоит заметить один важный момент - метод toDataURL() относится к canvas, но не к ‘2d’-контексту canvas’а.
Библиотека LoDash - это развитие и продолжение библиотеки Underscore.js. Если выбирать, какой библиотекой пользоваться, то выбор будет однозначно за LoDash. Достаточно сравнить колличество методов Underscore.js vs LoDash - LoDash Documentation.
Еще один большой плюс LoDash - это ее модульность. Каждый ее метод доступен в виде отдельного модуля \ модулей, который \ которые можно подключить и использовать; а не “тянуть” всю библиотеку (хотя она и небольшая - 4\22 kB).
Например, подключить только методы для работы с массивами:
Или же подключить только один метод chunk, чтобы уменьшить итоговую сборку:
Или вот так - нам нужны только два модуля random и foreach для генерации случайный фоновых заливок у коллекции блоков.
Тогда можно поступить так - устанавливаем всего два модуля вместо всей библиотеки lodash:
… и используем их, ибо только они нам нужны сейчас:
В этой статье я познакомлюсь с пятью методами LoDash, которые я отложил “на потом” при знакомстве с Underscore.js. Это методы для работы с функциями - .delay(), .once(), .after(), .before(), .debounce().
lodash - .delay()
Метод .delay() очень прост, но тем не менее - весьма полезен на практике. Это возможность запускать функцию с временной задержкой.
Самый простой пример - через 2 секунды в консоли браузера появится сообщение Hello World:
Справка по этому методу в официальной документации - Lodash - Delay.
lodash - .once()
Весьма полезный метод, который позволяет запустить функцию только один раз. Приведу два примера.
Простой пример с кнопкой - alert выскочит только один раз при нажатии кнопки:
Второй пример интереснее. Посмотрим на разметку. Затем на javascript-код.
Эта часть кода - украшательство, генерирование случайного цвета для каждого из блоков class=”block” при помощи метода .random():
Здесь функция callOnce() сработает только один раз и при условии, когда блок secondo “поднимется” от нижней границы окна браузера на высоту 85% от общей высоты окна.
iterrator “отщекнется” один раз и изменит содержимое блока id=”count” с 0 на 1:
Весьма полезный на практике прием. Официальная справка по методу .once() находится здесь - LoDash - Once Method.
lodash - .before()
Метод .before() - счетчик выполнения функции; метод запустит функцию менее n-раз (less than n times). В приведенном ниже примере клик по элементу #before сработает 9 раз и запустит функцию 9 раз (и не более того):
Можно сказать и так - метод .before() ограничивает максимальное количество запусков функции - не более n-раз.
Метод .after() - еще один счетчик выполнения функции. Прямая противоположность методу .before().
Этот метод ограничивает количество попыток запуска функции - после n-попыток функцию можно будет запускать неограниченное количество раз:
Код выше выведет в консоль браузера текст After только после пятой попытки и далее сколько угодно раз. Где на практике можно применить такой подход - не приходит на ум, если честно )
Официальная документация по методу .after() находится здесь - LoDash - After Method.
lodash - .debounce()
Интересный и полезный на практике метод - запустить функцию function с временной задержкой; отсчет временной задержки идет с момента последнего запуска функции function.
Метод имеет большое количество аргументов - ссылка на официальную документацию - Lodash - Debounce Method.
Пример ниже будет выводить окно Call me after 2 second! с задержкой в 2 секунды после события resize - то есть, когда будут изменены размеры окна браузера:
Заключение
Вот я в меру своих сил и освоил несколько интересных методов библиотеки LoDash. Наиболее полезными для меня показались методы .debounce() и .once().
Что сказать - библиотека LoDash просто замечательная и чрезвычайно полезная. К тому же она постоянно развивается - ее автор John-David Dalton не устает ее совершенствовать.
Немного пофлудил по JavaScript … останавливаться пока не собираюсь )