• out of the blue
  • What you see is not what you get.
  • Sources

  • Main html file: dhtml/grid-deform-menu.html
    ge1doot mini library: /library/ge1doot.js
  • CSS

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

  • 
    <canvas id="screen">CANVAS lens grid menu</canvas>
    
  • JS

  • 
    /* =======================================================
     *  ---- grid deform ----
     * script: Gerard Ferrandez - January 2013
     * Released under the MIT license
     * http://www.dhteumeuleu.com/LICENSE.html
     * ======================================================= */
    
    "use strict";
    
    (function () {
    	var scr, ctx, pointer, points, planes, setup, over, overZoomed,
    		size, radius, force, ngrid, npoints, nplanes;
    	// ==== Points constructor ====
    	var Points = function (x, y) {
    		this.x  = Math.round((0.5 * (scr.width - (size * ngrid * setup.aspectRatio))) + x * setup.aspectRatio);
    		this.y  = Math.round(0.5 * (scr.height - (size * ngrid)) + y);
    		this.X  = this.x;
    		this.Y  = this.y;
    		this.vx = 0;
    		this.vy = 0;
    		this.d  = 0;
    	}
    	// ==== points Math ====
    	Points.prototype.update = function () {
    		var z, a, x, y,
    			dx = pointer.X - this.x,
    			dy = pointer.Y - this.y;
    		this.d = Math.sqrt(dx * dx + dy * dy);
    		if (this.d < radius) {
    			z = this.d * force * (radius - this.d) / radius,
    			a = Math.atan2(dy, dx);
    			x = this.x + Math.cos(a) * z;
    			y = this.y + Math.sin(a) * z;
    		} else {
    			x = this.x;
    			y = this.y;
    		}
    		this.vx = (this.vx + (x - this.X) * setup.spring) * setup.friction;
    		this.vy = (this.vy + (y - this.Y) * setup.spring) * setup.friction;
    		this.X += this.vx;
    		this.Y += this.vy;
    	}
    	// ==== Planes constructor ====
    	var Plane = function (p0, p1, p2, p3) {
    		this.p0  = p0;
    		this.p1  = p1;
    		this.p2  = p2;
    		this.p3  = p3;
    		this.img = false;
    		this.url = "";
    		this.d2  = 0;
    		this.isLoading = false;
    		this.width  = 0;
    		this.height = 0;
    		
    	}
    	// ==== compute z-index ====
    	Plane.prototype.update = function () {
    		this.zIndex = Math.max(this.p0.d, this.p1.d, this.p2.d, this.p3.d);
    	}
    	// ==== draw triangle ====
    	Plane.prototype.drawTriangle = function (p0, p1, p2, up) {
    		if (this.isLoading) {
    			if (this.img.complete) {
    				this.isLoading = false;
    				this.width  = this.img.width;
    				this.height = this.img.height;
    				this.d2     = -this.width * this.height;
    			}
    		} else {
    			var dx, dy, d;
    			ctx.save();
    			ctx.beginPath();
    			ctx.moveTo(p0.X, p0.Y + up);
    			ctx.lineTo(p1.X, p1.Y + up);
    			ctx.lineTo(p2.X, p2.Y + up);
    			ctx.closePath();
    			// ---- clip ----
    			ctx.clip();
    			// ---- transformation matrix ----
    			if (up > 0) {
    				ctx.transform(
    					(this.height * p0.X - this.height * p1.X) / this.d2, // m11
    					(this.height * p0.Y - this.height * p1.Y) / this.d2, // m12
    					(this.width  * p1.X - this.width  * p2.X) / this.d2, // m21
    					(this.width  * p1.Y - this.width  * p2.Y) / this.d2, // m22
    					 p0.X, // dx
    					 p0.Y  // dy
    				);
    			} else {
    				ctx.transform(
    					(this.height * p2.X - this.height * p1.X) / this.d2, // m11
    					(this.height * p2.Y - this.height * p1.Y) / this.d2, // m12
    					(this.width  * p0.X - this.width  * p2.X) / this.d2, // m21
    					(this.width  * p0.Y - this.width  * p2.Y) / this.d2, // m22
    					 p0.X, // dx
    					 p0.Y  // dy
    				);
    			}
    			ctx.drawImage(this.img, 0, 0);
    			ctx.restore();
    		}
    	}
    	// ==== draw Plane ====
    	Plane.prototype.draw = function () {
    		// ---- path ----
    		ctx.beginPath();
    		ctx.moveTo(this.p0.X-1, this.p0.Y-1);
    		ctx.lineTo(this.p1.X+1, this.p1.Y-1);
    		ctx.lineTo(this.p2.X+1, this.p2.Y+1);
    		ctx.lineTo(this.p3.X-1, this.p3.Y+1);
    		ctx.closePath();
    		// ---- fill color ----
    		if (ctx.isPointInPath(pointer.X, pointer.Y)) {
    			// ---- on mouse over ----
    			over = this;
    			if (this.img) scr.setCursor("pointer");
    			else scr.setCursor("default");
    		}
    		var c = setup.intensity - (this.zIndex / scr.minSize * setup.intensity);
    		ctx.fillStyle = "RGB(" + 
    			Math.round(c * setup.color[0]) + "," + 
    			Math.round(c * setup.color[1]) + "," + 
    			Math.round(c * setup.color[2]) + ")";
    		ctx.fill();
    		if (this.img) {
    			// ---- draw image ----
    			this.drawTriangle(this.p0, this.p1, this.p2,  1);
    			this.drawTriangle(this.p0, this.p2, this.p3, -1);
    		}
    	}
    	/* ==== build grid ==== */
    	var creatGrid = function () {
    		// ---- dimensions ----
    		var s   = 0.8 * scr.minSize;
    		if (scr.width < scr.height) s = s / setup.aspectRatio;
    		size    = s / ngrid;
    		radius  = s / 3;
    		// ---- create points ----
    		points = [];
    		for (var i = 0; i <= ngrid; i++) {
    			for (var j = 0; j <= ngrid; j++) {
    				points.push(
    					new Points(
    						size * j,
    						size * i
    					)
    				);
    			}
    		}
    		// ---- create grid ----
    		planes = [];
    		for (var i = 0; i < ngrid; i++) {
    			for (var j = 0; j < ngrid; j++) {
    				planes.push(
    					new Plane(
    						points[i + (ngrid + 1) * j],
    						points[i + (ngrid + 1) * j + 1],
    						points[i + (ngrid + 1) * (j + 1) + 1],
    						points[i + (ngrid + 1) * (j + 1)]
    					)
    				);
    			}
    		}
    		// ---- add grid content ----
    		for (var i = 0; i < setup.content.length; i++) {
    			var o = setup.content[i],
    				p = planes[(o.y * 1) + ngrid * o.x];
    			if (o.canvas) {
    				// ---- dynamic canvas ----
    				var cache = document.createElement('canvas');
    				p.width = cache.width = o.w || 400;
    				p.height = cache.height = o.h || 400;
    				p.d2 = -p.width * p.height;
    				var ict = cache.getContext('2d');
    				o.canvas(ict);
    				p.img = cache;
    			} else {
    				// ---- external image ----
    				p.img = new Image();
    				p.img.src = o.img;
    				p.isLoading = true;
    			}
    			p.url = o.url || false;
    		}
    	}
    	var click = function (e) {
    		if (force == -setup.zoomMin) {
    			if (over.img) {
    				// ---- zoom in ----
    				force = -setup.zoomMax;
    				overZoomed = over;
    			}
    		} else {
    			// ---- zoom out ----
    			force = -setup.zoomMin;
    			// ---- link ----
    			if (over.url) {
    				document.location.href = over.url;
    			}
    		}
    	}
    	/* ==== main loop ==== */
    	var run = function () {
    		// ---- clear screen ----
    		ctx.clearRect(0, 0, scr.width, scr.height);
    		// ---- zoom out ----
    		if (over != overZoomed) force = -setup.zoomMin;
    		// ---- update points ----
    		for (var i = 0; i < npoints; i++) {
    			points[i].update();
    		}
    		// ---- update grid ----
    		for (var i = 0; i < nplanes; i++) {
    			planes[i].update();
    		}
    		// ---- zIndex sorting ----
    		planes.sort(function (p0, p1) {
    			return p1.zIndex - p0.zIndex;
    		});
    		// ---- draw grid ----
    		for (var i = 0; i < nplanes; i++) {
    			planes[i].draw();
    		}
    		// ---- next frame ----
    		requestAnimFrame(run);
    	}
    	/* ==== init script ==== */
    	var init = function (s) {
    		// ---- setup ----
    		setup    = s;
    		force    = -s.zoomMin;
    		ngrid    = s.ngrid;
    		npoints  = (ngrid + 1) * (ngrid + 1);
    		nplanes  = ngrid * ngrid;
    		// ---- canvas ----
    		scr = new ge1doot.Screen({
    			container: "screen",
    			resize: function () {
    				scr.minSize = Math.min(scr.width, scr.height);
    				creatGrid();
    			}
    		});
    		ctx = scr.ctx;
    		scr.resize();
    		// ---- pointer ----
    		pointer = new ge1doot.Pointer({
    			tap: click
    		});
    		pointer.X = scr.width  * 0.5;
    		pointer.Y = scr.height * 0.5;
    		// ---- start engine ----
    		run();
    	}
    	return {
    		// ---- launch script -----
    		load : function (setup) {
    			window.addEventListener('load', function () {
    				init(setup);
    			}, false);
    		}
    	}
    })().load({
    	/* ----- setup ----- */
    	zoomMin: 1.5,
    	zoomMax: 6,
    	spring: 0.025,
    	friction: 0.95,
    	aspectRatio: 1.618,
    	intensity: 255,
    	color: [0,0.5,1],
    	ngrid: 6,
    	content: [
    		{x:2, y:0, img:"../images/ct33.jpg"},
    		{x:2, y:2, img:"../images/ct23.jpg"},
    		{x:4, y:1, img:"../images/ci38.jpg"},
    		{x:1, y:4, img:"../images/ct11.jpg"},
    		{x:3, y:4, img:"../images/df17.jpg"},
    		{x:0, y:2, img:"../images/ct07.jpg"},
    		{x:0, y:0, img:"../images/ct14.jpg"},
    		{x:5, y:5, img:"../images/ct35.jpg"},
    		{x:5, y:3, img:"../images/ct05.jpg"},
    		{x:1, y:1, w:400, h:400, canvas: function (ctx) {
    			ctx.fillStyle = '#fff';
    			ctx.font = 'bold 400px sans-serif';
    			ctx.textAlign = 'center';
    			ctx.textBaseline = 'bottom';
    			ctx.fillText('?', 200, 420);
    		}},
    		{x:2, y:1, w:400, h:400, canvas: function (ctx) {
    			ctx.fillStyle = '#fff';
    			ctx.font = 'bold 100px sans-serif';
    			ctx.textAlign = 'center';
    			ctx.textBaseline = 'bottom';
    			ctx.fillText('R+R-R', 200, 250);
    		}}
    	]
    });
    
    © www.dhteumeuleu.com 2004-2015