Refactored and optimized magic
This commit is contained in:
parent
2fa00b2929
commit
208a20f06f
@ -24,6 +24,7 @@
|
|||||||
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400,400italics,300,300italics,200' rel='stylesheet' type='text/css'>
|
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400,400italics,300,300italics,200' rel='stylesheet' type='text/css'>
|
||||||
|
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
|
<script src="js/AnimationFrame.min.js"></script>
|
||||||
<script async src="js/magic.js"></script>
|
<script async src="js/magic.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
8
js/AnimationFrame.min.js
vendored
Normal file
8
js/AnimationFrame.min.js
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* An even better animation frame.
|
||||||
|
*
|
||||||
|
* @copyright Oleg Slobodskoi 2013
|
||||||
|
* @website https://github.com/kof/animationFrame
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
(function(window){"use strict";var nativeRequestAnimationFrame,nativeCancelAnimationFrame;(function(){var i,vendors=["webkit","moz","ms","o"],top;try{window.top.name;top=window.top}catch(e){top=window}nativeRequestAnimationFrame=top.requestAnimationFrame;nativeCancelAnimationFrame=top.cancelAnimationFrame||top.cancelRequestAnimationFrame;for(i=0;i<vendors.length&&!nativeRequestAnimationFrame;i++){nativeRequestAnimationFrame=top[vendors[i]+"RequestAnimationFrame"];nativeCancelAnimationFrame=top[vendors[i]+"CancelAnimationFrame"]||top[vendors[i]+"CancelRequestAnimationFrame"]}nativeRequestAnimationFrame&&nativeRequestAnimationFrame(function(){AnimationFrame.hasNative=true})})();function AnimationFrame(options){if(!(this instanceof AnimationFrame))return new AnimationFrame(options);options||(options={});if(typeof options=="number")options={frameRate:options};options.useNative!=null||(options.useNative=true);this.options=options;this.frameRate=options.frameRate||AnimationFrame.FRAME_RATE;this._frameLength=1e3/this.frameRate;this._isCustomFrameRate=this.frameRate!==AnimationFrame.FRAME_RATE;this._timeoutId=null;this._callbacks={};this._lastTickTime=0;this._tickCounter=0}AnimationFrame.FRAME_RATE=60;AnimationFrame.shim=function(options){var animationFrame=new AnimationFrame(options);window.requestAnimationFrame=function(callback){return animationFrame.request(callback)};window.cancelAnimationFrame=function(id){return animationFrame.cancel(id)};return animationFrame};AnimationFrame.now=Date.now||function(){return(new Date).getTime()};AnimationFrame.navigationStart=AnimationFrame.now();AnimationFrame.perfNow=function(){if(window.performance&&window.performance.now)return window.performance.now();return AnimationFrame.now()-AnimationFrame.navigationStart};AnimationFrame.hasNative=false;AnimationFrame.prototype.request=function(callback){var self=this,delay;++this._tickCounter;if(AnimationFrame.hasNative&&self.options.useNative&&!this._isCustomFrameRate){return nativeRequestAnimationFrame(callback)}if(!callback)throw new TypeError("Not enough arguments");if(this._timeoutId==null){delay=this._frameLength+this._lastTickTime-AnimationFrame.now();if(delay<0)delay=0;this._timeoutId=window.setTimeout(function(){var id;self._lastTickTime=AnimationFrame.now();self._timeoutId=null;++self._tickCounter;for(id in self._callbacks){if(self._callbacks[id]){if(AnimationFrame.hasNative&&self.options.useNative){nativeRequestAnimationFrame(self._callbacks[id])}else{self._callbacks[id](AnimationFrame.perfNow())}delete self._callbacks[id]}}},delay)}this._callbacks[this._tickCounter]=callback;return this._tickCounter};AnimationFrame.prototype.cancel=function(id){if(AnimationFrame.hasNative&&this.options.useNative)nativeCancelAnimationFrame(id);delete this._callbacks[id]};if(typeof exports=="object"&&typeof module=="object"){module.exports=AnimationFrame}else if(typeof define=="function"&&define.amd){define(function(){return AnimationFrame})}else{window.AnimationFrame=AnimationFrame}})(window);
|
256
js/magic.js
256
js/magic.js
@ -1,5 +1,11 @@
|
|||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
|
|
||||||
|
// Array to hold all active spells being drawn
|
||||||
|
var spells = [];
|
||||||
|
|
||||||
|
// Save this query here now that the page is loaded
|
||||||
|
var 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.
|
||||||
*
|
*
|
||||||
@ -37,7 +43,10 @@ window.onload = function () {
|
|||||||
|
|
||||||
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
|
||||||
will change signs. */
|
will change signs.
|
||||||
|
|
||||||
|
Unused for now. It turns out that changing the canvas stroke color is a
|
||||||
|
costly operation and slows things down a lot. */
|
||||||
function nextColor(color, gradient) {
|
function nextColor(color, gradient) {
|
||||||
var values = ["r", "g", "b"];
|
var values = ["r", "g", "b"];
|
||||||
|
|
||||||
@ -66,7 +75,7 @@ window.onload = function () {
|
|||||||
"b": Math.floor(Math.random() * (255 + 1))};
|
"b": Math.floor(Math.random() * (255 + 1))};
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawSplineSegment(branch, t, context) {
|
function drawSplineSegment(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;
|
||||||
@ -76,16 +85,18 @@ window.onload = function () {
|
|||||||
var dx = (branch.points[0].x + 4*branch.points[1].x + branch.points[2].x) / 6;
|
var dx = (branch.points[0].x + 4*branch.points[1].x + branch.points[2].x) / 6;
|
||||||
var dy = (branch.points[0].y + 4*branch.points[1].y + branch.points[2].y) / 6;
|
var dy = (branch.points[0].y + 4*branch.points[1].y + branch.points[2].y) / 6;
|
||||||
context.moveTo(
|
context.moveTo(
|
||||||
ax*Math.pow(t, 3) + bx*Math.pow(t, 2) + cx*t + dx,
|
ax*Math.pow(branch.t, 3) + bx*Math.pow(branch.t, 2) + cx*branch.t + dx,
|
||||||
ay*Math.pow(t, 3) + by*Math.pow(t, 2) + cy*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(t+0.2, 3) + bx*Math.pow(t+0.2, 2) + cx*(t+0.2) + dx,
|
ax*Math.pow(branch.t+0.2, 3) + bx*Math.pow(branch.t+0.2, 2) + cx*(branch.t+0.2) + dx,
|
||||||
ay*Math.pow(t+0.2, 3) + by*Math.pow(t+0.2, 2) + cy*(t+0.2) + dy
|
ay*Math.pow(branch.t+0.2, 3) + by*Math.pow(branch.t+0.2, 2) + cy*(branch.t+0.2) + dy
|
||||||
);
|
);
|
||||||
|
branch.t += 0.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
function splitBranch(branch, new_branches) {
|
function splitBranch(branch) {
|
||||||
|
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++) {
|
||||||
|
|
||||||
@ -100,7 +111,7 @@ window.onload = function () {
|
|||||||
var y2 = branch.points[3].y - Math.cos(Math.PI * angle / 180) * length;
|
var y2 = branch.points[3].y - Math.cos(Math.PI * angle / 180) * length;
|
||||||
|
|
||||||
// Add to new branch array
|
// Add to new branch array
|
||||||
new_branches.push({
|
newBranches.push({
|
||||||
points:new Array(
|
points:new Array(
|
||||||
branch.points[1],
|
branch.points[1],
|
||||||
branch.points[2],
|
branch.points[2],
|
||||||
@ -108,124 +119,54 @@ window.onload = function () {
|
|||||||
{x:x2, y:y2}
|
{x:x2, y:y2}
|
||||||
),
|
),
|
||||||
angle:angle,
|
angle:angle,
|
||||||
|
t:0,
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
return newBranches;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNewBranches(branches) {
|
function cast(x, y, angle) {
|
||||||
// Create array to store next iteration of branchces
|
// Find random position if not defined
|
||||||
var new_branches = [];
|
if (x === undefined) {
|
||||||
|
x = Math.floor(Math.random() * (canvas.width + 1));
|
||||||
// Iterate over each branch
|
}
|
||||||
for (var j in branches) {
|
if (y === undefined) {
|
||||||
splitBranch(branches[j], new_branches);
|
y = Math.floor(Math.random() * (canvas.height + 1));
|
||||||
|
}
|
||||||
|
if (angle === undefined) {
|
||||||
|
angle = Math.random() * 360;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new_branches;
|
var color = pickRandomColor();
|
||||||
|
var colorString = "rgb(" + color.r + "," + color.g + "," + color.b + ")";
|
||||||
|
|
||||||
|
// Create new spell (branch)
|
||||||
|
spells.push({
|
||||||
|
branches:new Array({
|
||||||
|
points:new Array({x:x, y:y}, {x:x, y:y}, {x:x, y:y}, {x:x, y:y}),
|
||||||
|
angle:angle,
|
||||||
|
t:0,
|
||||||
|
}),
|
||||||
|
color:colorString,
|
||||||
|
duration:Math.floor(Math.random() * (600 - 50 + 1)) + 50,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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 drawTendrils(context, x, y, iterations, sort, prune, prune_to) {
|
function drawSpells(context, sort, prune, prune_to) {
|
||||||
|
var AnimationFrame = window.AnimationFrame;
|
||||||
// Set stroke colour
|
AnimationFrame.shim();
|
||||||
|
var animationFrame = new AnimationFrame(30),
|
||||||
|
timeCounter = 0,
|
||||||
|
waitTime = 80,
|
||||||
|
lastCast = 0,
|
||||||
|
color,
|
||||||
|
gradient;
|
||||||
context.lineWidth = 0.5;
|
context.lineWidth = 0.5;
|
||||||
var color = pickRandomColor();
|
|
||||||
context.strokeStyle = "rgb(" + color.r + "," + color.g + "," + color.b + ")";
|
|
||||||
|
|
||||||
// Create initial branch
|
|
||||||
var branches = [];
|
|
||||||
branches.push({
|
|
||||||
points:new Array({x:x, y:y}, {x:x, y:y}, {x:x, y:y}, {x:x, y:y}),
|
|
||||||
angle:(Math.random() * 360),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start drawing splines at t=0
|
function animate(time) {
|
||||||
var t = 0;
|
|
||||||
|
|
||||||
// Drawing interval
|
|
||||||
var interval = setInterval(function() {
|
|
||||||
|
|
||||||
var gradient = pickGradient();
|
|
||||||
|
|
||||||
// Set stroke color
|
|
||||||
var newColor = nextColor(color, gradient);
|
|
||||||
color = newColor.color;
|
|
||||||
gradient = newColor.gradient;
|
|
||||||
context.strokeStyle = "rgb(" + color.r + "," + color.g + "," + color.b + ")";
|
|
||||||
|
|
||||||
context.beginPath();
|
|
||||||
// Draw branches
|
|
||||||
for (var i in branches) {
|
|
||||||
drawSplineSegment(branches[i], t, context);
|
|
||||||
}
|
|
||||||
context.stroke();
|
|
||||||
context.closePath();
|
|
||||||
|
|
||||||
// Advance t
|
|
||||||
t += 0.2;
|
|
||||||
|
|
||||||
// When finished drawing splines, create a new set of branches
|
|
||||||
if (t >= 1) {
|
|
||||||
|
|
||||||
var new_branches = createNewBranches(branches);
|
|
||||||
|
|
||||||
// If over 10 branches, prune the branches
|
|
||||||
if (prune) {
|
|
||||||
if (sort) {
|
|
||||||
while (new_branches.length > 20) new_branches.pop();
|
|
||||||
} else {
|
|
||||||
while (new_branches.length > prune_to) {
|
|
||||||
new_branches.splice(Math.floor(Math.random() * new_branches.length), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace old branch array with new
|
|
||||||
branches = new_branches;
|
|
||||||
|
|
||||||
// Restart drawing splines at t=0
|
|
||||||
t = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count interations
|
|
||||||
iterations--;
|
|
||||||
if (iterations < 0) clearInterval(interval);
|
|
||||||
|
|
||||||
}, 32);
|
|
||||||
|
|
||||||
// Return interval
|
|
||||||
return interval;
|
|
||||||
}
|
|
||||||
|
|
||||||
function canvasClickHandler(event) {
|
|
||||||
var x = event.pageX;
|
|
||||||
var y = event.pageY;
|
|
||||||
var duration = Math.floor(Math.random() * (600 - 50 + 1)) + 50;
|
|
||||||
var canvas = document.getElementById("magic");
|
|
||||||
var context = canvas.getContext("2d");
|
|
||||||
|
|
||||||
var interval = drawTendrils(context, x, y, duration, false, true, 15);
|
|
||||||
}
|
|
||||||
|
|
||||||
function draw() {
|
|
||||||
var interval_time = 2000;
|
|
||||||
var metaInterval;
|
|
||||||
|
|
||||||
function cast() {
|
|
||||||
console.log(interval_time);
|
|
||||||
clearInterval(metaInterval);
|
|
||||||
|
|
||||||
// Go slower once started
|
|
||||||
if (interval_time === 2000) {
|
|
||||||
interval_time = 4000;
|
|
||||||
} else if (interval_time > 20000) {
|
|
||||||
return; // stop drawing
|
|
||||||
} else {
|
|
||||||
interval_time = interval_time * 1.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// resize canvas if document size changed
|
// resize canvas if document size changed
|
||||||
dimensions = getDocumentDimensions();
|
dimensions = getDocumentDimensions();
|
||||||
if ((dimensions.height !== canvas.height) ||
|
if ((dimensions.height !== canvas.height) ||
|
||||||
@ -234,18 +175,89 @@ window.onload = function () {
|
|||||||
canvas.width = dimensions.width;
|
canvas.width = dimensions.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find random position
|
// if enough time has passed, cast another spell to draw
|
||||||
var x = Math.floor(Math.random() * (canvas.width + 1));
|
if ((timeCounter - lastCast) >= waitTime) {
|
||||||
var y = Math.floor(Math.random() * (canvas.height + 1));
|
if (waitTime > 500) {
|
||||||
var duration = Math.floor(Math.random() * (600 - 50 + 1)) + 50;
|
return; // stop drawing
|
||||||
var interval = drawTendrils(context, x, y, duration, false, true, 15);
|
} else if (waitTime === 80){
|
||||||
|
waitTime = 125;
|
||||||
|
} else {
|
||||||
|
waitTime = waitTime * 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
metaInterval = setInterval(cast, interval_time);
|
console.log("cast: " + waitTime);
|
||||||
|
lastCast = timeCounter;
|
||||||
|
if (waitTime === 125) {
|
||||||
|
cast(5, 5, 270); // start position
|
||||||
|
} else {
|
||||||
|
cast(undefined, undefined, undefined); // random spell position
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw branches
|
||||||
|
for (var i in spells) {
|
||||||
|
context.beginPath();
|
||||||
|
context.strokeStyle = spells[i].color;
|
||||||
|
|
||||||
|
if (spells[i].duration > 0) {
|
||||||
|
for (var j in spells[i].branches) {
|
||||||
|
drawSplineSegment(spells[i].branches[j], context);
|
||||||
|
|
||||||
|
// When finished drawing splines, create a new set of branches
|
||||||
|
if (spells[i].branches[j].t >= 1) {
|
||||||
|
|
||||||
|
var newBranches = splitBranch(spells[i].branches[j]);
|
||||||
|
|
||||||
|
// Replace old branch with two new branches
|
||||||
|
spells[i].branches.splice(j, 1);
|
||||||
|
spells[i].branches = spells[i].branches.concat(newBranches);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If over 10 branches, prune the branches
|
||||||
|
if (prune) {
|
||||||
|
if (sort) {
|
||||||
|
while (spells[i].branches.length > prune_to) {
|
||||||
|
spells[i].branches.pop();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (spells[i].branches.length > prune_to) {
|
||||||
|
spells[i].branches.splice(
|
||||||
|
Math.floor(Math.random() * spells[i].branches.length),
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.stroke();
|
||||||
|
context.closePath();
|
||||||
|
spells[i].duration--;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
spells.splice(i, 1); // spell is done now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timeCounter++;
|
||||||
|
frameId = animationFrame.request(animate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Drawing interval
|
||||||
|
var frameId = animationFrame.request(animate);
|
||||||
|
return frameId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function canvasClickHandler(event) {
|
||||||
|
var x = event.pageX;
|
||||||
|
var y = event.pageY;
|
||||||
|
|
||||||
|
cast(x, y, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
var interval_time = 2000;
|
||||||
|
|
||||||
// Initialize canvas
|
// Initialize canvas
|
||||||
console.log("draw");
|
|
||||||
var canvas = document.getElementById("magic");
|
|
||||||
var dimensions = getDocumentDimensions();
|
var dimensions = getDocumentDimensions();
|
||||||
console.log(dimensions.height);
|
console.log(dimensions.height);
|
||||||
console.log(dimensions.width);
|
console.log(dimensions.width);
|
||||||
@ -259,7 +271,7 @@ window.onload = function () {
|
|||||||
var context = canvas.getContext("2d");
|
var context = canvas.getContext("2d");
|
||||||
|
|
||||||
// Cast magic spells
|
// Cast magic spells
|
||||||
metaInterval = setInterval(cast, interval_time);
|
var frameId = drawSpells(context, false, true, 30);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user