MongoDB

Что такое collection

В базах данных MongoDB данные объединяются в коллекции - collection. В одной базе данных может быть от одной до многих collections.

Смысл collection - объединять однотипные данные. То есть данные, котороые можно объединить по какому-либо признаку.

Например, в базе данных animals может быть две collections - cats и dogs. В коллекции cats хранятся все данные, о которых можно сказать - “это данные по кошкам”. В коллекции dogs хранятся “все данные по собакам”.

Создание collection

В базе данных создать collection можно командой:

db.createCollection('NAME_COLLECTION')

Например, я создам две коллекции cats и dogs в базе данных animals. Для этого создам базу данных animals:

> use animals
switched to db animals
>

Создам коллекцию cats:

> db.createCollection('cats')
{ "ok" : 1 }

Создам коллекцию dogs:

> db.createCollection('dogs')
{ "ok" : 1 }

Посмотреть список существующих колекций базы данных можно командой:

show collections

Проверю, создались ли успешно коллеции cats и dogs в базе данных animals:

> show collections
cats
dogs

Переименование collection

Операция переименования collection в MongoDB выполняется командой:

db.collection.renameCollection('NEW_NAME')

Например, я создал коллекцию bird в базе данных animals:

> db.createCollection('bird')
{ "ok" : 1 }
> show collections
bird
cats
dogs
>

… и хочу переименовать эту коллецию в birds:

> db.bird.renameCollection('birds')
{ "ok" : 1 }
> show collections
birds
cats
dogs
>

В результате коллекция bird успешно переименована в birds.

Удаление collection

В MongoDB удаление коллекции выполняется командой:

db.COLLECTION_NAME.drop()

Например, я хочу удалить коллекцию birds из базы данных animals:

> db.birds.drop()
true
> show collections
cats
dogs
>

Коллекция birds успешно удалена из базы данных.

Создание collection - автоматический способ

В MongoDB имеется способ автоматического создания collection - путем добавления документа в новую коллецию при помощи метода insert.

Например, коллекции insects в базе данных animals не существует. В будущую коллекцию insects я добавлю документ cockroach и тем самым автоматически создам коллецию insects:

> db.insects.insert({ name: 'cockroach' })
WriteResult({ "nInserted" : 1 })
> show collections
cats
dogs
insects
>

На этом все.


MongoDB

Создание базы данных

В двух предыдущих примерах научились устанавливать MongoDB. А также научились запускать и останавливать сервер MongoDB.

Настало время научиться создавать базы данных в MongoDB. Для этого нужно запустить и зайти в командную оболочку MongoDB.

Командная оболочка MongoDB носит имя mongo-shell и запускается в Linux одной командой:

mongo

В консоли отобразится сообщение и приглашение командной строки, говорящее о том, что мы находимся в командной оболочке MongoDB:

MongoDB shell version v3.4.4
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.4
...
> 

Список баз данных

Увидеть список всех существующих баз данных можно командой:

show dbs

Вот список баз данных на момент установки MongoDB в системе Linux. Видно, что обе базы пустые:

> show dbs
admin  0.000GB
local  0.000GB
> 

Создать базу данных

Команда создания новой базы данных в MongoDB очень проста:

use DATABASE_NAME

Например, создам новую базу данных по имени users:

> use users
switched to db users
> 

Команда user универсальная. Если база данных users уже существует, то будет выполнен просто переход в эту базу данных.

Если базы данных users не существует, то она будет создана и будет выполнен автоматический переход в эту базу данных.

Имя базы данных

Узнать имя текущей ( в которой на данный момент нахожусь ) базы данных можно командой:

db

В моем случае это будет так:

> db
users
>

Удаление базы данных

Удалить базу данных можно командой:

db.dropDatabase()

Важный момент - нужно находиться в той базе данных, которую необходимо удалить. Команда dropDatabase() не принимает аргументов.

Например, я хочу удалить базу данных users:

> show dbs
admin  0.000GB
local  0.000GB
users  0.000GB
>

Для этого я перейду в базу данных users:

> use users
switched to db users
>

И выполню команду dropDatabase():

