Skip to content

Instantly share code, notes, and snippets.

@lamberta
Created July 21, 2011 07:09
Show Gist options
  • Save lamberta/1096696 to your computer and use it in GitHub Desktop.
Save lamberta/1096696 to your computer and use it in GitHub Desktop.
Buffalo JavaScript Club Presentation: Let's Set Up an Animation Loop, Using HTML5 and a Canvas Element
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
body {
background-color: #bbb;
}
#canvas {
background-color: #fff;
}
</style>
</head>
<body>
<canvas id="canvas" width="400" height="400"></canvas>
<script>
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = (window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
return window.setTimeout(callback, 17 /*~ 1000/60*/);
});
}
function Ball (radius, color) {
this.radius = (radius === undefined) ? 40 : radius;
this.color = (color === undefined) ? "#ff0000" : color;
this.x = 0;
this.y = 0;
}
Ball.prototype.draw = function (context) {
context.save();
context.translate(this.x, this.y);
context.fillStyle = this.color;
context.beginPath();
//x, y, radius, start_angle, end_angle, anti-clockwise
context.arc(0, 0, this.radius, 0, (Math.PI * 2), true);
context.closePath();
context.fill();
context.restore();
};
window.onload = function () {
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
ball = new Ball(),
vx = Math.random() * 10 - 5,
vy = Math.random() * 10 - 5;
ball.x = canvas.width / 2;
ball.y = canvas.height / 2;
function checkBoundaries () {
var left = 0,
right = canvas.width,
top = 0,
bottom = canvas.height;
if (ball.x + ball.radius > right) {
ball.x = right - ball.radius;
vx *= -1;
} else if (ball.x - ball.radius < left) {
ball.x = left + ball.radius;
vx *= -1;
}
if (ball.y + ball.radius > bottom) {
ball.y = bottom - ball.radius;
vy *= -1;
} else if (ball.y - ball.radius < top) {
ball.y = top + ball.radius;
vy *= -1;
}
}
(function drawFrame () {
window.requestAnimationFrame(drawFrame, canvas);
context.clearRect(0, 0, canvas.width, canvas.height);
ball.x += vx;
ball.y += vy;
checkBoundaries();
ball.draw(context);
}());
};
</script>
</body>
</html>
\documentclass{beamer}
\usepackage{listings}
\usepackage{textcomp}
\usetheme{Szeged}
\usecolortheme{beaver}
\title[HTML5 Animation Loop]{Let's Set Up an Animation Loop!}
\subtitle{Using HTML5 and a Canvas Element}
\author[Lamberta]{Billy Lamberta \\ \href{http://lamberta.org}{lamberta.org} \\ \href{http://twitter.com/billyist}{@billyist}}
\date{Buffalo JavaScript Club, 21 Jul 2011}
%%For source code, use straight quotes and a monospace font
\lstset{upquote=true,basicstyle=\footnotesize\ttfamily}
\begin{document}
\begin{frame}
\titlepage
\end{frame}
\begin{frame}
\tableofcontents
\end{frame}
\section{A Minimal HTML5 Document}
\begin{frame}[fragile]
\begin{lstlisting}
<!doctype html>
<html>
</html>
\end{lstlisting}
\end{frame}
\begin{frame}[fragile]
\begin{lstlisting}
<!doctype html>
<html>
<head>
<meta charset="utf-8">
</head>
</html>
\end{lstlisting}
\end{frame}
\begin{frame}[fragile]
\begin{lstlisting}
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="style.css">
</head>
</html>
\end{lstlisting}
\end{frame}
\begin{frame}[fragile]
\begin{lstlisting}
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="canvas" width="400" height="400"></canvas>
</body>
</html>
\end{lstlisting}
\end{frame}
\begin{frame}[fragile]
\begin{lstlisting}
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="canvas" width="400" height="400"></canvas>
<script>
window.onload = function () {
//our javascript code will go here...
};
</script>
</body>
</html>
\end{lstlisting}
\end{frame}
\section{Access the Canvas API}
\begin{frame}
\tableofcontents[currentsection]
\end{frame}
\begin{frame}[fragile]
\begin{definition}
The \emph{canvas} is a bitmap image we draw \emph{to},
and \\
the \emph{context} is what we use to draw \emph{with}.
\end{definition}
\begin{lstlisting}
window.onload = function () {
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d');
};
\end{lstlisting}
\end{frame}
\begin{frame}{Example Context Commands}
\begin{itemize}
\item \texttt{moveTo}
\item \texttt{lineTo}
\item \texttt{stroke}
\item \texttt{fillRect}
\item \texttt{fillText}
\item \texttt{drawImage}
\item \textit{and many more!}
\end{itemize}
\end{frame}
\begin{frame}[fragile]
\begin{lstlisting}
window.onload = function () {
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d');
context.fillStyle = "#ff0000";
context.fillRect(50, 50, 100, 100);
};
\end{lstlisting}
\begin{center}
\includegraphics[width=0.5\textwidth]{./include/canvas01.jpg}
\end{center}
\end{frame}
\section{The Animation Loop}
\begin{frame}
\tableofcontents[currentsection]
\end{frame}
\begin{frame}[fragile]{The Old Days (6 months ago)}
Timer-based loops.
\begin{lstlisting}
var callback = function () {
console.log("tick!");
};
window.setInterval(callback, 1000/60); //fps
\end{lstlisting}
\begin{block}{Output}
\texttt{tick!}\\
\texttt{tick!}\\
\texttt{tick!}\\
\texttt{...}
\end{block}
\end{frame}
\begin{frame}{Precision Problems}
\begin{itemize}
\item The specified delay is added to the queue. If other jobs, then we wait.
\item Timers aren't accurate to the millisecond. Different browsers, different resolutions.
\end{itemize}
\end{frame}
\begin{frame}{The New Hotness}
\texttt{window.requestAnimationFrame(callback [, element]);}
\begin{itemize}
\item Browsers can consolidate CSS and JS animations into a
single reflow and repaint cycle.
\item If the animation is in a tab that's not visible, the
browser won't keep it running.
\item Vendors can continue to optimize this function.
\end{itemize}
\end{frame}
\begin{frame}[fragile]{The Shim}
\begin{lstlisting}
if (!window.requestAnimationFrame) {
window.requestAnimationFrame =
(window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
return window.setTimeout(callback, 1000/60);
});
}
\end{lstlisting}
\end{frame}
\begin{frame}[fragile]{The Loop}
\begin{lstlisting}
window.onload = function () {
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d');
(function drawFrame () {
window.requestAnimationFrame(drawFrame, canvas);
context.clearRect(0, 0, canvas.width, canvas.height);
//animation code...
}());
};
\end{lstlisting}
\end{frame}
\section{Draw Stuff}
\begin{frame}
\tableofcontents[currentsection]
\end{frame}
\begin{frame}[fragile,shrink]{ball.js}
\begin{lstlisting}
function Ball (radius, color) {
this.radius = (radius === undefined) ? 40 : radius;
this.color = (color === undefined) ? "#ff0000" : color;
this.x = 0;
this.y = 0;
}
Ball.prototype.draw = function (context) {
context.save();
context.translate(this.x, this.y);
context.fillStyle = this.color;
context.beginPath();
//x, y, radius, start_angle, end_angle, anti-clockwise
context.arc(0, 0, this.radius, 0, (Math.PI * 2), true);
context.closePath();
context.fill();
context.restore();
};
\end{lstlisting}
\end{frame}
\begin{frame}[fragile,shrink]{Detect Edges}
\begin{lstlisting}
function checkBoundaries () {
var left = 0,
right = canvas.width,
top = 0,
bottom = canvas.height;
if (ball.x + ball.radius > right) {
ball.x = right - ball.radius;
vx *= -1; //bounce
} else if (ball.x - ball.radius < left) {
ball.x = left + ball.radius;
vx *= -1;
}
if (ball.y + ball.radius > bottom) {
ball.y = bottom - ball.radius;
vy *= -1;
} else if (ball.y - ball.radius < top) {
ball.y = top + ball.radius;
vy *= -1;
}
}
\end{lstlisting}
\end{frame}
\begin{frame}[fragile,shrink]
\begin{lstlisting}
<script src="utils.js"></script>
<script src="ball.js"></script>
<script>
window.onload = function () {
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
ball = new Ball(),
vx = Math.random() * 10 - 5,
vy = Math.random() * 10 - 5;
ball.x = canvas.width / 2;
ball.y = canvas.height / 2;
(function drawFrame () {
window.requestAnimationFrame(drawFrame, canvas);
context.clearRect(0, 0, canvas.width, canvas.height);
ball.x += vx;
ball.y += vy;
checkBoundaries();
ball.draw(context);
}());
};
</script>
\end{lstlisting}
\end{frame}
\begin{frame}{References}
\begin{thebibliography}{10}
\bibitem{Irish}
Paul Irish
\newblock \href{http://paulirish.com/2011/requestanimationframe-for-smart-animating/}{\emph{requestAnimationFrame for smart animating}}
\bibitem{Lamberta}
Billy Lamberta
\newblock \href{http://www.amazon.com/dp/1430236655?tag=de05f-20}{\emph{Foundation HTML5 Animation with JavaScript}}
\bibitem{Mozilla}
Mozilla Developer Network
\newblock \href{https://developer.mozilla.org/en/DOM/window.mozRequestAnimationFrame}{\emph{requestAnimationFrame}}
\bibitem{Zakas}
Nicholas C. Zakas
\newblock \href{http://nczonline.net/blog/2011/05/03/better-javascript-animations-with-requestanimationframe/}{\emph{Better JavaScript animations with requestAnimationFrame}}
\end{thebibliography}
\end{frame}
\end{document}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment