Сегодня мы вернемся к практическим занятиям по html5. Я думаю, что мы уже сделали хороший перерыв в наших уроках. В этом уроке я покажу вам, как создать панель для рисования, которая вращается вокруг своей оси (на объекте html5 canvas). Каждая из ваших нарисованных фигур будет вращаться в псевдо 3D режиме. Здесь определены два объекта (прототипа): точка и форма (каждая фигура состоит из нескольких точек). Основная идея — проецировать и поворачивать точки фигур, а также проводить изогнутую линию между этими точками.
Live Demo
скачать в упаковке
Теперь мы можем скачать исходный код, распаковать его и начать смотреть на код
Шаг 1. HTML
На первом этапе мы должны подготовить некоторый базовый HTML-код:
index.html
<!-- add scripts --> <script src="js/pointer.js"></script> <script src="js/main.js"></script> .... <canvas id="scene" height="600" width="800" tabindex="1"></canvas>
Как обычно, мы включаем скрипты в заголовочный раздел документа, но в нашем случае не имеет значения, где именно он находится. Наш JS-код будет выполнен только тогда, когда документ будет загружен
Шаг 2. JS (HTML5)
Как обычно, в начале каждого кода JS мы можем определить несколько глобальных переменных:
JS / main.js
// Variables var canvas, ctx; // canvas and context objects var vPointer; // draw pointer pbject var aShapes = []; // shapes array var iLMx = iLMy = 0; // last pointer positions var vActShape; // active shape object
Теперь мы можем представить наш первый объект низкого уровня — Point:
// Point object
var Point = function (x, y, z) {
this.x = x;
this.y = y;
this.z = z;
this.x0 = x;
this.z0 = z;
this.xp = 0;
this.yp = 0;
this.zp = 0;
this.fov = 2000;
}
Point.prototype.project = function () {
this.zp = this.fov / (this.fov + this.z);
this.xp = this.x * this.zp;
this.yp = this.y * this.zp;
}
Point.prototype.rotate = function (ax, ay) {
this.x = parseInt(Math.round(this.x0 * ax + this.z0 * ay));
this.z = parseInt(Math.round(this.x0 * -ay + this.z0 * ax));
}
Для объекта «Точка» у нас есть целый набор внутренних свойств и только две функции: проецировать и вращать. Другой объект Shape — это более сложный объект:
// Shape object
var Shape = function () {
this.angle = 0;
this.color = '#000';
this.halfheight = canvas.height / 2;
this.halfwidth = canvas.width / 2;
this.len = 0;
this.points = [];
return this;
}
// Add point to shape
Shape.prototype.addPoint = function (x, y, z) {
this.points.push(
new Point(Math.round(x), Math.round(y), Math.round(z))
);
this.len++;
}
// Rotate shape
Shape.prototype.rotate = function () {
this.angle += Math.PI / 180; // offset by 1 degree (radians in one degrees)
var ax = Math.cos(this.angle);
var ay = Math.sin(this.angle);
// Rotate all the points of the shape object
for (var i = 0; i < this.len; i++) {
this.points[i].rotate(ax, ay);
}
}
Shape.prototype.draw = function () {
// points projection
for (var i = 0; i < this.len; i++) {
this.points[i].project();
}
// draw a curved line between points
var p0 = this.points[0];
ctx.beginPath();
ctx.moveTo(p0.xp + this.halfwidth, p0.yp + this.halfheight);
for (var i = 1, pl = this.points.length; i < pl; i++) {
var apnt = this.points[i];
var xc = (p0.xp + apnt.xp) / 2;
var yc = (p0.yp + apnt.yp) / 2;
ctx.quadraticCurveTo(p0.xp + this.halfwidth, p0.yp + this.halfheight, xc + this.halfwidth, yc + this.halfheight);
p0 = apnt;
}
// stroke properties
ctx.lineWidth = 8;
ctx.strokeStyle = this.color;
ctx.lineCap = 'round'; // rounded end caps
ctx.stroke();
}
У него меньше параметров, но больше функций (addPoint, rotate и draw). Теперь мы можем начать добавлять несколько основных функций сцены: инициализация главной сцены (canvas) (sceneInit), функция рендеринга (drawScene) и генератор случайных цветов (getRandomColor):
// Get random color
function getRandomColor() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++ ) {
color += letters[Math.round(Math.random() * 15)];
}
return color;
}
// Draw main scene function
function drawScene() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // Clear canvas
if (vPointer.bDown) { // on mouse down
var iDx = iLMx - vPointer.X;
var iDy = iLMy - vPointer.Y;
var dif = Math.sqrt(iDx * iDx + iDy * iDy); // difference between two last points
if (dif > 5) {
if (! vActShape) {
aShapes.push( // prepare a new shape object
vActShape = new Shape()
);
vActShape.color = getRandomColor();
}
iLMx = vPointer.X;
iLMy = vPointer.Y;
vActShape.addPoint(vPointer.X - canvas.width * 0.5, vPointer.Y - canvas.height * 0.5, 0);
}
} else {
// Once mouse is released - cleanup
if (vActShape) {
vActShape = '';
iLMx = iLMy = 0;
}
// Rotate the shapes
aShapes.forEach(function(sh) {
sh.rotate();
});
}
// Draw all shapes
aShapes.forEach(function(sh) {
sh.draw();
});
}
// Initialization
function sceneInit() {
// Prepare canvas and context objects
canvas = document.getElementById('scene');
// Canvas resize
canvas.width = canvas.clientWidth;
canvas.height = window.innerHeight;
ctx = canvas.getContext('2d');
// Add two custom shapes
var oShape = new Shape();
oShape.addPoint(-200,-200,50);
oShape.addPoint(0,0,0);
oShape.addPoint(-400,200,0);
oShape.addPoint(200,-400,-50);
aShapes.push(oShape);
var oShape2 = new Shape();
oShape2.addPoint(200,200,-50);
oShape2.addPoint(0,0,0);
oShape2.addPoint(400,-200,0);
oShape2.addPoint(-200,400,50);
aShapes.push(oShape2);
// Mouse pointer event handler
vPointer = new CPointer(canvas);
// Main scene loop
setInterval(drawScene, 20);
}
// Window onload init
if (window.attachEvent) {
window.attachEvent('onload', sceneInit);
} else {
if (window.onload) {
var curronload = window.onload;
var newonload = function() {
curronload();
sceneInit();
};
window.onload = newonload;
} else {
window.onload = sceneInit;
}
}
В sceneInit мы добавили две фигуры. Как вы уже заметили, для обработки событий мыши мы используем экземпляр класса CPointer. Вот:
JS / pointer.js
CPointer = function (canvas) {
var self = this;
this.body = document.body;
this.html = document.documentElement;
this.elem = canvas;
this.X = 0;
this.Y = 0;
this.Xi = 0;
this.Yi = 0;
this.Xr = 0;
this.Yr = 0;
this.startX = 0;
this.startY = 0;
this.bDrag = false;
this.bMoved = false;
this.bDown = false;
this.bXi = 0;
this.bYi = 0;
this.sX = 0;
this.sY = 0;
this.left = canvas.offsetLeft;
this.top = canvas.offsetTop;
self.elem.onmousedown = function (e) {
self.bDrag = false;
self.bMoved = false;
self.bDown = true;
self.Xr = e.clientX;
self.Yr = e.clientY;
self.X = self.sX = self.Xr - self.left;
self.Y = self.sY = self.Yr - self.top + ((self.html && self.html.scrollTop) || self.body.scrollTop);
}
self.elem.onmousemove = function(e) {
self.Xr = (e.clientX !== undefined ? e.clientX : e.touches[0].clientX);
self.Yr = (e.clientY !== undefined ? e.clientY : e.touches[0].clientY);
self.X = self.Xr - self.left;
self.Y = self.Yr - self.top + ((self.html && self.html.scrollTop) || self.body.scrollTop);
if (self.bDown) {
self.Xi = self.bXi + (self.X - self.sX);
self.Yi = self.bYi - (self.Y - self.sY);
}
if (Math.abs(self.X - self.sX) > 2 || Math.abs(self.Y - self.sY) > 2) {
self.bMoved = true;
if (self.bDown) {
if (! self.bDrag) {
self.startX = self.sX;
self.startY = self.sY;
self.bDrag = true;
}
} else {
self.sX = self.X;
self.sY = self.Y;
}
}
}
self.elem.onmouseup = function() {
self.bXi = self.Xi;
self.bYi = self.Yi;
if (! self.bMoved) {
self.X = self.sX;
self.Y = self.sY;
}
self.bDrag = false;
self.bDown = false;
self.bMoved = false;
}
}
Live Demo
скачать в упаковке
Вывод
Сегодня мы создали вращающуюся панель, где мы можем рисовать (с помощью мыши) с использованием HTML5. Я надеюсь, вам понравился наш урок. Удачи и добро пожаловать обратно.