> db.dropDatabase()
{ "dropped" : "users", "ok" : 1 }
>

Посмотрю список баз данных после удаления базы users и удостоверюсь, что она удалена успешно:

> show dbs
admin  0.000GB
local  0.000GB
>

На этом все.


MongoDB

Управление MongoDB

MongoDB управляется так же, как и все остальные процессы в системе Linux.

Запуск MongoDB

Сервер MongoDB запускается командой:

sudo service mongod start

Будет “висеть” в фоновом режиме и слушать команды на порту 27017.

Остановка MongoDB

Остановить сервер MongoDB можно командой:

sudo service mongod stop

Перезапуск MongoDB

Если нужно перезапустить сервер MongoDB, то это выполняется командой:

sudo service mongod restart

Статус MongoDB

Посмотреть статус сервера MongoDB можно командой:

sudo service mongod status

Покажет, запущен сервер MongoDB и его текущее состояние.


Что такое m4b

Формат 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. Программу нужно устанавливать вручную, набором нескольких команд.

Итак, открываем терминал и последовательно запускаем на выполнение команды:

sudo apt-get install python-qt4
sudo apt-get install libcanberra-gtk-module
sudo apt-get install faac
sudo apt-get install libmp4v2-2
sudo apt-get install mp4v2-utils
sudo apt-get install sox
sudo apt-get install libsox-fmt-mp3

Можно объединить все в одну команду, конечно, но я оставил все команды разделенными для большей ясности процесса.

Установку пакетов я производил под 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.zip, получаем разархивированную директорию m4baker-master
  • открываем директорию m4baker-master в терминале и запускаем команду установки программы m4Baker:
sudo python setup.py install --optimize=1

Эта команда установит программу m4Baker со всеми ее зависимостями.

Теперь можно запустить m4Baker любым удобным способом - через Start Menu, из командной строки консоли. У меня m4Baker успешно запустился через лаунчер Synapse.

Обзор m4Baker

m4Baker благодаря использованию SoX, faac и mp4v2 поддерживает большинство популярных аудио-форматов, есть возможность изменения и добавления метаданных, добавления обложки.

Первый запуск m4Baker выглядит таким образом:

m4Baker - First Launch

Справка, представленная в самой программе m4Baker, является самодостаточной - ее полностью хватает для пользования.

Для загрузки mp3-файлов нужно нажать кнопку “Add Audiobook” ( иконка книги ), откроется диалоговое окно выбора файлов.

После загрузки файлы добавятся в список обработки:

m4Baker - Add Files

Стоит обратить внимание, что выборка должна быть “чистой” - только mp3-файлы и никакого другого “мусора”, иначе программа m4Baker не запустится для обработки файлов.

В создаваемой m4b-аудиокниге возможно автоматическое разделение на части, возможна сортировка исходных файлов по имени или номеру трека (ID3-тегу), создание маркеров глав, перемещение глав между аудиокнигами:

m4Baker - Chapters

m4Baker - Sort Chapters

m4Baker - Split Book

Нажатие на кнопку “Procell all” ( иконка шестеренки ) запустит процесс создания файла формата m4b:

m4Baker - Process

Замечания

OS Linux

Я протестировал приложение m4Baker на рабочей машине Linux Mint 18 Cinnamon. Файлы mp3-формата аудиокниги были успешно сконвертированы в файл формата m4b. На Linux Desktop данный файл успешно прослушивался.

m4Baker - Play m4b File

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 нет достойной альтернативы.

В написании данного материала мне помог ресурс - Baking audiobooks with m4Baker.

На этом все. Всем удачного Нового Года!


Вторая часть официальной документации по Fabric. Речь идет об анимации объектов на Canvas, применении стандартных фильтров к изображениям или создании пользовательских фильтров.

Вычитал и поправил некоторые орфографические ошибки. Продолжает радовать факт, как автор детально и внятно описывает работу каждой строки кода в сниппетах. Эту статью можно продолжать читать просто как общий легкий курс по JavaScript. )


Это вторая часть серии статей об открытой Javascript Canvas библиотеке Fabric.js, которую мы используем на printio.ru для редактора дизайнов.

