Make Magic object, and /magic page

This commit is contained in:
Tyler Hallada 2014-12-20 18:18:08 -05:00
parent 3c16648f09
commit 5dff1b2fd7
5 changed files with 296 additions and 256 deletions

9
_data/magic.yml Normal file
View File

@ -0,0 +1,9 @@
counting: false
waitTime: 20
step: 0.4
prune_to: 15
maxDuration: 600
minDuration: 50
splineLength: 15
maxAngleChange: 180
minAngleChange: 90

View File

@ -30,8 +30,8 @@
<link rel="shortcut icon" href="/favicon.png" /> <link rel="shortcut icon" href="/favicon.png" />
<!-- Scripts --> <!-- Scripts -->
<script src="js/AnimationFrame.min.js"></script> <script src="/js/AnimationFrame.min.js"></script>
<script async src="js/magic.js"></script> <script async src="/js/magic.js"></script>
<!-- Google Analytics --> <!-- Google Analytics -->
<script> <script>

View File

@ -3,6 +3,11 @@ layout: home
title: Tyler Hallada title: Tyler Hallada
--- ---
<script>
window.onload = function () {
new Magic().draw();
}
</script>
<div id="home"> <div id="home">
<div class="card profile-card"> <div class="card profile-card">
<div class="row clearfix"> <div class="row clearfix">

View File

