Знакомство с HTML5 Canvas
Появилось желание взяться за изучение "убийцы Flash'а" HTML5, а в частности элемента Canvas. Это достаточно интересная технология, позволяющая создавать различные растровые 2d изображения средствами JavaScript. Просидев несколько часов за изучением технологии и вспоминании JavaScript, я написал небольшое интерактивное "приложение", которое, в первую очередь, предназначено для обучения меня и вас. Это обычные круги и прямоугольники, которые можно двигать мышью. Для ловли событий я использую jQuery, мне он кажется наиболее удобным.
Логика работы🔗︎
Принцип работы прост: сначала я записываю все создаваемые объекты в общий массив объектов и отрисовываю сцену. Затем в событии, выполняемом при нажатии на сцену, я определяю тип нажатого объекта и добавляю необходимую информацию(тип, номер и смещение курсора относительно начальной точки) о нажатом объекте в массив. После этого описывается событие, которое выполняется при перемещении мыши по сцене, в котором я обновляю координаты перемещаемого объекта в общем массиве объектов. Затем просто перерисовывается вся сцена. Ну и последнее событие, выполняемое при отпускании нажатой кнопки мыши, в котором я просто очищаю массив с информацией о выбранном объекте.
Код🔗︎
Простенький HTML с созданием canvas и подключением JavaScript кода:
<!DOCTYPE html>
<html>
<head>
<title>html5movobj</title>
</head>
<body>
<canvas id="canvas" width="1000" height="400"> Этот элемент не поддерживается </canvas>
<script type="text/javascript" src="jquery-1.9.0.min.js"></script>
<script type="text/javascript" src="scripts.js"></script>
</body>
</html>
Создадим глобальные массивы, в objs будет храниться вся информация об объектах, а в move_select будет храниться информация о перемещаемом объекте:
var objs = {Circle: [], Rectangle: []};
var move_select;
Создадим 3 класса: Circle, Rectangle и Draw. Circle, Rectangle - это классы с помощью которых я буду заносить информацию о создаваемых объектах в массив objs. А Draw - это класс, который я создал для более удобного использования функций context:
function Circle(x, y, radius, color) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
}
function Rectangle(x1, y1, x2, y2, color) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.color = color;
}
function Draw(ctx) {
this.ctx = ctx;
}
Создаём метод класса Draw, для перерисовки сцены:
Draw.prototype.scene = function(canvas) {
// очистим сцену
this.ctx.clearRect(0, 0, canvas.width, canvas.height);
// рисуем круги
for (var i = 0; i < objs.Circle.length; i++) {
this.ctx.beginPath();
this.ctx.fillStyle = objs.Circle[i].color;
this.ctx.arc(objs.Circle[i].x, objs.Circle[i].y, objs.Circle[i].radius, 0, Math.PI * 2, true);
this.ctx.closePath();
this.ctx.fill();
}
// рисуем прямоуголники
for (var i = 0; i < objs.Rectangle.length; i++) {
this.ctx.beginPath();
this.ctx.fillStyle = objs.Rectangle[i].color;
this.ctx.fillRect(objs.Rectangle[i].x1, objs.Rectangle[i].y1, objs.Rectangle[i].x2, objs.Rectangle[i].y2);
this.ctx.closePath();
this.ctx.fill();
}
}
Методы, для работы с общим массивом объектов:
// методы для работы с кругами
Draw.prototype.fillCircle = function(x, y, radius, color) {
objs.Circle.push(new Circle(x, y, radius, color));
}
Draw.prototype.moveCircle = function(id, offset, x, y) {
objs.Circle[id].x = x + offset[0];
objs.Circle[id].y = y + offset[1];
}
// методы для работы с прямоуголниками
Draw.prototype.fillRect = function(x1, y1, x2, y2, color) {
objs.Rectangle.push(new Rectangle(x1, y1, x2, y2, color));
}
Draw.prototype.moveRect = function(id, offset, x, y) {
objs.Rectangle[id].x1 = x + offset[0];
objs.Rectangle[id].y1 = y + offset[1];
}
Инициализация canvas'а, добавление объектов в массив и отрисовка сцены:
$(function(){
var canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
draw = new Draw(context);
// создадим объекты
draw.fillRect(363, 162, 20, 10, 'rgb(0, 162, 232)');
draw.fillCircle(373, 128, 80, '#456');
draw.fillCircle(343, 105, 20, '#321');
draw.fillCircle(405, 109, 20, '#321');
draw.fillCircle(336, 200, 20, '#321');
draw.fillCircle(409, 202, 20, '#321');
// нарисуем начальную сцену
draw.scene(canvas);
Опишем действия, выполняемые при нажатии на canvas:
// действия при нажатии на canvas
$('#canvas').mousedown(function(e) {
var canvasPosition = $(this).offset();
var mouseX = (e.pageX - canvasPosition.left);
var mouseY = (e.pageY - canvasPosition.top);
for (var i = 0; i < objs.Circle.length; i++) {
var circleX = objs.Circle[i].x;
var circleY = objs.Circle[i].y;
var radius = objs.Circle[i].radius;
// проверка на нажатие в область круга
if (Math.pow(mouseX - circleX, 2) + Math.pow(mouseY - circleY, 2) < Math.pow(radius, 2)) {
// запишем информацию о выбранном элементе: тип, id, и смещение указателя мыши
move_select = [ 'Circle', i, [circleX - mouseX, circleY - mouseY] ];
}
}
for (var i = 0; i < objs.Rectangle.length; i++) {
var rectX1 = objs.Rectangle[i].x1;
var rectY1 = objs.Rectangle[i].y1;
var rectX2 = objs.Rectangle[i].x1 + objs.Rectangle[i].x2;
var rectY2 = objs.Rectangle[i].y1 + objs.Rectangle[i].y2;
// проверка на нажатие в область прямоуголника
if (mouseX > rectX1 && mouseX < rectX2 && mouseY > rectY1 && mouseY < rectY2) {
// запишем информацию о выбранном элементе: тип, id, и смещение указателя мыши
move_select = [ 'Rectangle', i, [rectX1 - mouseX, rectY1 - mouseY] ];
}
}
});
Опишем действия, выполняемые при перемещении мыши по сцене:
// действия при перемещении указателя
$('#canvas').mousemove(function(e) {
var canvasPosition = $(this).offset();
var mouseX = (e.pageX - canvasPosition.left);
var mouseY = (e.pageY - canvasPosition.top);
if (move_select != undefined) {
// обновим координаты элемента
if (move_select[0] == 'Circle') {
draw.moveCircle(move_select[1], move_select[2], mouseX, mouseY);
} else if (move_select[0] == 'Rectangle') {
draw.moveRect(move_select[1], move_select[2], mouseX, mouseY);
}
// перерисуем сцену
draw.scene(canvas);
}
});
Опишем действия, выполняемые при отпускании нажатой кнопки мыши и завершим скрипт:
// действия при отпуске кнопки нажатия
$('#canvas').mouseup(function(e) {
move_select = undefined;
});
});