В первой части этой серии мы ознакомились с самыми базовыми аспектами canvas библиотеки Fabric.js. Мы узнали, чем может быть полезна Fabric, рассмотрели ее объектную модель и иерархию объектов; увидели, что существуют как простые фигуры (прямоугольник, треугольник, круг), так и сложные (SVG). Научились выполнять простые операции над этими объектами.

Ну вот, разобрались с азами, давайте приступать к более интересным вещам!

Анимация

Любая уважающая себя canvas-библиотека в наше время включает в себя средства работы с анимацией. Fabric — не исключение. Ведь мы имеем мощную объектную модель и гибкие графические возможности. Было бы грех не уметь это приводить в движение.

Вы наверное помните, как менять атрибут у объекта. Просто вызываем метод

1
set
, передавая соответствующее значение:

rect.set('angle', 45);

Анимировать объект можно по такому же принципу и с такой же легкостью. Каждый объект в Fabric имеет метод

1
animate
(наследуя от
1
fabric.Object
) который … анимирует этот объект:

rect.animate( 'angle', 45, {
  onChange: canvas.renderAll.bind(canvas)
});

Первый аргумент - это атрибут, который хотим менять. Второй аргумент — финальное значение этого атрибута.

Например, если прямоугольник находится под углом

1
-15°
и мы указываем
1
45°
, то угол постепенно изменится с
1
-15°
до
1
45°
.

Ну, а последний аргумент — опциональный объект для более детальных настроек (длительность, вызовы, easing и т. д. )

1
animate
кстати имеет очень полезную функциональность — поддержку относительных значений. Например, если нужно подвинуть объект на
1
100px
вправо, то сделать это очень просто:

rect.animate('left', '+=100', { onChange: canvas.renderAll.bind(canvas) });

По такому же принципу, для поворота объекта на 5 градусов против часовой стрелки:

rect.animate('angle', '-=5', { onChange: canvas.renderAll.bind(canvas) });

Вы наверняка заметили, что мы постоянно указываем вызов

1
onChange
. Разве 3-й аргумент не опциональный? Да, именно так.

Дело в том, что как раз это вызывание

1
canvas.renderAll
на каждый кадр анимации позволяет видеть саму анимацию!

Mетод

1
animate
всего лишь изменяет значение атрибута в течении указанного времени и по определенному алгоритму (
1
easing
).

1
rect.animate('angle', 45)
изменяет значение угла, при этом не перерисовывая экран после каждого изменения. А перерисовка экрана нужна для того, чтобы увидеть анимацию.

Ну, а почему же

1
animate
не перерисовывает экран автоматически? Из-за производительности. Ведь на холсте могут находиться сотни или даже тысячи объектов.

Было бы довольно ужасно, если каждый из объектов перерисовывал экран при изменении. В таком случае лучше использовать, например,

1
requestAnimationFrame
для постоянной отрисовки холста, не вызывая
1
renderAll
для каждого объекта.

Однако, в большинстве случаев вы скорее всего будете использовать

1
canvas.renderAll
как
1
onChange
-вызов.

Возвращаясь к опциям для анимации - что же именно мы можем менять?

  • 1
    
    from:
    - позволяет менять начальное значение атрибута для анимации (если не хотим использовать текущее)
  • 1
    
    duration:
    - длительность анимации; по умолчанию - 500 ms
  • 1
    
    onComplete:
    - функция для вызова в конце анимации (callback)
  • 1
    
    easing:
    - функция easing (смягчение)

Все эти опции более менее очевидны, кроме наверное

1
easing
. Давайте посмотрим поближе.

По умолчанию,

1
animate
используют
1
easeInSine
-функцию для смягчения анимации. Если такой вариант не подходит, в Fabric имеется большой набор популярных easing-функций (доступных через объект
1
fabric.util.ease
).

Например, вот так можно подвинуть объект вправо, при этом “отпружинивая”” в конце:

rect.animate('left', 500, {
  onChange: canvas.renderAll.bind(canvas),
  duration: 1000,
  easing: fabric.util.ease.easeOutBounce
});

