Сегодня мы вернемся к практическим занятиям по 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. Я надеюсь, вам понравился наш урок. Удачи и добро пожаловать обратно.