Есть такой замечательный фреймворк React, который позволяет работать с огромным и мутабельным DOM в красивом иммутабельном функциональном стиле. Это действительно круто.
Но я бы хотел рассказать про опыт использования React, который позволяет работать с мутабельной абстракцией над "иммутабельным" canvas элементом. Звучит странно, но работает отлично.
Я использую элемент
Konva помогает очень сильно, но хочется большего. Так же я начал использовать React в своих приложениях, и он мне действительно нравится. И я подумал, как же я могу использовать React для рисования графики на canvas?
Получить доступ к контексту canvas из React компонента и что-нибудь нарисовать очень просто:
Демо: http://jsbin.com/xituko/edit?js,output
Работает отлично для простых примеров. Но для большого приложения такой подход не очень хорош, так как не позволяет создавать вложенные React компоненты:
А что на счет достаточно удобных реактовских методов (такие как "shouldComponentUpdate", "componentDidMount" и т.д.)?
А как же подход "полное описание представления в функции render"?
Мне действительно нравится реактовский подход к построению приложения. Поэтому я сделал плагин react-konva, который рисует на canvas через фреймворк Konva.
Далее
Демо: http://jsbin.com/camene/5/edit?html,js,output
react-canvas это совершенно другой плагин. Он позволяет рисовать "стандартные DOM объекты" (картинки, текст) с очень большой производительностью. Но этот плагин НЕ позволяет рисовать графику. В то время как react-konva сделан именно для рисования сложной графики на canvas с помощью React.
react-art так же позволяет рисовать графику и имеет поддержку SVG, но он не имеет поддержки событий для графических фигур.
Про производительно говорят очень часто, когда упоминают React. Собственно одна из главных причин использования React — это решение проблем производительности.
Но я сделал этот плагин НЕ для решения проблем с производительностью. Прямое использование canvas должно быть наиболее производительным путём, так, используя react-konva, мы имеем Konva, которая работает поверх canvas, и React, который работает поверх Konva.
Но я сделал плагин для борьбы со сложностью приложения. Konva помогает очень сильно (особенно когда нужны события для объектов на холсте, например "click", без фреймворка это сделать очень тяжело). Но React помогает еще сильнее, так как он предоставляет очень хорошую структуру кода и движения данных.
react-konva имеет поддержку большого количества основных фигур: Circle, Rect, Ellipse, Line, Sprite, Image, Text, TextPath, Star, Ring, Arc, Label, SVG Path, RegularPolygon, Arrow и вы можете создать свои собственные фигуры. Так же есть встроенная поддержка drag&drop, анимации, фильтры, система кэширования, десктоп и мобильные события (mouseenter, click, dblclick, dragstart, dragmove, dragend, tap, dbltap, и так далее).
Демо: http://jsbin.com/gakadi/2/edit?html,js,output
Демо: http://jsbin.com/tekopu/1/edit
Github: https://github.com/lavrton/react-konva
Konva framework: http://konvajs.github.io/
Но я бы хотел рассказать про опыт использования React, который позволяет работать с мутабельной абстракцией над "иммутабельным" canvas элементом. Звучит странно, но работает отлично.
Мотивация
Я использую элемент
<canvas>
очень часто. Я сделал несколько достаточно сложных веб-приложений, в которых canvas — это основной элемент представления данных. Использовать canvas без всяких фреймворков и библиотек может быть действительно сложно в крупных приложениях. Поэтому я начал часто использовать фреймворки. Сейчас я поддерживаю фреймворк Konva (есть обзорная статья https://habrahabr.ru/post/250897/).Konva помогает очень сильно, но хочется большего. Так же я начал использовать React в своих приложениях, и он мне действительно нравится. И я подумал, как же я могу использовать React для рисования графики на canvas?
React + canvas без фреймворков
Получить доступ к контексту canvas из React компонента и что-нибудь нарисовать очень просто:
class CanvasComponent extends React.Component {
componentDidMount() {
this.updateCanvas();
}
updateCanvas() {
const ctx = this.refs.canvas.getContext('2d');
ctx.fillRect(0,0, 100, 100);
}
render() {
return (
<canvas ref="canvas" width={300} height={300}/>
);
}
}
ReactDOM.render(<CanvasComponent/>, document.getElementById('container'));
Демо: http://jsbin.com/xituko/edit?js,output
Работает отлично для простых примеров. Но для большого приложения такой подход не очень хорош, так как не позволяет создавать вложенные React компоненты:
// компонент, который будет использовать многократно
function rect(props) {
const {ctx, x, y, width, height} = props;
ctx.fillRect(x, y, width, height);
}
class CanvasComponent extends React.Component {
componentDidMount() {
this.updateCanvas();
}
componentDidUpdate() {
this.updateCanvas();
}
updateCanvas() {
const ctx = this.refs.canvas.getContext('2d');
ctx.clearRect(0,0, 300, 300);
// отобразить "дочерние" компоненты
rect({ctx, x: 10, y: 10, width: 50, height: 50});
rect({ctx, x: 110, y: 110, width: 50, height: 50});
}
render() {
return (
<canvas ref="canvas" width={300} height={300}/>
);
}
}
ReactDOM.render(<CanvasComponent/>, document.getElementById('container'));
А что на счет достаточно удобных реактовских методов (такие как "shouldComponentUpdate", "componentDidMount" и т.д.)?
А как же подход "полное описание представления в функции render"?
Реализация
Мне действительно нравится реактовский подход к построению приложения. Поэтому я сделал плагин react-konva, который рисует на canvas через фреймворк Konva.
npm install react konva react-konva --save
Далее
import React from 'react';
import ReactDOM from 'react-dom';
import {Layer, Rect, Stage, Group} from ‘react-konva’;
class MyRect extends React.Component {
constructor(...args) {
super(...args);
this.state = {
color: 'green'
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
color: Konva.Util.getRandomColor()
});
}
render() {
return (
<Rect
x={10} y={10} width={50} height={50}
fill={this.state.color}
shadowBlur={10}
onClick={this.handleClick}
/>
);
}
}
function App() {
return (
<Stage width={700} height={700}>
<Layer>
<MyRect/>
</Layer>
</Stage>
);
}
ReactDOM.render(<App/>, document.getElementById('container'));
Демо: http://jsbin.com/camene/5/edit?html,js,output
Сравнения
react-konva vs react-canvas
react-canvas это совершенно другой плагин. Он позволяет рисовать "стандартные DOM объекты" (картинки, текст) с очень большой производительностью. Но этот плагин НЕ позволяет рисовать графику. В то время как react-konva сделан именно для рисования сложной графики на canvas с помощью React.
react-konva vs react-art
react-art так же позволяет рисовать графику и имеет поддержку SVG, но он не имеет поддержки событий для графических фигур.
react-konva vs vanilla canvas
Про производительно говорят очень часто, когда упоминают React. Собственно одна из главных причин использования React — это решение проблем производительности.
Но я сделал этот плагин НЕ для решения проблем с производительностью. Прямое использование canvas должно быть наиболее производительным путём, так, используя react-konva, мы имеем Konva, которая работает поверх canvas, и React, который работает поверх Konva.
Но я сделал плагин для борьбы со сложностью приложения. Konva помогает очень сильно (особенно когда нужны события для объектов на холсте, например "click", без фреймворка это сделать очень тяжело). Но React помогает еще сильнее, так как он предоставляет очень хорошую структуру кода и движения данных.
Особенности
react-konva имеет поддержку большого количества основных фигур: Circle, Rect, Ellipse, Line, Sprite, Image, Text, TextPath, Star, Ring, Arc, Label, SVG Path, RegularPolygon, Arrow и вы можете создать свои собственные фигуры. Так же есть встроенная поддержка drag&drop, анимации, фильтры, система кэширования, десктоп и мобильные события (mouseenter, click, dblclick, dragstart, dragmove, dragend, tap, dbltap, и так далее).
Пример нестандартной фигуры
function MyShape() {
return (
<Shape fill=”#00D2FF” draggable
sceneFunc={function (ctx) {
ctx.beginPath();
ctx.moveTo(20, 50);
ctx.lineTo(220, 80);
ctx.quadraticCurveTo(150, 100, 260, 170);
ctx.closePath();
// Konva specific method
ctx.fillStrokeShape(this);
}}
/>
);
}
Демо: http://jsbin.com/gakadi/2/edit?html,js,output
Пример работы с событиями
class MyCircle extends React.Component {
constructor(…args) {
super(…args);
this.state = { isMouseInside: false};
this.handleMouseEnter = this.handleMouseEnter.bind(this);
this.handleMouseLeave = this.handleMouseLeave.bind(this);
}
handleMouseEnter() {
this.setState({ isMouseInside: true});
}
handleMouseLeave() {
this.setState({ isMouseInside: false});
}
render() {
return (
<Circle
x={100} y={60} radius={50}
fill=”yellow” stroke=”black”
strokeWidth={this.state.isMouseInside ? 5 : 1}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
/>
);
}
}
Демо: http://jsbin.com/tekopu/1/edit
Ссылки
Github: https://github.com/lavrton/react-konva
Konva framework: http://konvajs.github.io/
Комментарии (10)
babylon
07.02.2016 01:27Мой выбор CreateJS. Судя по популярности, она непопулярна из-за сложенности:)
TheRabbitFlash
08.02.2016 04:39Не теряйте свое драгоценное время. Просто возьмите PixiJS или CreateJS.
lavrton
08.02.2016 05:20+1Почему же я теряю время?
TheRabbitFlash
08.02.2016 13:42+1ну хотя бы потому, что используете Konva. Я его смотрел-смотрел, чесал затылок и не понял конкретного применения, где не справится чужое современное и бесплатное решение с более навернутым функционалом… Есть более взрослые, простые и реально свежие, поддерживающиеся и развивающиеся продукты типа pixi/createjs.
Я же не только флешом занимаюсь (это если мой профиль смотрели). Я очень много делаю B2B приложений на html5 канвасе.
stas404
«Библиотека», а не «фреймворк».
stas404
Судя по этим и другим минусам, либо у кого-то плохое настроение, либо действительно есть народ, который не понимает разницу между фреймворком и библиотекой. Первым настроение я вряд ли смогу поднять, но вот для вторых на всякий случай напишу:
Фреймворк — это архитектура, в которую вы встраиваете ваш код, а библиотека — это кирпичик, который вы встраиваете в свою архитектуру.
ReactJS является именно библиотекой, выполняющей ровно одну задачу — это view-слой для отрисовки пользовательского интерфейса. Он ничего не знает о том, как архитектурно устроено приложение, не обязывает разработчика следовать конкретным предписаниям при организации структуры приложения и может быть беспроблемно и в любой момент заменен на другую view-библиотеку.
В конце концов, сами фейсбуковцы на главной странице так и пишут:
«React — A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES».