• circumscrible
  • Round and round we go -- where you'll end up nobody knows.
  • Sources

  • Main html file: dhtml/draw3D-auto.html
    ge1doot mini library: /library/ge1doot.js
  • CSS

  • 
    html {
    	overflow: hidden;
    	-ms-touch-action: none;
    	-ms-content-zooming: none;
    }
    body {
    	position: absolute;
    	margin: 0;
    	padding: 0;
    	background: #000;
    	width: 100%;
    	height: 100%;
    }
    #screen {
    	position: absolute;
    	width: 100%;
    	height: 100%;
    	cursor: pointer;
    }
    
  • HTML

  • 
    <canvas id="screen">HTML5 CANVAS 3D Drawing demo</canvas>
    
  • JS

  • 
    /* =======================================================
     *  ---- HTML5 CANVAS 3D drawing ----
     * script: Gerard Ferrandez - 7 February 2013
     * Released under the MIT license
     * http://www.dhteumeuleu.com/LICENSE.html
     * ======================================================= */
     
    "use strict";
    
    (function () {
    	// ==== private variables =====
    	var scr, ctx, pointer;
    	var shapes = [];
    	var sparks = [];
    	var sparkId = 0;
    	var fov = 650;
    	var globalZ = 0;
    	var xm = 0;
    	var ym = 0;
    	var auto = true;
    	var currentShape;
    	var start = true;
    	// ==== spark object ====
    	var Spark = function (x, y) {
    		this.x = x;
    		this.y = y;
    		this.sx = Math.random() - 0.5;
    		this.sy = 5 + Math.random() * 10;
    	}
    	// ==== draw sparks ====
    	Spark.prototype.draw = function () {
    		if (this.y < scr.height) {
    			this.x += this.sx;
    			this.y += this.sy;
    			ctx.moveTo(this.x, this.y - 2);
    			ctx.lineTo(this.x, this.y);
    		}
    	}
    	// ==== shape object ====
    	var Shape = function () {
    		this.points = [];
    		this.length = 0;
    		this.filled = false;
    		this.color = "";
    		this.angle = 0;
    		this.fov = fov;
    		return this;
    	}
    	// ==== add point ====
    	Shape.prototype.addPoint = function (x, y, z) {
    		this.points.push(
    			new Point(Math.round(x), Math.round(y), Math.round(z))
    		);
    		this.length++;
    		if (Math.random() > 0.5) {
    			sparks[sparkId++] = new Spark(x + scr.width * 0.5, y + scr.height * 0.5);
    			if (sparkId == 100) sparkId = 0;
    		}
    	}
    	// ==== rotate shape ====
    	Shape.prototype.rotate = function () {
    		// ---- increment angle ----
    		this.angle += Math.PI / 180;
    		var ax = Math.cos(this.angle);
    		var ay = Math.sin(this.angle);
    		// ---- points rotation ----
    		for (var i = 0; i < this.length; i++) {
    			this.points[i].rotate(ax, ay);
    		}
    	}
    	// ==== draw shape ====
    	Shape.prototype.draw = function () {
    		// ---- 3D to 2D points projection ----
    		for (var i = 0; i < this.length; i++) {
    			this.points[i].project(this.fov);
    		}
    		// ---- draw smooth curve through N points ----
    		var p0 = this.points[0];
    		var lf = scr.width * 0.5;
    		var tp = scr.height * 0.5;
    		ctx.beginPath();
    		ctx.moveTo(Math.random() * 3 - 1.5 + p0.xp + lf, Math.random() * 3 - 1.5 + p0.yp + tp);
    		for (var i = 1, l = this.points.length; i < l; i++) {
    			var p1 = this.points[i];
    			var xc = Math.random() * 3 - 1.5 + (p0.xp + p1.xp) / 2;
    			var yc = Math.random() * 3 - 1.5 + (p0.yp + p1.yp) / 2;
    			ctx.quadraticCurveTo(p0.xp + lf, p0.yp + tp, xc + lf, yc + tp);
    			p0 = p1;
    		}
    		// ---- paint ----
    		ctx.strokeStyle = "#fff";
    		ctx.lineWidth = 10;
    		ctx.lineCap = "round";
    		ctx.lineJoin = "round";
    		if (this.filled) {
    			ctx.closePath();
    			ctx.fillStyle = this.color;
    			ctx.fill();
    		}
    		ctx.stroke();
    	}
    	// ==== 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;
    	}
    	// ==== 3D to 2D point projection ====
    	Point.prototype.project = function (sfov) {
    		this.zp = sfov / (sfov + this.z);
    		this.xp = this.x * this.zp;
    		this.yp = this.y * this.zp;
    	}
    	// ==== rotate point ====
    	Point.prototype.rotate = function (ax, ay) {
    		this.x = Math.round(this.x0 *  ax + this.z0 * ay);
    		this.z = Math.round(this.x0 * -ay + this.z0 * ax);
    	}
    	// ==== painting pointer ====
    	var movePointer = function () {
    		if (pointer.isDown) {
    			var dx = xm - pointer.X;
    			var dy = ym - pointer.Y;
    			var d = Math.sqrt(dx * dx + dy * dy);
    			if (d > 10) {
    				if (!currentShape) {
    					if (start) {
    						start = false;
    						shapes.length = 0;
    					}
    					shapes.push(
    						currentShape = new Shape()
    					);
    				}
    				var z = fov / (fov + globalZ);
    				currentShape.addPoint(
    					(pointer.X - scr.width  * 0.5) / z, 
    					(pointer.Y - scr.height * 0.5) / z, 
    					globalZ
    				);
    				xm = pointer.X;
    				ym = pointer.Y;
    				// ---- closing shape ----
    				currentShape.filled = false;
    				currentShape.color = "";
    				var first = currentShape.points[0];
    				var last  = currentShape.points[currentShape.length - 1];
    				var dx = last.x - first.x;
    				var dy = last.y - first.y;
    				var dz = last.z - first.z;
    				var d = Math.sqrt(dx * dx + dy * dy + dz * dz);
    				if (d < 15) {
    					if (currentShape.length > 4) {
    						currentShape.color = 'hsla(' + Math.round(Math.random() * 360) + ', 90%, 60%, 0.2)';
    						currentShape.filled = true;
    					}
    				}
    			}
    		} else {
    			// ---- up ----
    			if (currentShape) {
    				currentShape = false;
    			}
    			// ---- rotate ----
    			if (auto) {
    				var i = 0, s;
    				while ( s = shapes[i++]) s.rotate();
    			}
    		}
    	}
    	// ==== save drawing ====
    	var save = function (id) {
    		// ---- clean up ----
    		var array = shapes.slice(0);
    		for (var i = 0; i < array.length; i++) {
    			delete array[i].angle;
    			var pts = array[i].points;
    			for (var j = 0; j < pts.length; j++) {
    				var p = pts[j];
    				for (var k in p) {
    					if (k.length != 1) delete p[k];
    				}
    			}
    		}
    		// ---- save json to local storage ----
    		var a = JSON.stringify(array);
    		window.localStorage.setItem(id, a);
    		// ---- re-load ----
    		load(id);
    	}
    	// ==== load drawing ====
    	var load = function (id) {
    		// ---- clear all ----
    		shapes.length = 0;
    		// ---- load ----
    		var array = JSON.parse(window.localStorage.getItem(id));
    		// ---- rebuild objects ----
    		build(array);
    	}
    	// ==== inject data ====
    	var build = function(array) {
    		if (array) {
    			for (var i = 0; i < array.length; i++) {
    				shapes.push(
    					currentShape = new Shape()
    				);
    				var p = array[i].points;
    				for (var j = 0; j < p.length; j++) {
    					currentShape.points.push(
    						new Point(p[j].x, p[j].y, p[j].z)
    					);
    				}
    				currentShape.length = array[i].length;
    				currentShape.filled = array[i].filled;
    				currentShape.color  = array[i].color;
    			}
    		}
    	}
    	// ==== init script ====
    	var init = function (json) {
    		// ---- screen ----
    		scr = new ge1doot.Screen({
    			container: "screen",
    			resize: function () {
    				fov = Math.round(scr.width * 0.5);
    			}
    		});
    		scr.resize();
    		ctx = scr.ctx;
    		// ---- pointer events ----
    		pointer = new ge1doot.Pointer({	});
    		// ---- some key events ----
    		document.body.onkeydown = function (e) {
    			// ---- storage detection ----
    			var storage = typeof window.localStorage == 'object';
    			// ---- hold/release rotation [SPACE] ----
    			if (e.keyCode == 32) {
    				auto = !auto;
    			}
    			// ---- undo shapes [DEL] ----
    			if (e.keyCode == 46) {
    				if (shapes.length > 0) {
    					shapes.length--;
    				}
    			}
    			// ---- switch global Z [Z] ----
    			if (e.keyCode == 90) {
    				if (globalZ == 0) globalZ = fov * 0.35; else globalZ = 0;
    			}
    			// ---- save/load [S/L]----
    			if (e.keyCode == 83 && storage) save("circumscrible");
    			if (e.keyCode == 76 && storage) load("circumscrible");
    			return false;
    		}
    		// ---- intro drawing ----
    		build(json);
    		// ---- engine start ----
    		run();
    	}
    	// ======== main loop ========
    	var run = function () {
    		ctx.clearRect(0, 0, scr.width, scr.height);
    		movePointer();
    		// ---- draw shapes ----
    		var i = 0, s;
    		while ( s = shapes[i++]) {
    			s.draw();
    		}
    		// ---- sparks ----
    		ctx.beginPath();
    		var i = 0, s;
    		while ( s = sparks[i++]) {
    			s.draw();
    		}
    		ctx.lineWidth = 1;
    		ctx.strokeStyle = "#fff";
    		ctx.stroke();
    		// ---- animation loop ----
    		requestAnimFrame(run);
    	}
    	return {
    		// ---- onload event ----
    		load : function (json) {
    			window.addEventListener('load', function () {
    				init(json);
    			}, false);
    		}
    	}
    })().load([{"points":[{"x":10,"y":-18,"z":-80},{"x":10,"y":-8,"z":-82},{"x":10,"y":2,"z":-83},{"x":10,"y":12,"z":-85},{"x":11,"y":22,"z":-86},{"x":11,"y":33,"z":-87}],"length":6,"filled":false,"color":"","fov":506},{"points":[{"x":10,"y":-21,"z":-81},{"x":9,"y":-23,"z":-71},{"x":7,"y":-22,"z":-61},{"x":6,"y":-15,"z":-52},{"x":6,"y":-5,"z":-47},{"x":6,"y":6,"z":-47},{"x":6,"y":17,"z":-49},{"x":7,"y":26,"z":-57},{"x":8,"y":28,"z":-67},{"x":9,"y":29,"z":-76},{"x":11,"y":29,"z":-87}],"length":11,"filled":false,"color":"","fov":506},{"points":[{"x":4,"y":-30,"z":-29},{"x":4,"y":-18,"z":-29},{"x":4,"y":-8,"z":-30},{"x":4,"y":5,"z":-30},{"x":4,"y":16,"z":-30},{"x":4,"y":27,"z":-30}],"length":6,"filled":false,"color":"","fov":506},{"points":[{"x":4,"y":-30,"z":-31},{"x":2,"y":-33,"z":-20},{"x":1,"y":-32,"z":-9},{"x":0,"y":-25,"z":0},{"x":0,"y":-15,"z":1},{"x":1,"y":-8,"z":-9},{"x":3,"y":-8,"z":-22},{"x":2,"y":3,"z":-17},{"x":1,"y":13,"z":-10},{"x":0,"y":22,"z":-3},{"x":0,"y":32,"z":1}],"length":11,"filled":false,"color":"","fov":506},{"points":[{"x":-4,"y":-29,"z":29},{"x":-3,"y":-18,"z":25},{"x":-2,"y":-7,"z":20},{"x":-2,"y":3,"z":16},{"x":-1,"y":13,"z":12},{"x":-1,"y":23,"z":10}],"length":6,"filled":false,"color":"","fov":506},{"points":[{"x":-4,"y":-34,"z":29},{"x":-4,"y":-22,"z":33},{"x":-4,"y":-10,"z":36},{"x":-5,"y":2,"z":38},{"x":-5,"y":13,"z":41},{"x":-5,"y":23,"z":43},{"x":-6,"y":33,"z":46}],"length":7,"filled":false,"color":"","fov":506},{"points":[{"x":-2,"y":2,"z":17},{"x":-3,"y":2,"z":28}],"length":2,"filled":false,"color":"","fov":506},{"points":[{"x":-2,"y":5,"z":13},{"x":-3,"y":3,"z":24},{"x":-4,"y":3,"z":35}],"length":3,"filled":false,"color":"","fov":506},{"points":[{"x":-6,"y":-31,"z":49},{"x":-6,"y":-20,"z":52},{"x":-7,"y":-10,"z":55},{"x":-7,"y":1,"z":59},{"x":-8,"y":12,"z":62},{"x":-8,"y":22,"z":66},{"x":-9,"y":12,"z":73},{"x":-10,"y":2,"z":77},{"x":-11,"y":7,"z":86},{"x":-11,"y":18,"z":91},{"x":-12,"y":28,"z":95},{"x":-13,"y":15,"z":104},{"x":-13,"y":5,"z":108},{"x":-14,"y":-12,"z":115},{"x":-15,"y":-22,"z":119},{"x":-15,"y":-33,"z":124}],"length":16,"filled":false,"color":"","fov":506}]);
    
    © www.dhteumeuleu.com 2004-2015