You gotta move, Gino Vannelli exhorted. Who better to decide the issue (the only white boy to headline on Soul Train -- true fact!). So should you want to be spicing up your website blog, working some animation into the content is a good place to start.
The concept hasn't changed since Steamboat Willy: repeatedly draw a series of images quickly. JavaScript has tools to help. Its Canvas object provides methods for drawing which, let's be honest, aren't great but let you do interesting things with enough imagination and effort. The "repeatedly" part comes from either (the older) setInterval or (the newer) requestAnimationFrame. We're told the latter is the better option, producing smoother animation by cooperating with the browser refresh internals as well as halting animation if a window is hidden, which saves battery and earns points for style.
There's lots of example JavaScript animation code out there. Alas, the examples I find make my fur puffy. Invariably, the authors are so absorbed in their minimal footprint and anonymous functions and strict adherence to a DOM workflow and CSS reusability they make the thing look more complicated than it is.
What you need is simple example code. Rudimentary example code. Code with no pride or shame.
That's exactly the kind of JavaScript I write.
Here's a little spinning grid animation I implemented in JS. Usage is straightforward: Use the "start" button to start and the "stop" button to stop. The code will use either setInterval or requestAnimationFrame for animation duty depending on the radio button setting -- SI for the former, GAF for the latter (note you must pause the animation to switch). Be advised you need a mildly up to date browser to use requestAnimationFrame -- something like Chrome 24 or Safari 6.1 or Firefox 23. Internet Explorer is iffy, as IE always is. If the animation only works on the setInterval setting, you're prolly not using a browser that supports requestAnimationFrame. (If it doesn't work on either setting, something heinous is afoot.)
Code appears below. The heart is drawGrid, which draws the grid using the roll, pitch, and yaw angles of the current view orientation. It pumps these through some trig blah-blah I stole from somewhere, then draws a dot at each appropriate location using the Canvas arc function.
We wrap drawGrid in either setInterval (which takes a repeat parameter, in milliseconds) or requestAnimationFrame (which uses the default ~60 fps setting -- see the docs). We clear either the global "Timer" or the global "RequestID" to halt. I think you'll find it's all rather straightforward. The only mild weirdness is drawGrid must itself call requestAnimationFrame on exit (be sure to update requestID in this call -- I forgot to do that and spent an evening wondering why it wasn't working).
Take the app out for a spin (wordplay!). If you like what you see, feel free to use the code for your own nefarious purposes. Just cut-and-paste into a file with an .html extension and load the file in your browser and you're good to go. Modify as desired.
Animation!
The concept hasn't changed since Steamboat Willy: repeatedly draw a series of images quickly. JavaScript has tools to help. Its Canvas object provides methods for drawing which, let's be honest, aren't great but let you do interesting things with enough imagination and effort. The "repeatedly" part comes from either (the older) setInterval or (the newer) requestAnimationFrame. We're told the latter is the better option, producing smoother animation by cooperating with the browser refresh internals as well as halting animation if a window is hidden, which saves battery and earns points for style.
There's lots of example JavaScript animation code out there. Alas, the examples I find make my fur puffy. Invariably, the authors are so absorbed in their minimal footprint and anonymous functions and strict adherence to a DOM workflow and CSS reusability they make the thing look more complicated than it is.
What you need is simple example code. Rudimentary example code. Code with no pride or shame.
That's exactly the kind of JavaScript I write.
Here's a little spinning grid animation I implemented in JS. Usage is straightforward: Use the "start" button to start and the "stop" button to stop. The code will use either setInterval or requestAnimationFrame for animation duty depending on the radio button setting -- SI for the former, GAF for the latter (note you must pause the animation to switch). Be advised you need a mildly up to date browser to use requestAnimationFrame -- something like Chrome 24 or Safari 6.1 or Firefox 23. Internet Explorer is iffy, as IE always is. If the animation only works on the setInterval setting, you're prolly not using a browser that supports requestAnimationFrame. (If it doesn't work on either setting, something heinous is afoot.)
We wrap drawGrid in either setInterval (which takes a repeat parameter, in milliseconds) or requestAnimationFrame (which uses the default ~60 fps setting -- see the docs). We clear either the global "Timer" or the global "RequestID" to halt. I think you'll find it's all rather straightforward. The only mild weirdness is drawGrid must itself call requestAnimationFrame on exit (be sure to update requestID in this call -- I forgot to do that and spent an evening wondering why it wasn't working).
Take the app out for a spin (wordplay!). If you like what you see, feel free to use the code for your own nefarious purposes. Just cut-and-paste into a file with an .html extension and load the file in your browser and you're good to go. Modify as desired.
Animation!
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/transitional.dtd">
<html>
<head>
<style type="text/css">
.button1 {
background-color: white;
border:1px solid black;
border-radius: 4px;
color: black;
text-align: center;
font-size: 18px;
font-family: arial;
display: inline-block;
width: 72px;
}
</style>
</head>
<body>
<br/>
<div style="font-family:Arial,Helvetica,sans-serif;font-size:small;text-align:center">
<br/><br/>
<canvas id="gridCanvas" width="200" height="200" style="border-style:solid;border-width:2px">
your browser does not support canvas :-(
</canvas>
<br/>
<input type="button" class="button1" id="startButton" value=" start " onclick="startRotation()" />
<input type="button"class="button1" id="stopButton" value=" stop " onclick="stopRotation()" />
<br/><br/>
<input type="button" class="button1" id="SIButton" value=" SI " onclick="chooseSI()" />
<input type="button"class="button1" id="GAFButton" value=" GAF " onclick="chooseGAF()" />
</div>
<body onload = rotatorInit();>
<script language="JavaScript" type="text/javascript">
<!-- hides script from old browers - it just an html comment block
var POINT_X; // x-position of grid points
var POINT_Y; // y-position of grid points
var Theta_radians; // current view roll angle
var Omega_radians; // current view pitch angle
var Phi_radians; // current view yaw angle
var Timer; // animation timer for SetInterval()
var RequestID; // ID for getAnimationFrame()
var AnimationFunction;
var kUseSetInterval = 1;
var kUseGetAnimationFrame = 2;
var kNPoints = 100; // number of grid points
var kPointSize = 5; // radius for drawing
var kDeltaAngle = 0.05; // angle increment
// -----------------------------------------------------------------
// INIT
// -----------------------------------------------------------------
function rotatorInit()
{
// we assume a planar grid (at Z = 0) -- homework: add POINT_Z
// and modify the drawing code to handly a true 3D object :-)
POINT_X = new Array(kNPoints);
POINT_Y = new Array(kNPoints);
Theta_radians = 0;
Omega_radians = 0;
Phi_radians = 0;
newGrid();
document.getElementById("startButton").disabled = false;
document.getElementById("startButton").style.color = "black";
document.getElementById("stopButton").disabled = true;
document.getElementById("stopButton").style.color = "grey";
AnimationFunction = kUseSetInterval;
document.getElementById("SIButton").style.border = "5px solid red";
document.getElementById("GAFButton").style.border = "1px solid black";
}
function newGrid()
{
var canvas = document.getElementById("gridCanvas");
var context = canvas.getContext('2d');
// compute number of rows and cols
var iMax = Math.ceil(Math.sqrt(kNPoints));
var delta_x = canvas.width / (iMax + 1);
var delta_y = canvas.height / (iMax + 1);
var index = 0;
for (var irow = 0; irow < iMax; irow++) {
for (var icol = 0; icol < iMax; icol++) {
POINT_X[index] = delta_x + (icol * delta_x);
POINT_Y[index] = delta_y + (irow * delta_y);
index++;
}
}
drawGrid();
}
// -----------------------------------------------------------------
// GRAPHICS
// -----------------------------------------------------------------
function animateGrid()
{
// this function called if using getAnimationFrame
// (else we call drawGrid directly)
// 1. draw
drawGrid();
// 2. reload
//
// some sources use "window.requestAnimationFrame(animateGrid)"
// -- either seems to work
RequestID = requestAnimationFrame(animateGrid);
}
function drawGrid()
{
var canvas = document.getElementById("gridCanvas");
var context = canvas.getContext('2d');
// erase
context.fillStyle = "#FFFFFF";
context.fillRect(0,0,canvas.width,canvas.height);
// standard 3D trig blah-blah
var cc = Math.cos(Theta_radians) * Math.cos(Omega_radians);
var cs = Math.cos(Theta_radians) * Math.sin(Omega_radians);
var sc = Math.sin(Theta_radians) * Math.cos(Omega_radians);
var ss = Math.sin(Theta_radians) * Math.sin(Omega_radians);
var ssc = ss * Math.cos(Phi_radians);
var scc = sc * Math.cos(Phi_radians);
var csc = cs * Math.cos(Phi_radians);
var ccc = cc * Math.cos(Phi_radians);
var x, y;
var x_ecl, y_ecl;
var X0 = canvas.width / 2.0;
var Y0 = canvas.height / 2.0;
// redraw grid in current view orientation
context.fillStyle = "#FF0000";
for (var index = 0; index < kNPoints; index++) {
x = POINT_X[index] - X0;
y = POINT_Y[index] - Y0;
x_ecl = (cc - ssc) * x + (-sc - csc) * y;
y_ecl = (cs + scc) * x + (-ss + ccc) * y;
context.beginPath();
context.arc(X0 + x_ecl, Y0 + y_ecl, kPointSize, 0, 6.28318);
context.fill();
}
// increment roll, pitch, and yaw for next call
// (incrementing each differently gives a nice effect)
Theta_radians += kDeltaAngle;
Theta_radians += kDeltaAngle;
Omega_radians += kDeltaAngle;
Phi_radians += kDeltaAngle;
Phi_radians += kDeltaAngle;
Phi_radians += kDeltaAngle;
}
// -----------------------------------------------------------------
// GUI
// -----------------------------------------------------------------
function startRotation()
{
document.getElementById("startButton").disabled = true;
document.getElementById("startButton").style.color = "grey";
document.getElementById("stopButton").disabled = false;
document.getElementById("stopButton").style.color = "black";
document.getElementById("SIButton").disabled = true;
document.getElementById("SIButton").style.color = "grey";
document.getElementById("GAFButton").disabled = true;
document.getElementById("GAFButton").style.color = "grey";
if (AnimationFunction === kUseSetInterval) {
Timer = setInterval(drawGrid, 25);
//Timer = setInterval("drawGrid()", 25); // also works
//Timer = setInterval("drawGrid", 25); // this is wrong
} else {
// some sources use "window.requestAnimationFrame(animateGrid)"
// -- either seems to work
RequestID = requestAnimationFrame(animateGrid);
}
}
function stopRotation()
{
document.getElementById("startButton").disabled = false;
document.getElementById("startButton").style.color = "black";
document.getElementById("stopButton").disabled = true;
document.getElementById("stopButton").style.color = "grey";
document.getElementById("SIButton").disabled = false;
document.getElementById("SIButton").style.color = "black";
document.getElementById("GAFButton").disabled = false;
document.getElementById("GAFButton").style.color = "black";
if (AnimationFunction === kUseSetInterval) {
clearInterval(Timer);
} else {
// some sources use "window.cancelAnimationFrame(RequestID)"
// -- either seems to work
cancelAnimationFrame(RequestID);
}
}
// switching between SI and GAF assumes the animation is paused
function chooseSI()
{
AnimationFunction = kUseSetInterval;
document.getElementById("SIButton").style.border = "5px solid red";
document.getElementById("GAFButton").style.border = "1px solid black";
}
function chooseGAF()
{
AnimationFunction = kUseGetAnimationFrame;
document.getElementById("GAFButton").style.border = "5px solid red";
document.getElementById("SIButton").style.border = "1px solid black";
}
// this closes the html comment but is also a js comment -->
</script>
</body>
</html>
No comments:
Post a Comment