A Pen By
Chris Roche

` ````
<canvas id="c"></canvas>
```

` ````
@import "compass/css3";
body {
background: #efefef;
overflow: hidden;
}
#c {
width: 100%;
height: 1080px;
}
```

` ````
#-------------------------------------------------------
# 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: 83 # maximum red value
greenMin: 0 # minimum green value
greenMax: 100 # maximum green value
blueMin: 100 # minimum blue value
blueMax: 180 # 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()
# 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()
```

999px

Loading
..................

Alt F
Opt F
Find & Replace

Also see: Tab Triggers