Заметьте, что мы используем

1
fabric.util.ease.easeOutBounce
как опцию смягчения. Есть и другие популярные функции —
1
easeInCubic
,
1
easeOutCubic
,
1
easeInElastic
,
1
easeOutElastic
,
1
easeInBounce
,
1
easeOutExpo
и т. д.

Вот в принципе и все, что нужно знать о анимации. Теперь можно с легкостью делать интересные вещи — менять угол объекта, чтобы сделать его вращающимся; анимировать

1
left/top
чтобы его двигать; анимировать
1
width/height
для увеличения/уменьшения; анимировать
1
opacity
для появления/исчезания и т. д.

Фильтры изображений

В первой части этой серии мы узнали, как работать с изображениями в Fabric. Как вы наверное помните, для этого используется

1
fabric.Image
-конструктор, передавая в него
1
<img>
-элемент.

Также есть метод

1
fabric.Image.fromURL
, с помощью которого можно создать объект прямо из строки URL. И конечно же эти
1
fabric.Image
-объекты можно кинуть на холст, где они отобразятся как и все остальное.

Работать с изображениями прикольно, а с фильтрами изображений — еще веселей! Fabric уже имеет несколько фильтров, а также позволяет легко определять свои.

Некоторые фильтры из Fabric вам наверное знакомы — удаление белого фона, перевод в черно-белый, негатив или яркость. А некоторые менее популярны — градиентная прозрачность, сепия, шум.

Так как же применить фильтр к изображению? Каждый

1
fabric.Image
-объект имеет
1
filters
-атрибут, который просто является массивом фильтров. Каждый элемент в этом массиве — или один из существующих в Fabric или собственный фильтр.

Ну вот, к примеру, сделаем картинку черно-белой:

fabric.Image.fromURL( 'pug.jpg', function ( img ) {
  
  // добавляем фильтр
  img.filters.push( new fabric.Image.filters.Grayscale() );
  
  // применяем фильтры и перерисовываем canvas после применения
  img.applyFilters( canvas.renderAll.bind( canvas ) );
  
  // добавляем изображения на холст
  canvas.add( img );

});

Fabric Image

А вот так можно сделать сепию:

fabric.Image.fromURL( 'pug.jpg', function ( img ) {
  
  img.filters.push( new fabric.Image.filters.Sepia() );
  
  img.applyFilters( canvas.renderAll.bind( canvas ) );
  
  canvas.add( img );

});

Fabric Image

С атрибутом

1
filters
можно делать все тоже что и с обычным массивом — удалить фильтр (с помощью
1
pop
,
1
splice
, или
1
shift
), добавить фильтр (с помощью
1
push
,
1
splice
,
1
unshift
) или даже соединить несколько фильтров.

Когда вызывается

1
applyFilters
, все фильтры в массиве применяются к картинке по очереди. Вот, например, давайте создадим картинку с увеличенной яркостью и с эффектом сепии:

fabric.Image.fromURL( 'pug.jpg', function ( img ) {

  img.filters.push(
    new fabric.Image.filters.Sepia(),
    new fabric.Image.filters.Brightness({ brightness: 100 }));

  img.applyFilters(canvas.renderAll.bind( canvas ));
  
  canvas.add( img );

});

Fabric Image

Заметьте, что мы передали

1
{ brightness: 100 }
-объект в Brightness-фильтр. Это потому, что некоторым фильтрам ничего дополнительного не нужно; а некоторым (например -
1
grayscale
,
1
invert
,
1
sepia
) надо указать определенные параметры.

Для фильтра яркости это собственно само значение яркости (

1
0-255
). У фильтра шума, это значение шума (
1
0-1000
). А у фильтра удаления белого фона (‘remove white’) есть порог (‘threshold’) и расстояние (‘distance’).

Ну вот разобрались с фильтрами; пора создать свой!

Образец для создания фильтров будет довольно прост. Нам нужно создать ‘класс’’ и написать метод

1
applyTo
. Опционально мы можем дать фильтру
1
toJSON
-метод (поддержка JSON-сериализации) и/или
1
initialize
(если фильтр имеет дополнительные параметры):

fabric.Image.filters.Redify = fabric.util.createClass({

  type: 'Redify',

  applyTo: function ( canvasEl ) {
    var context = canvasEl.getContext( '2d' ),
        imageData = context.getImageData( 0, 0, canvasEl.width, canvasEl.height ),
        data = imageData.data;

    for ( var i = 0, len = data.length; i < len; i += 4 ) {
      data[ i + 1 ] = 0;
      data[ i + 2 ] = 0;
    }

    context.putImageData( imageData, 0, 0 );
  }
});

fabric.Image.filters.Redify.fromObject = function ( object ) {
  return new fabric.Image.filters.Redify( object );
};

Fabric Image

Не вникая сильно в подробности кода, стоит заметить, что самое главное происходит в цикле, где мы меняем зеленую (

1
data[i+1]
) и голубую (
1
data[i+2]
) компоненты каждого пикселя на
1
0
, по сути дела удаляя их.

Красная компонента остается нетронутой, что и делает все изображение красным.

Примечание чтеца: для желающих - более подробно аналог представленного выше кода описан в моей статье Canvas - Raw Pixel.

Как видите,

1
applyTo
-метод получает в себя canvas-элемент, который представляет собой изображение. Имея такой canvas, мы можем пройтись по всем пикселям изображения (
1
getImageData().data
) изменяя их как нам угодно.

Цвета

Независимо от того, с чем вам удобней работать — hex, RGB, или RGBA-форматами цвета — Fabric упрощает утомительные операции и переводы из одного формата в другой.

Давайте посмотрим на несколько способов определить цвет в Fabric:

new fabric.Color( '#f55' );
new fabric.Color( '#123123' );
new fabric.Color( '356735' );
new fabric.Color( 'rgb( 100, 0, 100 )' );
new fabric.Color( 'rgba( 10, 20, 30, 0.5 )' );

Перевод формата происходит очень просто. Метод

1
toHex()
переводит цвет в
1
hex
. Метод
1
toRgb()
— в
1
RGB
, а метод
1
toRgba()
— в
1
RGB
с альфа каналом (прозрачностью):

new fabric.Color( '#f55' ).toRgb(); // => "rgb( 255, 85, 85 )"
new fabric.Color( 'rgb( 100, 100, 100 )' ).toHex(); // => "646464"
new fabric.Color( 'fff' ).toHex(); // => "FFFFFF"

Кстати, можно делать не только перевод. Можно “накладывать” цвета один на другой или делать из них черно-белый вариант:

var redish = new fabric.Color('#f55');
var greenish = new fabric.Color('#5f5');

redish.overlayWith(greenish).toHex(); // => "AAAA55"
redish.toGrayscale().toHex(); // => "A1A1A1"

Градиенты

Еще более экспрессивный способ работы с цветами — используя градиенты. Градиенты позволяет плавно смешать один цвет с другим, открывая возможность довольно изумительным эффектам.

Fabric поддерживает градиенты с помощью метода setGradient (setGradientFill до 1.1.0 версии), который присутствует на всех объектах.

Вызывание

1
setGradient( 'fill', { ... } )
- это почти как выставление значения ‘fill’ у объекта, только вместо цвета используется градиент:

var circle = new fabric.Circle({
  left: 100,
  top: 100,
  radius: 50
});

circle.setGradient( 'fill', {
  x1: 0,
  y1: -circle.height / 2,
  x2: 0,
  y2: circle.height / 2,
  colorStops: {
    0: '#000',
    1: '#fff'
  }
});

Fabric Image

В этом примере мы создаем круг в точке

1
100/100
с радиусом в
1
50px
. Потом выставляем ему градиент, идущий по всей высоте объекта, от черного к белому.

Как видите, метод получает в себя конфигурационный объект, в котором могут присутствовать 2 пары координат (

1
x1/y1
и
1
x2/y2
) и объект ‘colorStops’.

Координаты указывают, где градиент начинается и где он заканчивается.

1
colorStops
указывают - из каких цветов он состоит.

