var Stuff = function (options) { options = options || {}; this.paused = false; // Save this query here now that the page is loaded this.canvas = document.getElementById("magic"); this.counter = 0; }; /* Get the height and width of full document. To avoid browser * incompatibility issues, choose the maximum of all height/width values. * * Method from http://stackoverflow.com/a/1147768 */ Stuff.prototype.getDocumentDimensions = function () { var body = document.body, html = document.documentElement; var height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight); var width = Math.max(body.scrollWidth, body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth); return {"height": height, "width": width}; }; /* Choose a random RGB color */ Stuff.prototype.pickRandomColor = function() { var normal = {"r": Math.floor(Math.random() * (255 + 1)), "g": Math.floor(Math.random() * (255 + 1)), "b": Math.floor(Math.random() * (255 + 1))}; var gamma = 2.2; var L = 0.2126 * Math.pow(normal.r / 255, gamma) + 0.7152 * Math.pow(normal.g / 255, gamma) + 0.0722 * Math.pow(normal.b / 255, gamma); var inverse = 'black'; if (L < 0.5) { inverse = 'white'; } return {'normal': normal, 'inverse': inverse}; }; Stuff.prototype.changeColorValue = function(color_value) { if (color_value === 255) { return color_value - this.getRandomInRange(5, 50); } else if (color_value === 0) { return color_value + this.getRandomInRange(5, 50); } var new_color_value = color_value + this.getRandomInRange(5, 100, true); if (new_color_value > 255) { return 255; } else if (new_color_value < 0) { return 0; } return new_color_value; }; /* Mutate RGB color by random increment */ Stuff.prototype.changeColor = function(color) { var normal = {"r": this.changeColorValue(color.normal.r), "g": this.changeColorValue(color.normal.g), "b": this.changeColorValue(color.normal.b)}; var gamma = 2.2; var L = 0.2126 * Math.pow(normal.r / 255, gamma) + 0.7152 * Math.pow(normal.g / 255, gamma) + 0.0722 * Math.pow(normal.b / 255, gamma); var inverse = 'black'; if (L < 0.5) { inverse = 'white'; } return {'normal': normal, 'inverse': inverse}; }; Stuff.prototype.clear = function() { this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); }; Stuff.prototype.getRandomInRange = function(min, max, negative) { rand = Math.floor(Math.random() * (max - min + 1)) + min; if (negative) { if (Math.random() < 0.5) { rand = rand * -1; } } return rand; }; Stuff.prototype.sortedInsert = function(ordered_array, value, to_insert) { for (i = 0; i < ordered_array.length; i++) { if (ordered_array[i] >= value) { ordered_array.splice(i, 0, to_insert); return; } } ordered_array.push(to_insert); }; Stuff.prototype.lineToNearestPoints = function(context, ordered_array) { for (i = 0; i < ordered_array.length; i++) { point = ordered_array[i]; context.beginPath(); context.strokeStyle = 'black'; context.moveTo(point.x, point.y); if (i !== 0) { point_before = ordered_array[i-1]; context.lineTo(point_before.x, point_before.y); } context.moveTo(point.x, point.y); if (i !== (ordered_array.length - 1)) { point_after = ordered_array[i+1]; context.lineTo(point_after.x, point_after.y); } context.stroke(); context.closePath(); } }; Stuff.prototype.initPoints = function(width, height, num_points) { this.points = []; for (i = 0; i < num_points; i++) { random_x = Math.floor(Math.random() * (width + 1)); random_y = Math.floor(Math.random() * (height + 1)); point = {'x': random_x, 'y': random_y}; this.points.push(point); } }; Stuff.prototype.distance = function(point1, point2) { return Math.sqrt(Math.pow((point1.x - point2.x), 2) + Math.pow((point1.y - point2.y), 2)); }; Stuff.prototype.drawPolygon = function(context) { for (i = 0; i < this.points.length; i++) { if (this.converge_point) { var pull_amount = this.getRandomInRange(2, 7, false); if (this.points[i].x >= this.converge_point.x) { this.points[i].x = this.points[i].x - pull_amount; } else { this.points[i].x = this.points[i].x + pull_amount; } if (this.points[i].y >= this.converge_point.y) { this.points[i].y = this.points[i].y - pull_amount; } else { this.points[i].y = this.points[i].y + pull_amount; } } else if ((this.mouse_position) && (this.old_mouse_position) && (this.distance(this.mouse_position, this.points[i]) < 50)) { // var push_amount = this.getRandomInRange(this.mouse_velocity * 0.75, this.mouse_velocity, false); // if (this.points[i].x >= this.mouse_position.x) { // this.points[i].x = this.points[i].x + push_amount; // } else { // this.points[i].x = this.points[i].x - push_amount; // } // if (this.points[i].y >= this.mouse_position.y) { // this.points[i].y = this.points[i].y + push_amount; // } else { // this.points[i].y = this.points[i].y - push_amount; // } var push_amount_x = this.old_mouse_position.x - this.mouse_position.x; var push_amount_y = this.old_mouse_position.y - this.mouse_position.y; this.points[i].x = this.points[i].x - this.getRandomInRange(push_amount_x * 0.5, push_amount_x * 1.5); this.points[i].y = this.points[i].y - this.getRandomInRange(push_amount_y * 0.5, push_amount_y * 1.5); } if (this.counter % 10 === 0) { if (Math.random() < 0.5) { this.points[i].y = this.points[i].y - this.getRandomInRange(3, 6, false); } else { this.points[i].y = this.points[i].y + this.getRandomInRange(3, 6, false); } } else { this.points[i].x = this.points[i].x + this.getRandomInRange(0, 2, true); this.points[i].y = this.points[i].y + this.getRandomInRange(0, 2, true); } // this.x_sorted = []; // this.y_sorted = []; // this.sortedInsert(this.x_sorted, this.points[i].x, point); // this.sortedInsert(this.y_sorted, this.points[i].y, point); context.beginPath(); context.arc(this.points[i].x, this.points[i].y, 1, 0, 2*Math.PI); context.stroke(); context.closePath(); } this.converge_point = null; // this.lineToNearestPoints(context, this.x_sorted); // this.lineToNearestPoints(context, this.y_sorted); }; /* Most of this funtion is provided by Maissan Inc. in their tutorial: http://www.maissan.net/articles/simulating-vines */ Stuff.prototype.drawThing = function(context, sort, prune, chain) { var AnimationFrame = window.AnimationFrame; AnimationFrame.shim(); var animationFrame = new AnimationFrame(30); context.lineWidth = 0.5; var self = this; function animate(time) { // resize canvas if document size changed dimensions = self.getDocumentDimensions(); if ((dimensions.height !== self.canvas.height) || (dimensions.width !== self.canvas.width)) { self.canvas.height = dimensions.height; self.canvas.width = dimensions.width; } if (self.points === undefined) { self.initPoints(self.canvas.width, self.canvas.height, self.canvas.width * 1.5); } if (!self.paused) { self.clear(); self.drawPolygon(context); self.counter = self.counter + 1; } frameId = animationFrame.request(animate); } // Drawing interval var frameId = animationFrame.request(animate); return frameId; }; Stuff.prototype.canvasClickHandler = function(event) { var x = event.pageX; var y = event.pageY; this.converge_point = {'x': x, 'y': y}; if (this.color) { this.color = this.changeColor(this.color); } else { this.color = this.pickRandomColor(); } this.canvas.style.backgroundColor = "rgb(" + this.color.normal.r + "," + this.color.normal.g + "," + this.color.normal.b + ")"; this.context.strokeStyle = this.color.inverse; }; Stuff.prototype.canvasMouseMoveHandler = function(event) { var x = event.pageX; var y = event.pageY; this.old_mouse_position = this.mouse_position; this.mouse_position = {'x': x, 'y': y}; if (this.old_mouse_position) { this.mouse_velocity = this.distance(this.mouse_position, this.old_mouse_position); } else { this.mouse_velocity = 0.0; } }; Stuff.prototype.draw = function() { var interval_time = 2000; // Initialize canvas var dimensions = this.getDocumentDimensions(); this.canvas.height = dimensions.height; this.canvas.width = dimensions.width; // Check for canvas support, bind click event, and get context if (this.canvas.getContext){ var self = this; this.canvas.addEventListener("click", function () { self.canvasClickHandler.apply(self, arguments); }); this.canvas.addEventListener("mousemove", function () { self.canvasMouseMoveHandler.apply(self, arguments); }); this.context = this.canvas.getContext("2d"); // Draw stuff var frameId = this.drawThing(this.context, false, true, true); } };