|
#------------------------------------------------------- |
|
# CONFIGURATION - Feel free to mess with these values |
|
#------------------------------------------------------- |
|
|
|
config = |
|
#RENDERING SETTINGS |
|
triangles: 30 # number of triangles to render |
|
speed: 2 # speed of animation ... approximately pixels per update |
|
|
|
#TRIANGLE COLORS |
|
redMin: 0 # minimum red value (0 - 255) |
|
redMax: 255 # maximum red value |
|
greenMin: 0 # minimum green value |
|
greenMax: 32 # maximum green value |
|
blueMin: 0 # minimum blue value |
|
blueMax: 32 # maximum blue value |
|
opacityMin: .05 # minimum opacity (0 - 1) |
|
opacityMax: .2 # maximum opacity |
|
|
|
# Clamps a numeric value between a minimum and maximum (inclusively) |
|
clamp = (value, min, max) -> Math.min(Math.max(value, min), max) |
|
|
|
# Gets a random number between min (inclusive) and max (exclusive) |
|
rand = (min = 0, max = 1) -> Math.random() * (max - min) + min |
|
|
|
# requestAnimationFrame compatibility/fallback wrapper |
|
# a la Paul Irish: http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ |
|
window.requestAnimFrame = |
|
window.requestAnimationFrame || |
|
window.webkitRequestAnimationFrame || |
|
window.mozRequestAnimationFrame || |
|
window.oRequestAnimationFrame || |
|
window.msRequestAnimationFrame || |
|
(callback, element) -> window.setTimeout callback, 1000 / 60 |
|
|
|
|
|
#------------------------------------------------------- |
|
# VECTOR - A point on the canvas or a bearing |
|
#------------------------------------------------------- |
|
|
|
class Vector |
|
x: 0 |
|
y: 0 |
|
|
|
constructor: (@x = 0, @y = 0) -> |
|
|
|
# Generates a random vector within the bounds |
|
@random: (bounds) => |
|
x = rand bounds.minX(), bounds.maxX() |
|
y = rand bounds.minY(), bounds.maxY() |
|
new @ x, y |
|
|
|
# Detects whether or not this vertex is out of the bounds |
|
outOfBounds: (bounds) -> |
|
bounds.minX() >= @x || |
|
bounds.maxX() <= @x || |
|
bounds.minY() >= @y || |
|
bounds.maxY() <= @y |
|
|
|
# Clamps the vector to a bounds |
|
clamp: (bounds) -> |
|
@x = clamp @x, bounds.minX(), bounds.maxX() |
|
@y = clamp @y, bounds.minY(), bounds.maxY() |
|
@ |
|
|
|
# Adds another vector to this one |
|
addVector: (vector) -> |
|
@x += vector.x |
|
@y += vector.y |
|
@ |
|
|
|
# performs a scalar product against the vector |
|
scalarProduct: (scalar) -> |
|
@x *= scalar |
|
@y *= scalar |
|
@ |
|
|
|
# clamps and reflects a vector if it's out of bounds |
|
reflectAndClamp: (bounds, bearing) -> |
|
if not @outOfBounds bounds then return @ |
|
if @x <= bounds.minX() or @x >= bounds.maxX() then bearing.x *= -1 |
|
if @y <= bounds.minY() or @y >= bounds.maxY() then bearing.y *= -1 |
|
@clamp(bounds) |
|
@ |
|
|
|
|
|
#------------------------------------------------------- |
|
# BOUND - A rectangular boundary for drawing |
|
#------------------------------------------------------- |
|
|
|
class Bounds |
|
topLeft: undefined |
|
bottomRight: undefined |
|
|
|
constructor: (@topLeft = new Vector, @bottomRight = new Vector) -> |
|
window.bounds = @ |
|
|
|
minX: -> @topLeft.x |
|
maxX: -> @bottomRight.x |
|
|
|
minY: -> @topLeft.y |
|
maxY: -> @bottomRight.y |
|
|
|
|
|
#------------------------------------------------------- |
|
# TRIANGLE - Polygon to be drawn onto the canvas |
|
#------------------------------------------------------- |
|
|
|
class Triangle |
|
red: undefined |
|
green: undefined |
|
blue: undefined |
|
opacity: undefined |
|
|
|
bounds: undefined |
|
vertices: undefined |
|
bearings: undefined |
|
|
|
bearingBounds: new Bounds(new Vector(-1, -1), new Vector(1, 1)) |
|
|
|
# creates a random triangle within the bounds |
|
constructor: (@bounds = new Bounds) -> |
|
@red = Math.floor rand config.redMin, config.redMax |
|
@green = Math.floor rand config.greenMin, config.greenMax |
|
@blue = Math.floor rand config.blueMin, config.blueMax |
|
@opacity = rand config.opacityMin, config.opacityMax |
|
|
|
@vertices = [] |
|
@bearings = [] |
|
|
|
for [0...3] |
|
@vertices.push Vector.random(@bounds) |
|
@bearings.push Vector.random(@bearingBounds).scalarProduct config.speed |
|
|
|
# updates the position of each of the triangle vertices by its bearing |
|
update: -> |
|
for i in [0...3] |
|
vertex = @vertices[i] |
|
bearing = @bearings[i] |
|
vertex.addVector bearing |
|
vertex.reflectAndClamp @bounds, bearing |
|
|
|
# renders the triangle in the canvas context |
|
# includes an optional partialStep for extrapolating lag position |
|
render: (context, partialStep = 0) -> |
|
points = [] |
|
for i in [0...3] |
|
bearing = @bearings[i] |
|
vertex = @vertices[i] |
|
points.push |
|
x: parseInt(vertex.x + partialStep * bearing.x) |
|
y: parseInt(vertex.y + partialStep * bearing.y) |
|
|
|
context.fillStyle = "rgba(#{@red},#{@green},#{@blue},#{@opacity})" |
|
context.beginPath() |
|
context.moveTo points[0].x, points[0].y |
|
context.lineTo(points[i].x, points[i].y) for i in [2..0] |
|
context.closePath() |
|
context.fill() |
|
@ |
|
|
|
|
|
#------------------------------------------------------- |
|
# CANVAS - The animation logic and wrapper for the canvas |
|
#------------------------------------------------------- |
|
|
|
class Canvas |
|
updateStep: 1000/60 |
|
el: undefined |
|
context: undefined |
|
triangles: undefined |
|
previous: undefined |
|
lag: undefined |
|
|
|
constructor: (@el) -> |
|
@context = @el.getContext('2d') |
|
@triangles = [] |
|
@previous = new Date().getTime() |
|
@lag = 0.0 |
|
|
|
# add a Triangle to the stack to be animated |
|
addTriangle: (triangle) -> @triangles.push triangle |
|
|
|
# updates all the triangles on the stack |
|
update: -> |
|
triangle.update() for triangle in @triangles |
|
return |
|
|
|
# renders all the triangles on the stack |
|
render: (partial) -> |
|
@context.clearRect 0, 0, @el.width, @el.height |
|
triangle.render(@context, partial) for triangle in @triangles |
|
return |
|
|
|
# run at each animation frame |
|
# inspired by Bob Nystrom: http://gameprogrammingpatterns.com/ |
|
loop: -> |
|
current = new Date().getTime() |
|
@lag += current - @previous |
|
@previous = current |
|
|
|
while @lag >= @updateStep |
|
@update() |
|
@lag -= @updateStep |
|
|
|
@render @lag / @updateStep |
|
|
|
# start the animation loop using requestAnimationFrame wrapper |
|
start: -> |
|
window.requestAnimFrame => @start() |
|
@loop() |
|
|
|
|
|
#------------------------------------------------------- |
|
# THE MAGIC |
|
#------------------------------------------------------- |
|
|
|
# grab the canvas element |
|
el = document.getElementById 'c' |
|
$el = $(el) |
|
|
|
# gimme that full screen |
|
el.width = $el.innerWidth() |
|
el.height = $el.innerHeight() |
|
|
|
console.log el.width |
|
console.log el.height |
|
|
|
# get the vectors to define the bounds |
|
# giving a bit of wiggle room for overflow (makes for a prettier pattern) |
|
topLeft = new Vector() |
|
topLeft.x -= el.width * .25 |
|
topLeft.y -= el.height * .25 |
|
|
|
bottomRight = new Vector el.width, el.height |
|
bottomRight.x += el.width * .25 |
|
bottomRight.y += el.height * .25 |
|
|
|
# make the bounds for rendering the triangles |
|
bounds = new Bounds topLeft, bottomRight |
|
|
|
# create the canvas object with the canvas element |
|
canvas = new Canvas el |
|
|
|
# add in the triangles |
|
canvas.addTriangle new Triangle bounds for [0...config.triangles] |
|
|
|
# profit! |
|
canvas.start() |