<!DOCTYPE html>
<head>
<title>sketch.js » Basic Example</title>
<link rel="stylesheet" href="css/example.css">
<style type="text/css">
html, body {
background: none;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
(function ( root, factory ) {
if ( typeof exports === 'object' ) {
// CommonJS like
module.exports = factory(root, root.document);
} else if ( typeof define === 'function' && define.amd ) {
// AMD
define( function() { return factory( root, root.document ); });
} else {
// Browser global
root.Sketch = factory( root, root.document );
}
}( typeof window !== "undefined" ? window : this, function ( window, document ) {
"use strict";
/*
----------------------------------------------------------------------
Config
----------------------------------------------------------------------
*/
var MATH_PROPS = 'E LN10 LN2 LOG2E LOG10E PI SQRT1_2 SQRT2 abs acos asin atan ceil cos exp floor log round sin sqrt tan atan2 pow max min'.split( ' ' );
var HAS_SKETCH = '__hasSketch';
var M = Math;
var CANVAS = 'canvas';
var WEBGL = 'webgl';
var DOM = 'dom';
var doc = document;
var win = window;
var instances = [];
var defaults = {
fullscreen: true,
autostart: true,
autoclear: true,
autopause: true,
container: doc.body,
interval: 1,
globals: true,
retina: false,
type: CANVAS
};
var keyMap = {
8: 'BACKSPACE',
9: 'TAB',
13: 'ENTER',
16: 'SHIFT',
27: 'ESCAPE',
32: 'SPACE',
37: 'LEFT',
38: 'UP',
39: 'RIGHT',
40: 'DOWN'
};
/*
----------------------------------------------------------------------
Utilities
----------------------------------------------------------------------
*/
function isArray( object ) {
return Object.prototype.toString.call( object ) == '[object Array]';
}
function isFunction( object ) {
return typeof object == 'function';
}
function isNumber( object ) {
return typeof object == 'number';
}
function isString( object ) {
return typeof object == 'string';
}
function keyName( code ) {
return keyMap[ code ] || String.fromCharCode( code );
}
function extend( target, source, overwrite ) {
for ( var key in source )
if ( overwrite || !( key in target ) )
target[ key ] = source[ key ];
return target;
}
function proxy( method, context ) {
return function() {
method.apply( context, arguments );
};
}
function clone( target ) {
var object = {};
for ( var key in target ) {
if ( key === 'webkitMovementX' || key === 'webkitMovementY' )
continue;
if ( isFunction( target[ key ] ) )
object[ key ] = proxy( target[ key ], target );
else
object[ key ] = target[ key ];
}
return object;
}
/*
----------------------------------------------------------------------
Constructor
----------------------------------------------------------------------
*/
function constructor( context ) {
var request, handler, target, parent, bounds, index, suffix, clock, node, copy, type, key, val, min, max, w, h;
var counter = 0;
var touches = [];
var resized = false;
var setup = false;
var ratio = win.devicePixelRatio || 1;
var isDiv = context.type == DOM;
var is2D = context.type == CANVAS;
var mouse = {
x: 0.0, y: 0.0,
ox: 0.0, oy: 0.0,
dx: 0.0, dy: 0.0
};
var eventMap = [
context.eventTarget || context.element,
pointer, 'mousedown', 'touchstart',
pointer, 'mousemove', 'touchmove',
pointer, 'mouseup', 'touchend',
pointer, 'click',
pointer, 'mouseout',
pointer, 'mouseover',
doc,
keypress, 'keydown', 'keyup',
win,
active, 'focus', 'blur',
resize, 'resize'
];
var keys = {}; for ( key in keyMap ) keys[ keyMap[ key ] ] = false;
function trigger( method ) {
if ( isFunction( method ) )
method.apply( context, [].splice.call( arguments, 1 ) );
}
function bind( on ) {
for ( index = 0; index < eventMap.length; index++ ) {
node = eventMap[ index ];
if ( isString( node ) )
target[ ( on ? 'add' : 'remove' ) + 'EventListener' ].call( target, node, handler, false );
else if ( isFunction( node ) )
handler = node;
else target = node;
}
}
function update() {
cAF( request );
request = rAF( update );
if ( !setup ) {
trigger( context.setup );
setup = isFunction( context.setup );
}
if ( !resized ) {
trigger( context.resize );
resized = isFunction( context.resize );
}
if ( context.running && !counter ) {
context.dt = ( clock = +new Date() ) - context.now;
context.millis += context.dt;
context.now = clock;
trigger( context.update );
// Pre draw
if ( is2D ) {
if ( context.retina ) {
context.save();
context.scale( ratio, ratio );
}
if ( context.autoclear )
context.clear();
}
// Draw
trigger( context.draw );
// Post draw
if ( is2D && context.retina )
context.restore();
}
counter = ++counter % context.interval;
}
function resize() {
target = isDiv ? context.style : context.canvas;
suffix = isDiv ? 'px' : '';
w = context.width;
h = context.height;
if ( context.fullscreen ) {
h = context.height = win.innerHeight;
w = context.width = win.innerWidth;
}
if ( context.retina && is2D && ratio ) {
target.style.height = h + 'px';
target.style.width = w + 'px';
w *= ratio;
h *= ratio;
}
if ( target.height !== h )
target.height = h + suffix;
if ( target.width !== w )
target.width = w + suffix;
if ( is2D && !context.autoclear )
context.scale( ratio, ratio );
if ( setup ) trigger( context.resize );
}
function align( touch, target ) {
bounds = target.getBoundingClientRect();
touch.x = touch.pageX - bounds.left - (win.scrollX || win.pageXOffset);
touch.y = touch.pageY - bounds.top - (win.scrollY || win.pageYOffset);
return touch;
}
function augment( touch, target ) {
align( touch, context.element );
target = target || {};
target.ox = target.x || touch.x;
target.oy = target.y || touch.y;
target.x = touch.x;
target.y = touch.y;
target.dx = target.x - target.ox;
target.dy = target.y - target.oy;
return target;
}
function process( event ) {
event.preventDefault();
copy = clone( event );
copy.originalEvent = event;
if ( copy.touches ) {
touches.length = copy.touches.length;
for ( index = 0; index < copy.touches.length; index++ )
touches[ index ] = augment( copy.touches[ index ], touches[ index ] );
} else {
touches.length = 0;
touches[0] = augment( copy, mouse );
}
extend( mouse, touches[0], true );
return copy;
}
function pointer( event ) {
event = process( event );
min = ( max = eventMap.indexOf( type = event.type ) ) - 1;
context.dragging =
/down|start/.test( type ) ? true :
/up|end/.test( type ) ? false :
context.dragging;
while( min )
isString( eventMap[ min ] ) ?
trigger( context[ eventMap[ min-- ] ], event ) :
isString( eventMap[ max ] ) ?
trigger( context[ eventMap[ max++ ] ], event ) :
min = 0;
}
function keypress( event ) {
key = event.keyCode;
val = event.type == 'keyup';
keys[ key ] = keys[ keyName( key ) ] = !val;
trigger( context[ event.type ], event );
}
function active( event ) {
if ( context.autopause )
( event.type == 'blur' ? stop : start )();
trigger( context[ event.type ], event );
}
// Public API
function start() {
context.now = +new Date();
context.running = true;
}
function stop() {
context.running = false;
}
function toggle() {
( context.running ? stop : start )();
}
function clear() {
if ( is2D )
context.clearRect( 0, 0, context.width * ratio, context.height * ratio );
}
function destroy() {
parent = context.element.parentNode;
index = instances.indexOf( context );
if ( parent ) parent.removeChild( context.element );
if ( ~index ) instances.splice( index, 1 );
bind( false );
stop();
}
extend( context, {
touches: touches,
mouse: mouse,
keys: keys,
dragging: false,
running: false,
millis: 0,
now: NaN,
dt: NaN,
destroy: destroy,
toggle: toggle,
clear: clear,
start: start,
stop: stop
});
instances.push( context );
return ( context.autostart && start(), bind( true ), resize(), update(), context );
}
/*
----------------------------------------------------------------------
Global API
----------------------------------------------------------------------
*/
var element, context, Sketch = {
CANVAS: CANVAS,
WEB_GL: WEBGL,
WEBGL: WEBGL,
DOM: DOM,
instances: instances,
install: function( context ) {
if ( !context[ HAS_SKETCH ] ) {
for ( var i = 0; i < MATH_PROPS.length; i++ )
context[ MATH_PROPS[i] ] = M[ MATH_PROPS[i] ];
extend( context, {
TWO_PI: M.PI * 2,
HALF_PI: M.PI / 2,
QUARTER_PI: M.PI / 4,
random: function( min, max ) {
if ( isArray( min ) )
return min[ ~~( M.random() * min.length ) ];
if ( !isNumber( max ) )
max = min || 1, min = 0;
return min + M.random() * ( max - min );
},
lerp: function( min, max, amount ) {
return min + amount * ( max - min );
},
map: function( num, minA, maxA, minB, maxB ) {
return ( num - minA ) / ( maxA - minA ) * ( maxB - minB ) + minB;
}
});
context[ HAS_SKETCH ] = true;
}
},
create: function( options ) {
options = extend( options || {}, defaults );
if ( options.globals ) Sketch.install( self );
element = options.element = options.element || doc.createElement( options.type === DOM ? 'div' : 'canvas' );
context = options.context = options.context || (function() {
switch( options.type ) {
case CANVAS:
return element.getContext( '2d', options );
case WEBGL:
return element.getContext( 'webgl', options ) || element.getContext( 'experimental-webgl', options );
case DOM:
return element.canvas = element;
}
})();
( options.container || doc.body ).appendChild( element );
return Sketch.augment( context, options );
},
augment: function( context, options ) {
options = extend( options || {}, defaults );
options.element = context.canvas || context;
options.element.className += ' sketch';
extend( context, options, true );
return constructor( context );
}
};
/*
----------------------------------------------------------------------
Shims
----------------------------------------------------------------------
*/
var vendors = [ 'ms', 'moz', 'webkit', 'o' ];
var scope = self;
var then = 0;
var a = 'AnimationFrame';
var b = 'request' + a;
var c = 'cancel' + a;
var rAF = scope[ b ];
var cAF = scope[ c ];
for ( var i = 0; i < vendors.length && !rAF; i++ ) {
rAF = scope[ vendors[ i ] + 'Request' + a ];
cAF = scope[ vendors[ i ] + 'Cancel' + a ];
}
scope[ b ] = rAF = rAF || function( callback ) {
var now = +new Date();
var dt = M.max( 0, 16 - ( now - then ) );
var id = setTimeout( function() {
callback( now + dt );
}, dt );
then = now + dt;
return id;
};
scope[ c ] = cAF = cAF || function( id ) {
clearTimeout( id );
};
/*
----------------------------------------------------------------------
Output
----------------------------------------------------------------------
*/
return Sketch;
}));
</script>
<script>
// ----------------------------------------
// Particle
// ----------------------------------------
function Particle( x, y, radius ) {
this.init( x, y, radius );
}
Particle.prototype = {
init: function( x, y, radius ) {
this.alive = true;
this.radius = radius || 5;
this.wander = 0.15;
this.theta = random( TWO_PI );
this.drag = 0.5;
this.color = 'white';
this.x = x || 0.0;
this.y = y || 0.0;
this.vx = 0.0;
this.vy = 0.0;
},
move: function() {
this.x += this.vx;
this.y += this.vy;
this.vx *= this.drag;
this.vy *= this.drag;
this.theta += random( -0.5, 0.5 ) * this.wander;
this.vx += sin( this.theta ) * 0.1;
this.vy += cos( this.theta ) * 0.1;
this.radius *= 0.96;
this.alive = this.radius > 0.5;
},
draw: function( ctx ) {
ctx.beginPath();
ctx.arc( this.x, this.y, this.radius, 0, TWO_PI );
ctx.fillStyle = this.color;
ctx.fill();
}
};
// ----------------------------------------
// Example
// ----------------------------------------
var MAX_PARTICLES = 100;
var COLOURS = [ '#9AB9E8','#FFF2C0'];
var particles = [];
var pool = [];
var demo = Sketch.create({
container: document.getElementById( 'container' ),
retina: 'auto'
});
demo.setup = function() {
// Set off some initial particles.
var i, x, y;
for ( i = 30;i<30; i++ ) {
x = ( demo.width * 0.1 ) + random( -50, 50);
y = ( demo.height * 0.1 ) + random( -50, 50 );
demo.spawn( x, y );
}
};
demo.spawn = function( x, y ) {
var particle, theta, force;
if ( particles.length >= MAX_PARTICLES )
pool.push( particles.shift() );
particle = pool.length ? pool.pop() : new Particle();
particle.init( x, y, random( 5, 40 ) );
particle.wander = random( 10,10);
particle.color = random( COLOURS );
particle.drag = random( 0.1,0.2);
theta = random( TWO_PI );
force = random(5, 10);
particle.vx = sin( theta ) * force;
particle.vy = cos( theta ) * force;
particles.push( particle );
};
demo.update = function() {
var i, particle;
for ( i = particles.length - 1; i >= 0; i-- ) {
particle = particles[i];
if ( particle.alive ) particle.move();
else pool.push( particles.splice( i, 1 )[0] );
}
};
demo.draw = function() {
demo.globalCompositeOperation = 'bolder';
for ( var i = particles.length - 1; i >= 0; i-- ) {
particles[i].draw( demo );
}
};
demo.mousemove = function() {
var particle, theta, force, touch, max, i, j, n;
for ( i = 0, n = demo.touches.length; i < n; i++ ) {
touch = demo.touches[i], max = random( 1, 4 );
for ( j = 0; j < max; j++ ) {
demo.spawn( touch.x, touch.y );
}
}
};
</script>
</body>
</html>