@ -1,29 +1,46 @@
window.onload = function () { var Magic = function (options) {
options = options || {}
// Array to hold all active spells being drawn // Array to hold all active spells being drawn
var spells = []; this.spells = [];
// When on, wait a certain amount of time before casting (for initial // When on, wait a certain amount of time before casting (for initial
// load-up) // load-up)
var counting = true; this.counting = options.counting || true;
// The amount of time to wait (in number of animate() calls) // The amount of time to wait (in number of animate() calls)
var waitTime = 20; this.waitTime = options.waitTime || 20;
// How fast to draw the spells // How fast to draw the spells
var step = 0.2; this.step = options.step || 0.2;
// Limit of branches per spell at any time // Limit of branches per spell at any time
var prune_to = 20; this.prune_to = options.prune_to || 20;
// Max duration possible (in steps) of a spell
this.maxDuration = options.maxDuration || 600;
// Min duration possible (in steps) of a spell
this.minDuration = options.minDuration || 50;
// Max angle deviation during branch split
this.maxAngleChange = options.maxAngleChange || 180;
// Min angle deviation during branch split
this.minAngleChange = options.minAngleChange || 90;
// Average length of new spline (branch)
this.splineLength = options.splineLength || 15;
// Save this query here now that the page is loaded // Save this query here now that the page is loaded
var canvas = document.getElementById("magic"); this.canvas = document.getElementById("magic");
}
/* Get the height and width of full document. To avoid browser
/* Get the height and width of full document. To avoid browser
* incompatibility issues, choose the maximum of all height/width values. * incompatibility issues, choose the maximum of all height/width values.
* *
* Method from http://stackoverflow.com/a/1147768 */ * Method from http://stackoverflow.com/a/1147768 */
function getDocumentDimensions() { Magic.prototype.getDocumentDimensions = function () {
var body = document.body, var body = document.body,
html = document.documentElement; html = document.documentElement;
@ -36,11 +53,11 @@ window.onload = function () {
html.offsetWidth); html.offsetWidth);
return {"height": height, "width": width}; return {"height": height, "width": width};
} }
/* Specifies what will be added to the red, green, and blue values of the /* Specifies what will be added to the red, green, and blue values of the
* color at each interval. */ * color at each interval. */
function pickGradient() { Magic.prototype.pickGradient = function () {
var directions = [-1, 0, 1]; var directions = [-1, 0, 1];
var magnitudes = [1, 2, 3]; var magnitudes = [1, 2, 3];
@ -50,9 +67,9 @@ window.onload = function () {
magnitudes[Math.floor(Math.random() * 3)], magnitudes[Math.floor(Math.random() * 3)],
"b": directions[Math.floor(Math.random() * 3)] * "b": directions[Math.floor(Math.random() * 3)] *
magnitudes[Math.floor(Math.random() * 3)]}; magnitudes[Math.floor(Math.random() * 3)]};
} }
/* Alters the given color by the given gradient and returns both. /* Alters the given color by the given gradient and returns both.
If the red, green, or blue value of the color is at 0 or 255 and the If the red, green, or blue value of the color is at 0 or 255 and the
gradient for that value is not zero, then the gradient for that value gradient for that value is not zero, then the gradient for that value
@ -60,7 +77,7 @@ window.onload = function () {
Unused for now. It turns out that changing the canvas stroke color is a Unused for now. It turns out that changing the canvas stroke color is a
costly operation and slows things down a lot. */ costly operation and slows things down a lot. */
function nextColor(color, gradient) { Magic.prototype.nextColor = function(color, gradient) {
var values = ["r", "g", "b"]; var values = ["r", "g", "b"];
// Check if color at end of range and reverse gradient direction if so // Check if color at end of range and reverse gradient direction if so
@ -79,16 +96,16 @@ window.onload = function () {
} }
return {"color": color, "gradient": gradient}; return {"color": color, "gradient": gradient};
} }
/* Choose a random RGB color to begin the color gradient with */ /* Choose a random RGB color to begin the color gradient with */
function pickRandomColor() { Magic.prototype.pickRandomColor = function() {
return {"r": Math.floor(Math.random() * (255 + 1)), return {"r": Math.floor(Math.random() * (255 + 1)),
"g": Math.floor(Math.random() * (255 + 1)), "g": Math.floor(Math.random() * (255 + 1)),
"b": Math.floor(Math.random() * (255 + 1))}; "b": Math.floor(Math.random() * (255 + 1))};
} }
function drawSplineSegment(branch, context) { Magic.prototype.drawSplineSegment = function(branch, context) {
var ax = (-branch.points[0].x + 3*branch.points[1].x - 3*branch.points[2].x + branch.points[3].x) / 6; var ax = (-branch.points[0].x + 3*branch.points[1].x - 3*branch.points[2].x + branch.points[3].x) / 6;
var ay = (-branch.points[0].y + 3*branch.points[1].y - 3*branch.points[2].y + branch.points[3].y) / 6; var ay = (-branch.points[0].y + 3*branch.points[1].y - 3*branch.points[2].y + branch.points[3].y) / 6;
var bx = (branch.points[0].x - 2*branch.points[1].x + branch.points[2].x) / 2; var bx = (branch.points[0].x - 2*branch.points[1].x + branch.points[2].x) / 2;
@ -102,22 +119,22 @@ window.onload = function () {
ay*Math.pow(branch.t, 3) + by*Math.pow(branch.t, 2) + cy*branch.t + dy ay*Math.pow(branch.t, 3) + by*Math.pow(branch.t, 2) + cy*branch.t + dy
); );
context.lineTo( context.lineTo(
ax*Math.pow(branch.t+step, 3) + bx*Math.pow(branch.t+step, 2) + cx*(branch.t+step) + dx, ax*Math.pow(branch.t+this.step, 3) + bx*Math.pow(branch.t+this.step, 2) + cx*(branch.t+this.step) + dx,
ay*Math.pow(branch.t+step, 3) + by*Math.pow(branch.t+step, 2) + cy*(branch.t+step) + dy ay*Math.pow(branch.t+this.step, 3) + by*Math.pow(branch.t+this.step, 2) + cy*(branch.t+this.step) + dy
); );
branch.t += step; branch.t += this.step;
} }
function splitBranch(branch) { Magic.prototype.splitBranch = function(branch) {
var newBranches = []; var newBranches = [];
// Replace with 2 new branches // Replace with 2 new branches
for (var k = 0; k < 2; k++) { for (var k = 0; k < 2; k++) {
// Generate random deviation from previous angle // Generate random deviation from previous angle
var angle = branch.angle - (Math.random() * 180 - 90); var angle = branch.angle - (Math.random() * this.maxAngleChange - this.minAngleChange);
// Generate random length // Generate random length
var length = Math.random() * 15 + 4; var length = Math.random() * this.splineLength + 4;
// Calculate new point // Calculate new point
var x2 = branch.points[3].x + Math.sin(Math.PI * angle / 180) * length; var x2 = branch.points[3].x + Math.sin(Math.PI * angle / 180) * length;
@ -137,39 +154,39 @@ window.onload = function () {
} }
return newBranches; return newBranches;
} }
function cast(x, y, angle, chain) { Magic.prototype.cast = function(x, y, angle, chain) {
// Find random position if not defined // Find random position if not defined
if (x === undefined) { if (x === undefined) {
x = Math.floor(Math.random() * (canvas.width + 1)); x = Math.floor(Math.random() * (this.canvas.width + 1));
} }
if (y === undefined) { if (y === undefined) {
y = Math.floor(Math.random() * (canvas.height + 1)); y = Math.floor(Math.random() * (this.canvas.height + 1));
} }
if (angle === undefined) { if (angle === undefined) {
angle = Math.random() * 360; angle = Math.random() * 360;
} }
var color = pickRandomColor(); var color = this.pickRandomColor();
var colorString = "rgb(" + color.r + "," + color.g + "," + color.b + ")"; var colorString = "rgb(" + color.r + "," + color.g + "," + color.b + ")";
// Create new spell (branch) // Create new spell (branch)
spells.push({ this.spells.push({
branches:new Array({ branches:new Array({
points:new Array({x:x, y:y}, {x:x, y:y}, {x:x, y:y}, {x:x, y:y}), points:new Array({x:x, y:y}, {x:x, y:y}, {x:x, y:y}, {x:x, y:y}),
angle:angle, angle:angle,
t:0, t:0,
}), }),
color:colorString, color:colorString,
duration:Math.floor(Math.random() * (600 - 50 + 1)) + 50, duration:Math.floor(Math.random() * (this.maxDuration - this.minDuration + 1)) + 50,
chain:chain, chain:chain,
}); });
} }
/* Most of this funtion is provided by Maissan Inc. in their tutorial: /* Most of this funtion is provided by Maissan Inc. in their tutorial:
http://www.maissan.net/articles/simulating-vines */ http://www.maissan.net/articles/simulating-vines */
function drawSpells(context, sort, prune, chain) { Magic.prototype.drawSpells = function(context, sort, prune, chain) {
var AnimationFrame = window.AnimationFrame; var AnimationFrame = window.AnimationFrame;
AnimationFrame.shim(); AnimationFrame.shim();
var animationFrame = new AnimationFrame(30), var animationFrame = new AnimationFrame(30),
@ -178,51 +195,52 @@ window.onload = function () {
gradient; gradient;
context.lineWidth = 0.5; context.lineWidth = 0.5;
var self = this;
function animate(time) { function animate(time) {
// resize canvas if document size changed // resize canvas if document size changed
dimensions = getDocumentDimensions(); dimensions = self.getDocumentDimensions();
if ((dimensions.height !== canvas.height) || if ((dimensions.height !== self.canvas.height) ||
(dimensions.width !== canvas.width)) { (dimensions.width !== self.canvas.width)) {
canvas.height = dimensions.height; self.canvas.height = dimensions.height;
canvas.width = dimensions.width; self.canvas.width = dimensions.width;
} }
// if enough time has passed, cast another spell to draw // if enough time has passed, cast another spell to draw
if (timeCounter >= waitTime && counting) { if (timeCounter >= self.waitTime && self.counting) {
cast(undefined, undefined, undefined, chain); // start position self.cast(undefined, undefined, undefined, chain); // start position
counting = false; self.counting = false;
} }
// Draw branches // Draw branches
for (var i in spells) { for (var i in self.spells) {
context.beginPath(); context.beginPath();
context.strokeStyle = spells[i].color; context.strokeStyle = self.spells[i].color;
if (spells[i].duration > 0) { if (self.spells[i].duration > 0) {
for (var j in spells[i].branches) { for (var j in self.spells[i].branches) {
drawSplineSegment(spells[i].branches[j], context); self.drawSplineSegment(self.spells[i].branches[j], context);
// When finished drawing splines, create a new set of branches // When finished drawing splines, create a new set of branches
if (spells[i].branches[j].t >= 1) { if (self.spells[i].branches[j].t >= 1) {
var newBranches = splitBranch(spells[i].branches[j]); var newBranches = self.splitBranch(self.spells[i].branches[j]);
// Replace old branch with two new branches // Replace old branch with two new branches
spells[i].branches.splice(j, 1); self.spells[i].branches.splice(j, 1);
spells[i].branches = spells[i].branches.concat(newBranches); self.spells[i].branches = self.spells[i].branches.concat(newBranches);
} }
} }
// If over 10 branches, prune the branches // If over 10 branches, prune the branches
if (prune) { if (prune) {
if (sort) { if (sort) {
while (spells[i].branches.length > prune_to) { while (self.spells[i].branches.length > self.prune_to) {
spells[i].branches.pop(); self.spells[i].branches.pop();
} }
} else { } else {
while (spells[i].branches.length > prune_to) { while (self.spells[i].branches.length > self.prune_to) {
spells[i].branches.splice( self.spells[i].branches.splice(
Math.floor(Math.random() * spells[i].branches.length), Math.floor(Math.random() * self.spells[i].branches.length),
1); 1);
} }
} }
@ -230,18 +248,18 @@ window.onload = function () {
context.stroke(); context.stroke();
context.closePath(); context.closePath();
spells[i].duration--; self.spells[i].duration--;
} else { } else {
// cast a new spell at random position // cast a new spell at random position
if (spells[i].chain) { if (self.spells[i].chain) {
cast(undefined, undefined, undefined, true); self.cast(undefined, undefined, undefined, true);
} }
spells.splice(i, 1); // spell is done now self.spells.splice(i, 1); // spell is done now
} }
} }
if (counting) { if (self.counting) {
timeCounter++; timeCounter++;
} }
frameId = animationFrame.request(animate); frameId = animationFrame.request(animate);
@ -250,33 +268,31 @@ window.onload = function () {
// Drawing interval // Drawing interval
var frameId = animationFrame.request(animate); var frameId = animationFrame.request(animate);
return frameId; return frameId;
} }
function canvasClickHandler(event) { Magic.prototype.canvasClickHandler = function(event) {
var x = event.pageX; var x = event.pageX;
var y = event.pageY; var y = event.pageY;
cast(x, y, undefined, false); this.cast(x, y, undefined, false);
} }
function draw() { Magic.prototype.draw = function() {
var interval_time = 2000; var interval_time = 2000;
// Initialize canvas // Initialize canvas
var dimensions = getDocumentDimensions(); var dimensions = this.getDocumentDimensions();
canvas.height = dimensions.height; this.canvas.height = dimensions.height;
canvas.width = dimensions.width; this.canvas.width = dimensions.width;
// Check for canvas support, bind click event, and get context // Check for canvas support, bind click event, and get context
if (canvas.getContext){ if (this.canvas.getContext){
canvas.addEventListener("click", canvasClickHandler); var self = this;
this.canvas.addEventListener("click", function () { self.canvasClickHandler.apply(self, arguments); });
var context = canvas.getContext("2d"); var context = this.canvas.getContext("2d");
// Cast magic spells // Cast magic spells
var frameId = drawSpells(context, false, true, true); var frameId = this.drawSpells(context, false, true, true);
} }
} }
draw();
};

10
magic/index.html Normal file
View File

@ -0,0 +1,10 @@
---
layout: home
title: Tyler Hallada
---
<script>
window.onload = function () {
new Magic({{ site.data.magic | jsonify }}).draw();
}
</script>