Atomico — это библиотека для создания веб-компонентов на JavaScript с
реактивной моделью данных. Одной из её сильных сторон является гибкая
интеграция с нативными DOM-элементами, включая
<canvas>. Использование Canvas в Atomico требует
внимательного подхода к жизненному циклу компонента и управлению
состоянием.
Компонент в Atomico определяется функцией, возвращающей шаблон с
использованием html:
import { c, html, useRef, useEffect } from "atomico";
function CanvasComponent() {
const canvasRef = useRef();
useEffect(() => {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
ctx.fillStyle = "lightblue";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "red";
ctx.font = "20px Arial";
ctx.fillText("Atomico Canvas", 50, 50);
}, []);
return html`
<canvas
${canvasRef}
width="400"
height="200"
style="border: 1px solid black;"
></canvas>
`;
}
export const canvasComponent = c(CanvasComponent);
Ключевые моменты:
useRef() позволяет получить ссылку на
элемент <canvas> после его рендера.useEffect() используется для инициализации и отрисовки
на Canvas. Этот хук гарантирует, что доступ к DOM возможен только после
монтирования компонента.Для динамического изменения графики используется реактивное состояние:
import { c, html, useRef, useEffect, useState } from "atomico";
function ReactiveCanvas() {
const canvasRef = useRef();
const [angle, setAngle] = useState(0);
useEffect(() => {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(angle);
ctx.fillStyle = "green";
ctx.fillRect(-50, -50, 100, 100);
ctx.restore();
}
draw();
}, [angle]);
return html`
<canvas
${canvasRef}
width="300"
height="300"
style="border: 1px solid black;"
></canvas>
<button oncl ick=${() => setAngle(angle + 0.1)}>Повернуть</button>
`;
}
export const reactiveCanvas = c(ReactiveCanvas);
Особенности реактивного обновления:
useState позволяет хранить состояние, влияющее на
отрисовку.useEffect([angle]) гарантирует
перерисовку при изменении значения.ctx.clearRect().Для создания плавной анимации интеграция Canvas с Atomico использует
requestAnimationFrame в useEffect:
function AnimatedCanvas() {
const canvasRef = useRef();
const [time, setTime] = useState(0);
useEffect(() => {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
let animationFrame;
function animate(timestamp) {
setTime(timestamp / 1000);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(
150 + 100 * Math.cos(time),
150 + 100 * Math.sin(time),
20,
0,
2 * Math.PI
);
ctx.fillStyle = "orange";
ctx.fill();
animationFrame = requestAnimationFrame(animate);
}
animationFrame = requestAnimationFrame(animate);
return () => cancelAnimationFrame(animationFrame);
}, [time]);
return html`<canvas ${canvasRef} width="300" height="300"></canvas>`;
}
export const animatedCanvas = c(AnimatedCanvas);
Важные аспекты анимации:
requestAnimationFrame синхронизирует обновления с
частотой обновления экрана.cancelAnimationFrame предотвращает утечки
памяти при размонтировании компонента.Atomico позволяет легко добавлять обработчики событий к Canvas. Например, обработка клика:
function InteractiveCanvas() {
const canvasRef = useRef();
function handleClick(event) {
const rect = canvasRef.current.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
console.log("Клик по координатам:", x, y);
}
return html`
<canvas
${canvasRef}
width="400"
height="400"
style="border: 1px solid black;"
oncl ick=${handleClick}
></canvas>
`;
}
export const interactiveCanvas = c(InteractiveCanvas);
Особенности работы с событиями:
getBoundingClientRect.Для корректного отображения на дисплеях с высоким разрешением важно масштабировать Canvas:
function HiDPICanvas() {
const canvasRef = useRef();
useEffect(() => {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
const dpr = window.devicePixelRatio || 1;
canvas.width = 300 * dpr;
canvas.height = 300 * dpr;
canvas.style.width = "300px";
canvas.style.height = "300px";
ctx.scale(dpr, dpr);
ctx.fillStyle = "purple";
ctx.fillRect(0, 0, 300, 300);
}, []);
return html`<canvas ${canvasRef}></canvas>`;
}
export const hiDpiCanvas = c(HiDPICanvas);
Принципы масштабирования:
devicePixelRatio.ctx.scale обеспечивает корректное масштабирование
отрисовки.Atomico использует Shadow DOM по умолчанию. Canvas в Shadow DOM работает идентично обычному DOM, но стоит помнить:
Canvas-компоненты Atomico могут интегрироваться с библиотеками для
графики, например, three.js или pixi.js.
Основной подход:
useRef.useEffect.Такой подход позволяет сочетать реактивный рендер Atomico с мощными движками для 2D и 3D-графики.
Atomico предоставляет полный контроль над Canvas через рефы и хуки, обеспечивая реактивность, управление жизненным циклом и поддержку сложных анимаций и интерактивности. Эта комбинация делает библиотеку удобным инструментом для интеграции динамической графики в современные веб-компоненты.