Графический редактор с помощью Fabric.js версии 6
В проектах веб-разработки разработчики обычно создают элементы пользовательского интерфейса из стандартных элементов DOM. Иногда веб-разработчикам требуется создавать интерактивную, динамичную, высокопроизводительную графику на веб-поверхностях.
В таких сценариях использование традиционных элементов HTML или SVG не обеспечит высокую производительность или все те гибкие возможности рисования, которые вы могли бы ожидать.
Мы могли бы использовать встроенный HTML canvas API для создания интерактивной, высокопроизводительной графики, но это низкоуровневый, менее производительный API для современных разработчиков. Итак, нам нужно найти высокоуровневую библиотеку canvas для создания интерактивной графики с меньшими затратами кода.
Решением для этого является Fabric.js HTML canvas library, полнофункциональная библиотека canvas, которая предлагает минимальный уровень API для интерактивного редактирования фигур, изображений и текста на нескольких слоях холста. Она позволяет активировать рисование от руки, применять графические фильтры и использовать анимацию.
И Fabric.js последняя версия, v6, содержит еще более совершенный API для современной веб-разработки.
В этой статье мы рассмотрим, что нового появилось в версии Fabric.js v6, и узнаем, как использовать ее для создания интерактивной графики на холсте, создав приложение для редактирования изображений с помощью Fabric.js v6 и React.
Что нового в Fabric.js версии 6?
Библиотека Fabric.js была первоначально выпущена в 2010 году, когда спецификация ECMAScript не содержала современных понятных концепций асинхронного программирования, таких как Promises и ключевые слова async/await, поэтому в более ранних версиях Fabric.js использовались старомодные обратные вызовы.
Fabric.js версия 6 модернизировала свой API и среду разработки, перейдя на современный синтаксис JavaScript и переписав библиотеку на TypeScript.
Вот краткое изложение основных изменений и усовершенствований, внесенных в Fabric.js v6, который вам, вероятно, потребуется указать в вашем коде:
- Проект Fabric.js перешел на TypeScript и начал предлагать разработчикам встроенные определения типов
- Fabric.js версия 6 поставляется с модульным импортом объектов (т.е. импортом { Canvas, Image } из 'fabric’), поэтому разработчики могут писать более читаемый код, а разработчики JavaScript-пакетов могут уменьшить размер конечного пакета с исходным кодом, поскольку библиотека поддерживает встраивание в дерево
- Методы ООП теперь используют Promises для обработки асинхронных операций вместо старомодных обратных вызовов, которые могут вызвать проблему с обратным вызовом. Это означает, что разработчики могут писать сокращенные блоки асинхронного кода с ключевыми словами async/await или методами Promise
- Библиотека улучшила совместимость с React за счет асинхронной обработки операций очистки DOM с помощью метода dispose() объекта Canvas
- Fabric.js теперь используются собственные классы ECMAScript, поэтому разработчикам не нужно использовать createClass(), как старые служебные функции, для создания пользовательских классов Fabric — вы можете использовать ключевое слово native extends
Помимо вышеупомянутых основных изменений и усовершенствований, другие важные изменения включают в себя устаревание классов и методов, а также изменения параметров, как указано в этом выпуске на GitHub.
Создание графического редактора с помощью Fabric.js версии 6
Теперь вы знаете о преимуществах использования Fabric.js для создания интерактивных элементов canvas, а также о новых функциях и изменениях в версии 6. Давайте узнаем, как работать с Fabric.js v6, создав простое приложение для редактирования изображений.
Приложение "Редактор изображений" будет охватывать все основные функции Fabric.js, реализуя импорт изображений, фильтрацию изображений, создание текстовых элементов и функции рисования от руки.
Разработка структуры приложения
Мы будем использовать React для разработки графического редактора, поэтому создайте новое приложение с помощью Create React App (CRA) следующим образом:
npx create-react-app fabric-image-editor cd fabric-image-editor
Давайте создадим структуру редактор изображений перед установкой требуемых зависимостей. Разделите экран на две строки для элементов и холста с помощью CSS на основе адаптируемых блоков, добавив следующий код к components/App.js файл:
import Toolbox from './Toolbox'; import EditorCanvas from './EditorCanvas'; import './App.css'; function App() { return ( <div className="editor"> <Toolbox /> <EditorCanvas /> </div> ); } export default App;
В состав заявки использует двух частей: панели инструментов и EditorCanvas снижение трудоемкости выращивания строк кода. Создание компонента панели инструментов с кнопками-заполнитель, добавив следующий код к components/Toolbox.js файл:
const Toolbox = () => { return ( <div className="toolbox"> <button /> <button /> <button /> </div> ); }; export default Toolbox;
Затем создайте последний подкомпонент EditorCanvas, который будет содержать HTML-элемент canvas нашего приложения для редактирования изображений:
const EditorCanvas = () => { return ( <div className="canvasbox"> <canvas width="1000" height="500"></canvas> </div> ); }; export default EditorCanvas;
Теперь нам нужно оформить структуру этого приложения с помощью CSS, добавив следующие определения CSS в файл components/App.css:
.editor { display: flex; flex-flow: column; height: 100vh; } .toolbox { padding: 0.5em; background-color: #414141; display: flex; gap: 0.5em; } .toolbox button { width: 32px; height: 32px; position: relative; font-size: 16px; border: none; background-color: #ccc; border-radius: 0.2em; } .canvasbox { overflow: auto; flex: 1; background-color: #777; } .canvasbox canvas { width: 1000px; height: 500px; background-color: white; /* TODO: remove */ }
Запустите приложение React с помощью npm start
или yarn start
. Вы увидите структуру приложения редактора изображений с несколькими кнопками-заполнителями:
Структура приложения редактора изображений с кнопками-заполнителями на панели инструментов.
Добавление Fabric.js и других необходимых зависимостей
Теперь мы можем приступить к добавлению необходимых зависимостей для продолжения процесса разработки. Мы будем использовать Fabric.js в качестве библиотеки canvas и пакета иконок Font Awesome SVG, поэтому установите их с помощью следующих команд:
npm install fabric @fortawesome/fontawesome-svg-core @fortawesome/free-solid-svg-icons @fortawesome/react-fontawesome #--- or --- yarn add fabric @fortawesome/fontawesome-svg-core @fortawesome/free-solid-svg-icons @fortawesome/react-fontawesome
Инициализация Fabric.js canvas
Текущий canvas — это пустой собственный HTML-элемент canvas с белым фоном, добавленный с помощью CSS - при щелчке или перетаскивании с помощью мыши или сенсорной панели ничего не происходит. Нам нужно превратить этот пустой холст в Fabric.js редактирование canvas, чтобы начать создание приложения для редактирования изображений.
Импорт класса Fabric.js холсты и другие необходимые реагируют функции в App.js файл:
import { useRef, useEffect, useState } from 'react'; import { Canvas } from 'fabric';
Затем инициализируйте экземпляр Fabric.js canvas и привяжите его к элементу <canvas> в функции App:
const canvasRef = useRef(null); const [canvas, setCanvas] = useState(null); useEffect(() => { const canvas = new Canvas(canvasRef.current, { backgroundColor: 'white' }); canvas.setDimensions({ width: 1000, height: 500 }); setCanvas(canvas); return () => canvas.dispose(); }, [canvasRef, setCanvas]);
Здесь мы используем ссылку реагировать, чтобы получить полотно ссылкой дом и государства холст для хранения экземпляра fabric.js справка.
Обновите шаблон компонента, передав ссылку на элемент canvas и экземпляр Fabric.js через component props:
return ( <div className="editor"> <Toolbox canvas={canvas} /> <EditorCanvas ref={canvasRef} canvas={canvas} /> </div> );
Здесь мы передали ссылку canvas на EditorCanvas через ref, поэтому нам нужно обернуть компонент EditorCanvas с помощью forwardRef, как показано в следующем фрагменте кода:
import { forwardRef } from 'react'; const EditorCanvas = forwardRef(({}, ref) => { return ( <div className="canvasbox"> <canvas ref={ref} width="1000" height="500"></canvas> </div> ); }); export default EditorCanvas;
Наконец, удалите временное свойство background-color из элемента canvas:
.canvasbox canvas { width: 1000px; height: 500px; /* background-color: white; TODO: remove */ }
Вышеуказанных изменений достаточно для инициализации Fabric.js с собственным элементом холста. Попробуйте перетащить мышь в область холста. Вы увидите поле выбора, указывающее, что Fabric.js инициализирован:
Поле выбора по умолчанию в Fabric.js.
Реализация функции импорта фотографий
Теперь наш Fabric.js canvas готов к размещению изображений, фигур, текстов и всех поддерживаемых Fabric.js объектов. Давайте добавим новую кнопку "Панель инструментов", чтобы пользователи приложения могли импортировать новые изображения в область редактирования фотографий.
Мы будем использовать шрифт потрясающие иконки в кнопках панели инструментов, чтобы импортировать все значки, которые мы будем использовать из исходного файла index.js :
import { library } from '@fortawesome/fontawesome-svg-core'; import { faImage, faFont, faPencil, faFilter, faTrash, faDownload } from '@fortawesome/free-solid-svg-icons'; library.add(faImage, faFont, faPencil, faFilter, faTrash, faDownload);
Импортируйте компонент Font Awesome icon и класс Fabric.js Image из файла Toolbox.js следующим образом:
import { Image } from 'fabric'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
Добавьте новую кнопку для импорта изображения и добавьте выбранное изображение на холст, обновив реализацию компонента Toolbox, как показано в следующем фрагменте кода:
const Toolbox = ({ canvas }) => { function fileHandler(e) { const file = e.target.files[0]; const reader = new FileReader(); reader.onload = async (e) => { const image = await Image.fromURL(e.target.result); image.scale(0.5); canvas.add(image); canvas.centerObject(image); canvas.setActiveObject(image); }; reader.readAsDataURL(file); e.target.value = ''; } return ( <div className="toolbox"> <button title="Add image"> <FontAwesomeIcon icon="image" /> <input type="file" accept=".png, .jpg, .jpeg" onChange={fileHandler} /> </button> </div> ); };
Здесь мы используем собственный элемент выбора файлов HTML со встроенным интерфейсом FileReader для чтения выбранного изображения как URL-адреса данных в кодировке base64. URL-адрес закодированного изображения используется для создания нового изображения Fabric.js с помощью асинхронного метода Image.fromURL().
Используйте следующее определение CSS, чтобы скрыть собственный элемент средства выбора файлов, позволяя пользователям открывать средство выбора файлов, нажимая на кнопку toolbox:
.toolbox input[type=file] { opacity: 0; position: absolute; inset: 0; }
Откройте приложение, нажмите кнопку добавления изображения и выберите любое изображение. Вы можете перемещать, изменять размер и вращать импортированные изображения с помощью элементов управления Fabric.js по умолчанию, как показано в следующем предварительном просмотре:
Импорт и работа с объектами изображений на холсте Fabric.js.
Попробуйте импортировать различные изображения и манипулировать этими графическими объектами с помощью встроенных Fabric.js элементы управления объектами. Несомненно, вы можете импортировать изображения в формате PNG с альфа-каналами (прозрачными областями).
Добавлена поддержка создания текстовых элементов
Fabric.js поддерживает добавление редактируемых и не редактируемых текстовых объектов на холст. Давайте добавим новую кнопку toolbox, чтобы добавить новый редактируемый текстовый элемент на поверхность редактора изображений.
Сначала добавьте новую кнопку на панели инструментов с обработчиком щелчков:
<button title="Add text" onClick={addText}> <FontAwesomeIcon icon="font" /> </button>
Затем реализуйте обработчик клика, написав код для добавления нового интерактивного текстового элемента в Fabric.js canvas следующим образом:
function addText() { const text = new IText('Edit this text'); canvas.add(text); canvas.centerObject(text); canvas.setActiveObject(text); }
Здесь мы использовали класс iText для создания нового редактируемого текстового элемента, поэтому обязательно импортируйте его из пакета fabric:
import { Image, IText } from 'fabric';
Теперь вы можете добавлять новые текстовые элементы, нажимая на недавно добавленную кнопку панели инструментов. Вы можете дважды щелкнуть по текстовому элементу, чтобы изменить текст, и использовать элементы управления для перемещения, изменения размера или поворота, как обычно:
Создание нового редактируемого текстового элемента в холсте Fabric.js.
Активация режима рисования от руки
Fabric.js имеет предварительно разработанную реализацию рисования от руки, которая поддерживает несколько встроенных типов кистей. Давайте активируем простую поддержку рисования от руки в приложении image editor, используя класс PencilBrush. Мы добавим кнопку переключения для активации/ деактивации режима рисования, поэтому создайте новое поле состояния для режима рисования в компоненте Toolbox:
const [drawingMode, setDrawingMode] = useState(false);
Добавьте следующую функцию в компонент Toolbox для переключения режима рисования:
function toggleDrawingMode() { canvas.isDrawingMode = !canvas.isDrawingMode; setDrawingMode(canvas.isDrawingMode); }
Затем добавьте новую кнопку для переключения режима рисования:
<button title="Drawing mode" onClick={toggleDrawingMode} className={drawingMode ? 'active' : ''}> <FontAwesomeIcon icon="pencil" /> </button> .toolbox button.active { background-color: #edbd50; }
Эти изменения в коде активируют режим рисования, но вы не увидите никаких линий при рисовании объектов на холсте, поскольку сначала нам нужно установить кисть для рисования от руки по умолчанию. Установите кисть по умолчанию, добавив следующие инструкции в блок useEffect() компонента App.:
const brush = new PencilBrush(canvas); brush.color = 'black'; brush.width = 5; canvas.freeDrawingBrush = brush;
Нажмите кнопку режима рисования от руки и нарисуйте что-нибудь. Вы можете вернуться в режим выбора, нажав на ту же кнопку панели инструментов, как показано в следующем предварительном просмотре:
Рисование на холсте Fabric.js с использованием режима рисования от руки.
Попробуйте использовать другой тип кисти в качестве кисти для рисования от руки по умолчанию, например, CircleBrush.
Применение фотофильтров и эффектов
Fabric.js реализует множество встроенных алгоритмов фильтрации, которые вы можете добавлять к объектам изображений, поэтому мы также можем использовать несколько фильтров в этом примере приложения для редактирования изображений. Мы добавим новую кнопку переключения в панель инструментов, нажав которую пользователи смогут активировать предпочитаемый фильтр изображений.
Сначала добавьте новую кнопку и список фильтров в панель инструментов:
<button title="Filters" onClick={() => setCurrentFilter(currentFilter ? null : 'sepia')} className={currentFilter ? 'active' : ''}> <FontAwesomeIcon icon="filter" /> </button> {currentFilter && <select onChange={(e) => setCurrentFilter(e.target.value)} value={currentFilter}> <option value="sepia">Sepia</option> <option value="vintage">Vintage</option> <option value="invert">Invert</option> <option value="polaroid">Polaroid</option> <option value="grayscale">Grayscale</option> </select> } .toolbox select { border-radius: 0.2em; min-width: 10em; }
Здесь мы использовали поле currentFilter state, чтобы сохранить текущее название фильтра для выбранного изображения. Название фильтра и параметры настройки можно получить из props, поскольку мы создаем состояние фильтра в App:
const Toolbox = ({ canvas, currentFilter, setCurrentFilter }) => { // ...
Примените выбранный фильтр, когда currentFilter будет изменен в событии рендеринга нового компонента, как показано в следующем фрагменте кода, используя блок useEffect():
import { Image, IText, filters } from 'fabric'; // ... useEffect(() => { if(!canvas || !canvas.getActiveObject() || !canvas.getActiveObject().isType('image')) return; function getSelectedFilter() { switch(currentFilter) { case 'sepia': return new filters.Sepia(); case 'vintage': return new filters.Vintage(); case 'invert': return new filters.Invert(); case 'polaroid': return new filters.Polaroid(); case 'grayscale': return new filters.Grayscale(); default: return null; } } const filter = getSelectedFilter(); const img = canvas.getActiveObject(); img.filters=filter ? [filter] : []; img.applyFilters(); canvas.renderAll(); }, [currentFilter, canvas]);
Указанный выше блок useEffect() обнаруживает изменения фильтра и применяет выбранный фильтр к текущему выбранному объекту изображения. Здесь мы использовали только пять фильтров, но вы можете использовать больше фильтров, проверив все поддерживаемые реализации фильтров в объекте filters.
Создайте новое поле состояния в компоненте приложения для хранения текущего фильтра и обязательно передайте его в дочерние компоненты:
const [currentFilter, setCurrentFilter] = useState(null); // ... return ( <div className="editor"> <Toolbox canvas={canvas} currentFilter={currentFilter} setCurrentFilter={setCurrentFilter} /> <EditorCanvas ref={canvasRef} canvas={canvas} setCurrentFilter={setCurrentFilter} /> </div> );
Здесь мы передали поля родительского состояния в дочерние компоненты через props, поскольку у нас есть только один уровень дочернего компонента, но вам следует использовать React context API, если в вашем приложении есть дочерние элементы внутри дочерних элементов, чтобы избежать детализации prop.
Теперь вы можете активировать фильтры для выбранного объекта изображения, как показано в следующем предварительном просмотре:
Активация фильтров для объектов изображений в Fabric.js.
Как вы заметили, это не позволяет должным образом изменить статус активации фильтра и обнаружить фильтры выбранного объекта изображения, поэтому мы должны подписаться на Fabric.js события, чтобы изменить список выбора фильтра на основе фильтра текущего выбранного изображения.
Сначала получите настройщик имени фильтра из реквизитов компонента EditorCanvas следующим образом:
const EditorCanvas = forwardRef(({ canvas, setCurrentFilter }, ref) => { // ...
Добавьте следующий useEffect() блок, чтобы установить текущий фильтр имен по фильтру выбранного изображения объекта путем прослушивания Fabric.js выбор событий:
useEffect(() => { if(!canvas) return; function handleSelection(e) { const obj = e.selected?.length === 1 ? e.selected[0] : null; const filter = obj?.filters?.at(0); setCurrentFilter(filter ? filter.type.toLowerCase() : null); } canvas.on({ 'selection:created': handleSelection, 'selection:updated': handleSelection, 'selection:cleared': handleSelection }); return () => { canvas.off({ 'selection:created': handleSelection, 'selection:updated': handleSelection, 'selection:cleared': handleSelection }); } }, [canvas, setCurrentFilter]);
Теперь редактор изображений корректно изменяет имя примененного фильтра, когда пользователи работают с несколькими изображениями:
Изменение выбранного фильтра при выборе пользователем объектов изображения.
Добавление базовой поддержки клавиатуры
Fabric.js предлагает множество встроенных функций редактирования изображений, но не реализует поддержку клавиатуры "из коробки". Однако мы можем легко прослушивать глобальные нажатия клавиш с помощью API-интерфейса прослушивателя событий браузера и запускать триггеры Fabric.js функции для реализации лучшей и гибкой поддержки клавиатуры.
Чтобы научиться работать с клавиатурой, мы можем разрешить пользователям удалять выбранные объекты canvas нажатием клавиши delete.
Прослушайте события нажатия клавиши и удалите выбранные элементы, добавив следующие инструкции кода в блок useEffect() компонента EditorCanvas:
function handleKeyDown(e) { if(e.key === 'Delete') { for(const obj of canvas.getActiveObjects()) { canvas.remove(obj); canvas.discardActiveObject(); } } } document.addEventListener('keydown', handleKeyDown, false);
Обязательно удалите подключенный прослушиватель событий, когда компонент будет размонтирован:
useEffect(() => { // ... return () => { document.removeEventListener('keydown', handleKeyDown, false); // ...
Попробуйте удалить объекты с холста, выбрав один или несколько элементов и нажав клавишу Delete на клавиатуре:
Удаление объектов из холста Fabric.js с помощью клавиши Delete.
Вы можете легко начать использовать функцию вырезания, копирования и вставки и перемещать объекты с помощью клавиш со стрелками, используя тот же обработчик нажатия клавиш, который мы создали ранее.
Очистка области холста
Что, если пользователю нужно очистить все существующие объекты и начать с нового дизайна? Просить пользователя выбрать все элементы и нажать клавишу удаления неудобно, поэтому давайте добавим новую кнопку на панели инструментов, чтобы пользователи могли мгновенно очистить холст.
Добавьте новую кнопку панели инструментов с обработчиком щелчка следующим образом:
<button title="Clear all" onClick={clearAll}> <FontAwesomeIcon icon="trash" /> </button> function clearAll() { if(window.confirm('Are you sure you want to clear all?')) { canvas.remove(...canvas.getObjects()); } }
Кнопка панели инструментов выше удаляет все существующие объекты холста после отображения диалогового окна подтверждения пользователя, как показано в следующем предварительном просмотре:
Удаление всех объектов холста для начала нового дизайна.
Экспорт Fabric.js canvas в виде изображения
Каждая программа-редактор изображений позволяет пользователям экспортировать текущий дизайн в виде изображения. В настоящее время большинство браузеров поддерживают экспорт изображений в формате PNG с помощью метода toDataURL() собственного элемента canvas, поэтому мы можем использовать его для экспорта Fabric.js canvas в виде изображения:
<button title="Download as image" onClick={downloadImage}> <FontAwesomeIcon icon="download" /> </button> function downloadImage() { const link = document.createElement('a'); link.download = 'photo_editor_image.png'; link.href = canvas.toDataURL(); link.click(); }
При нажатии кнопки загрузки веб-браузер загрузит изображение PNG, содержащее снимок холста Fabric.js, как показано в следующем предварительном просмотре:
Экспорт холста Fabric.js как изображения PNG с использованием встроенного API HTML Canvas.
Вы можете получить доступ к полному исходному коду этого примера приложения для редактирования изображений из этого репозитория на GitHub.
Советы по улучшению работы приложения для редактирования изображений
Мы многое рассмотрели, создав это приложение для редактирования изображений с помощью Fabric.js его ключевые особенности. Тем не менее, есть и другие идеи, которые вы могли бы реализовать, чтобы преобразовать этот образец графического редактора в полнофункциональную, продвинутую программу для редактирования изображений, такую как Adobe Photoshop:
- Добавление геометрических фигур с помощью заранее разработанных классов, таких как Circle, Rect и т.д.
- Предоставление пользователям возможности настраивать параметры фильтра с помощью нескольких элементов формы во всплывающем окне
- Реализация стека истории для отмены/повтора действий
- Внедрение эффективного контекстного меню с функциями "вырезать", "копировать", "вставить", "отправить назад" и "перенести вперед".
- Добавлен набор эффективных сочетаний клавиш, позволяющий пользователям использовать клавиатуру для редактирования изображений, т.е. для перемещения объектов с помощью клавиш со стрелками
- Использование Fabric.js встроенных функций сериализации/десериализации для сохранения/загрузки проектных документов и автосохранения изменений в локальном хранилище браузера
- Позволяет пользователям группировать несколько объектов для создания сложных структур
- Отображение слоев и предоставление пользователям возможности активировать режимы наложения цветов
- Добавление параметров настройки поверхности холста, таких как использование пользовательских размеров холста и изменение цвета заливки фона
Если здесь нет действительно полезных советов, которые, по вашему мнению, могли бы пригодиться другим, оставьте их в комментариях!
Готовы создать свой собственный эпический редактор?
В этом руководстве мы узнали, как использовать Fabric.js версию 6 с React, разработав пример приложения для редактирования изображений, которое поддерживает импорт изображений, добавление текстовых элементов и активацию графических фильтров. Мы также узнали, как использовать встроенные стандартные API-интерфейсы браузера с помощью Fabric.js, чтобы добавить поддержку клавиатуры и экспортировать canvas в виде изображения в формате PNG.
В версии 6 Fabric.js улучшен API за счет перехода на TypeScript и использования модульной кодовой базы на основе Promise, поэтому использование Fabric.js в версии 6 отсутствует старомодный стиль кодирования на основе обратных вызовов, который был в версии 5 и более ранних версиях.
Официальная документация по последнему модульному стилю программирования, основанному на Promise, еще не обновлена, и разработчики библиотеки работают над обновлениями документации.
Независимо от того, хотите ли вы создать графический редактор, подобный Adobe Photoshop, или программное обеспечение для разработки пользовательского интерфейса в стиле Figma, приведенная выше демонстрация должна направить вас к созданию отличного проекта. Счастливого написания кода.