Вы можете определить сколько угодно цветов; главное, чтобы их позиции находились в интервале от 0 до 1 (например 0, 0.1, 0.3, 0.5, 0.75, 1). 0 представляет начало градиента, 1 — его конец.

Чтобы понять, как мы определили координаты, посмотрим на изображение ниже:

Fabric Image

Так как координаты относительны центру объекта, то верхней точкой является

1
-circle.height / 2
, а нижней -
1
circle.height / 2
. Координаты по ширине (
1
x1/x2
) определяем точно так же.

Вот пример красно-голубого градиента, идущего слева направо:

circle.setGradient( 'fill', {
  x1: -circle.width / 2,
  y1: 0,
  x2: circle.width / 2,
  y2: 0,
  colorStops: {
    0: "red",
    1: "blue"
  }
});

Fabric Image

А вот 5-ти шаговый градиент-радуга, с цветами, занимающими по 20% всей длины:

circle.setGradient( 'fill', {
  x1: -circle.width / 2,
  y1: 0,
  x2: circle.width / 2,
  y2: 0,
  colorStops: {
    0: "red",
    0.2: "orange",
    0.4: "yellow",
    0.6: "green",
    0.8: "blue",
    1: "purple"
  }
});

Fabric Image

А вы можете придумать что-нибудь интересное?

Текст

Что если нужно отобразить не только картинки и векторные формы на холсте, а еще и текст? Fabric умеет и это! Встречайте

1
fabric.Text
.

Перед тем как говорить о тексте, стоит отметить, зачем мы вообще предоставляем поддержку работы с текстом.

Ведь canvas имеет встроенные методы

1
fillText
и
1
strokeText
.

Во-первых, для того чтобы можно было работать с текстом как с объектами. Выстроенные canvas-методы — как обычно — позволяют вывести текст на очень низком уровне.

А вот создав объект типа

1
fabric.Text
, мы можем работать с ним как и с любым другим объектом на холсте — двигать его, масштабировать, менять атрибуты, и т. д.

Вторая причина — чтобы иметь более богатый функционал, чем то что дает нам canvas. Некоторые вещи, которые есть в Fabric, но нет в родных методах:

  • Многострочность - Родные методы позволяют написать только одну линию, игнорируя переходы строки
  • Выравнивание текста - Влево, по центру, вправо. Полезно во время работы с многострочным текстом
  • Фон текста - Фон выводится только под самим текстом, в зависимости от выравнивания
  • Декорация текста - Подчеркивание, надчеркивание, перечеркивание
  • Высота строки - Полезно во время работы с многострочным текстом

Ну что ж, давайте посмотрим на вездесущий “hello world”?

var text = new fabric.Text( 'hello world', { left: 100, top: 100 });
canvas.add( text );

Вот и все! Для показа текста необходимо всего лишь добавить объект типа

1
fabric.Text
на холст, указывая нужную позицию.

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

1
left
,
1
top
,
1
fill
,
1
opacity
, и т. д.

Помимо обычных атрибутов, у текстовых объектов конечно же есть и свои, относящиеся к тексту. Вкратце, об этих атрибутах:

fontFamily

По умолчанию - “Times New Roman”. Позволяет менять семейство шрифта для текста:

var comicSansText = new fabric.Text( "I'm in Comic Sans", {
  fontFamily: 'Comic Sans'
});

Fabric Image

fontSize

Контролирует размер текста. Заметьте, что в отличие от других объектов в Fabric, мы не можем менять размер текста с помощью width/height. Вместо этого как раз и используется fontSize и конечно же

1
scaleX/scaleY
:

var text40 = new fabric.Text( "I'm at fontSize 40", {
  fontSize: 40
});
var text20 = new fabric.Text( "I'm at fontSize 20", {
  fontSize: 20
});

Fabric Image

fontWeight

Позволяет сделать текст жирнее или тоньше. Точно также как в CSS, можно использовать или слова (“normal”, “bold”) или номерные значения (100, 200, 400, 600, 800).

Важно понимать, что для определенной толщины нужно иметь соответствующий шрифт. Если в шрифте не присутствует “bold” (жирный) вариант, например, то жирный текст может не отобразиться:

var normalText = new fabric.Text( "I'm a normal text", {
  fontWeight: 'normal'
});
var boldText = new fabric.Text( "I'm a bold text", {
  fontWeight: 'bold'
});

Fabric Image

textDecoration

Позволяет добавить тексту перечеркивание, надчеркивание или подчеркивание. Опять же, эта декларация работает также, как в CSS.

Однако Fabric умеет даже немного больше, позволяя использовать эти декорации вместе (например, подчеркивание И перечеркивание), просто перечисляя их через пробел:

var underlineText = new fabric.Text( "I'm an underlined text", {
  textDecoration: 'underline'
});
var strokeThroughText = new fabric.Text( "I'm a stroke-through text", {
  textDecoration: 'line-through'
});
var overlineText = new fabric.Text( "I'm an overline text", {
  textDecoration: 'overline'
});

Fabric Image

shadow

До версии 1.3.0 этот атрибут назывался “textShadow”.

Тень для текста. Состоит из 4-х компонент: цвет, горизонтальный отступ, вертикальный отступ, и размер размытия.

Это все должно быть знакомо, если вы до этого работали с тенями в CSS. Меняя эти 4 опции, можно добиться многих интересных эффектов:

var shadowText1 = new fabric.Text( "I'm a text with shadow", {
  shadow: 'rgba(0,0,0,0.3) 5px 5px 5px'
});

var shadowText2 = new fabric.Text( "And another shadow", {
  shadow: 'rgba(0,0,0,0.2) 0 0 5px'
});

var shadowText3 = new fabric.Text( "Lorem ipsum dolor sit", {
  shadow: 'green -5px -5px 3px'
});

Fabric Image

fontStyle

Стиль текста. Может быть только один из двух:

1
normal
или
1
italic
. Опять же, работает так же, как и в CSS:

var italicText = new fabric.Text( "A very fancy italic text", {
  fontStyle: 'italic',
  fontFamily: 'Delicious'
});

var anotherItalicText = new fabric.Text( "another italic text", {
  fontStyle: 'italic',
  fontFamily: 'Hoefler Text'
});

Fabric Image

stroke и strokeWidth

Соединяя

1
stroke
(цвет наружнего штриха) и
1
strokeWidth
(ширину наружнего штриха), можно достичь довольно интересных эффектов.

Вот пара примеров:

var textWithStroke = new fabric.Text( "Text with a stroke", {
  stroke: '#ff1318',
  strokeWidth: 1
});

var loremIpsumDolor = new fabric.Text( "Lorem ipsum dolor", {
  fontFamily: 'Impact',
  stroke: '#c3bfbf',
  strokeWidth: 3
});

Fabric Image

Стоит отметить, что

1
stroke
был назван
1
strokeStyle
до версии 1.1.6

textAlign

Выравнивание полезно при работе с многострочным текстом. В однострочном тексте, выравнивание не видно, потому как ширина самого текстового объекта такая же как и длина строки.

Возможные значения:

1
left
,
1
center
,
1
right
и
1
justify
:

var text = 'this is\na multiline\ntext\naligned right!';
var alignedRightText = new fabric.Text( text, {
  textAlign: 'right'
});

Fabric Image

lineHeight

Еще один атрибут, скорее всего знакомый из CSS —

1
lineHeight
(высота строки).

Позволяет менять расстояние между строк в многострочном тексте. Вот пример текста с ‘lineHeight 3’ и второй с ‘lineHeight 1’:

var lineHeight3 = new fabric.Text( 'Lorem ipsum ...', {
  lineHeight: 3
});
var lineHeight1 = new fabric.Text( 'Lorem ipsum ...', {
  lineHeight: 1
});

Fabric Image

textBackgroundColor

И наконец, дать тексту фон можно с помощью

1
textBackgroundColor
. Заметьте, что фон заполняется только под самим текстом, а не на всю “коробку”.

Чтобы закрасить весь текстовый объект, можно использовать атрибут

1
backgroundColor
. Также видно, что фон зависит от выравнивания текста и
1
lineHeight
.

Если

1
lineHeight
очень большой, фон будет видно только под текстом:

var text = 'this is\na multiline\ntext\nwith\ncustom lineheight\n&background';
var textWithBackground = new fabric.Text( text, {
  textBackgroundColor: 'rgb(0,200,0)'
});

Fabric Image

События

События — незаменимый инструмент для создания сложных приложений. Для удобства пользования и более детальной настройки, Fabric имеет обширную систему событий; начиная от низкоуровневых событий мыши и вплоть до высокоуровневых событий объектов.

События позволяют нам “поймать” различные моменты, когда что-то происходит на холсте.

Хотим узнать, когда была нажата мышка? Следим за событием

1
mouse:down
.

Как насчет, когда объект был добавлен на холст? Для этого есть

1
object:added
.

Ну, а что насчет перерисовки холста? Используем

1
after:render
.

API событий очень прост и похож на то, к чему вы скорее всего привыкли в jQuery, Underscore.js или других популярных JS-библиотеках.

Есть метод on для инициализации слушателя событий и есть метод off для его удаления.

Давайте посмотрим на пример:

var canvas = new fabric.Canvas('...');
canvas.on( 'mouse:down', function( options ) {
  console.log( options.e.clientX, options.e.clientY );
});

Мы добавили слушатель события

1
mouse:down
на canvas-объекте и указали обработчика, который будет записывать координаты, где произошло это событие.

Таким образом, мы можем видеть, где именно произошел клик на холсте. Обработчик событий получает options-объект с двумя параметрами:

1
e
— оригинальное событие и
1
target
— Fabric-объект на холсте, если он найден.

Первый параметр присутствует всегда, а вот

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

Например, для

1
mouse:down
но не для
1
after:render
(так как это событие не “имеет” никаких объектов, а просто обозначает что холст был перерисован):

canvas.on( 'mouse:down', function( options ) {
  if (options.target) {
    console.log('an object was clicked! ', options.target.type);
  }
});

Этот пример выведет ‘an object was clicked!’ если мы нажмем на объект. Также покажется тип этого объекта.

Какие еще события доступны в Fabric?

На уровне мышки, у нас есть

1
mouse:down
,
1
mouse:move
и
1
mouse:up
.

Из общих есть

1
after:render
.

Есть события, касающиеся выбора объектов:

1
before:selection:cleared
,
1
selection:created
,
1
selection:cleared
.

Ну и конечно же, события объектов:

1
object:modified
,
1
object:selected
,
1
object:moving
,
1
object:scaling
,
1
object:rotating
,
1
object:added
и
1
object:removed
.

Стоит заметить, что события типа

1
object:moving
(или
1
object:scaling
) происходят постоянно, во время движения или масштабирования объекта, даже если на один пиксель.

В то же время, события типа

1
object:modified
или
1
selection:created
происходят только в конце действия (изменение объекта, создание группы объектов, и т. д.).

В предыдущих примерах мы присоединяли слушателя на canvas объект (

1
canvas.on( 'mouse:down', ... )
).

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

Для удобства, Fabric позволяет добавлять слушатели прямо на Fabric объекты!

var rect = new fabric.Rect({ width: 100, height: 50, fill: 'green' });
rect.on( 'selected', function () {
  console.log('selected a rectangle');
});

var circle = new fabric.Circle({ radius: 75, fill: 'blue' });
circle.on( 'selected', function () {
  console.log('selected a circle');
});

В этом примере слушатели “присоединяются” прямо к прямоугольнику и кругу. Вместо

1
object:selected
мы используем событие
1
selected
.

По такому же принципу, можно использовать событие

1
modified
(
1
object:modified
когда “вешаем” на холст),
1
rotating
(аналог
1
object:rotating
) и т. д.

Вы можете ознакомиться с событиями поближе и прямо в реальном времени вот в этой демо.

На этом 2-я часть подошла к концу. Столько всего нового, но это еще не все! В 3-й части мы рассмотрим группы объектов, сериализацию/десериализацию холста и формат JSON, SVG-парсер, а также создание подклассов.