Skip to content

Instantly share code, notes, and snippets.

@pketh
Last active September 1, 2016 19:23
Show Gist options
  • Save pketh/c6a016b8582beb66b079e36878bef1d7 to your computer and use it in GitHub Desktop.
Save pketh/c6a016b8582beb66b079e36878bef1d7 to your computer and use it in GitHub Desktop.
canvas success confetti
confetti: ->
# ported from http://codepen.io/iprodev/pen/azpWBr
PI = Math.PI
sqrt = Math.sqrt
round = Math.round
random = Math.random
cos = Math.cos
sin = Math.sin
rAF = window.requestAnimationFrame
cAF = window.cancelAnimationFrame or window.cancelRequestAnimationFrame
_now = Date.now
document.addEventListener 'DOMContentLoaded', ->
speed = 50
duration = 1.0 / speed
confettiRibbonCount = 11
ribbonPaperCount = 30
ribbonPaperDist = 8.0
ribbonPaperThick = 8.0
confettiPaperCount = 95
DEG_TO_RAD = PI / 180
RAD_TO_DEG = 180 / PI
colors = [
[
'#df0049'
'#660671'
]
[
'#00e857'
'#005291'
]
[
'#2bebbc'
'#05798a'
]
[
'#ffd200'
'#b06c00'
]
]
Vector2 = (_x, _y) ->
@x = _x
@y = _y
@Length = ->
sqrt @SqrLength()
@SqrLength = ->
@x * @x + @y * @y
@Add = (_vec) ->
@x += _vec.x
@y += _vec.y
return
@Sub = (_vec) ->
@x -= _vec.x
@y -= _vec.y
return
@Div = (_f) ->
@x /= _f
@y /= _f
return
@Mul = (_f) ->
@x *= _f
@y *= _f
return
@Normalize = ->
sqrLen = @SqrLength()
if sqrLen != 0
factor = 1.0 / sqrt(sqrLen)
@x *= factor
@y *= factor
return
@Normalized = ->
sqrLen = @SqrLength()
if sqrLen != 0
factor = 1.0 / sqrt(sqrLen)
return new Vector2(@x * factor, @y * factor)
new Vector2(0, 0)
return
EulerMass = (_x, _y, _mass, _drag) ->
@position = new Vector2(_x, _y)
@mass = _mass
@drag = _drag
@force = new Vector2(0, 0)
@velocity = new Vector2(0, 0)
@AddForce = (_f) ->
@force.Add _f
return
@Integrate = (_dt) ->
acc = @CurrentForce(@position)
acc.Div @mass
posDelta = new Vector2(@velocity.x, @velocity.y)
posDelta.Mul _dt
@position.Add posDelta
acc.Mul _dt
@velocity.Add acc
@force = new Vector2(0, 0)
return
@CurrentForce = (_pos, _vel) ->
`var speed`
totalForce = new Vector2(@force.x, @force.y)
speed = @velocity.Length()
dragVel = new Vector2(@velocity.x, @velocity.y)
dragVel.Mul @drag * @mass * speed
totalForce.Sub dragVel
totalForce
return
ConfettiPaper = (_x, _y) ->
@pos = new Vector2(_x, _y)
@rotationSpeed = random() * 600 + 800
@angle = DEG_TO_RAD * random() * 360
@rotation = DEG_TO_RAD * random() * 360
@cosA = 1.0
@size = 5.0
@oscillationSpeed = random() * 1.5 + 0.5
@xSpeed = 40.0
@ySpeed = random() * 60 + 50.0
@corners = new Array
@time = random()
ci = round(random() * (colors.length - 1))
@frontColor = colors[ci][0]
@backColor = colors[ci][1]
i = 0
while i < 4
dx = cos(@angle + DEG_TO_RAD * (i * 90 + 45))
dy = sin(@angle + DEG_TO_RAD * (i * 90 + 45))
@corners[i] = new Vector2(dx, dy)
i++
@Update = (_dt) ->
@time += _dt
@rotation += @rotationSpeed * _dt
@cosA = cos(DEG_TO_RAD * @rotation)
@pos.x += cos(@time * @oscillationSpeed) * @xSpeed * _dt
@pos.y += @ySpeed * _dt
if @pos.y > ConfettiPaper.bounds.y
@pos.x = random() * ConfettiPaper.bounds.x
@pos.y = 0
return
@Draw = (_g) ->
`var i`
if @cosA > 0
_g.fillStyle = @frontColor
else
_g.fillStyle = @backColor
_g.beginPath()
_g.moveTo (@pos.x + @corners[0].x * @size), (@pos.y + @corners[0].y * @size * @cosA)
i = 1
while i < 4
_g.lineTo (@pos.x + @corners[i].x * @size), (@pos.y + @corners[i].y * @size * @cosA)
i++
_g.closePath()
_g.fill()
return
return
ConfettiRibbon = (_x, _y, _count, _dist, _thickness, _angle, _mass, _drag) ->
@particleDist = _dist
@particleCount = _count
@particleMass = _mass
@particleDrag = _drag
@particles = new Array
ci = round(random() * (colors.length - 1))
@frontColor = colors[ci][0]
@backColor = colors[ci][1]
@xOff = cos(DEG_TO_RAD * _angle) * _thickness
@yOff = sin(DEG_TO_RAD * _angle) * _thickness
@position = new Vector2(_x, _y)
@prevPosition = new Vector2(_x, _y)
@velocityInherit = random() * 2 + 4
@time = random() * 100
@oscillationSpeed = random() * 2 + 2
@oscillationDistance = random() * 40 + 40
@ySpeed = random() * 40 + 80
i = 0
while i < @particleCount
@particles[i] = new EulerMass(_x, _y - (i * @particleDist), @particleMass, @particleDrag)
i++
@Update = (_dt) ->
`var i`
i = 0
@time += _dt * @oscillationSpeed
@position.y += @ySpeed * _dt
@position.x += cos(@time) * @oscillationDistance * _dt
@particles[0].position = @position
dX = @prevPosition.x - (@position.x)
dY = @prevPosition.y - (@position.y)
delta = sqrt(dX * dX + dY * dY)
@prevPosition = new Vector2(@position.x, @position.y)
i = 1
while i < @particleCount
dirP = Vector2.Sub(@particles[i - 1].position, @particles[i].position)
dirP.Normalize()
dirP.Mul delta / _dt * @velocityInherit
@particles[i].AddForce dirP
i++
i = 1
while i < @particleCount
@particles[i].Integrate _dt
i++
i = 1
while i < @particleCount
rp2 = new Vector2(@particles[i].position.x, @particles[i].position.y)
rp2.Sub @particles[i - 1].position
rp2.Normalize()
rp2.Mul @particleDist
rp2.Add @particles[i - 1].position
@particles[i].position = rp2
i++
if @position.y > ConfettiRibbon.bounds.y + @particleDist * @particleCount
@Reset()
return
@Reset = ->
`var ci`
`var i`
@position.y = -random() * ConfettiRibbon.bounds.y
@position.x = random() * ConfettiRibbon.bounds.x
@prevPosition = new Vector2(@position.x, @position.y)
@velocityInherit = random() * 2 + 4
@time = random() * 100
@oscillationSpeed = random() * 2.0 + 1.5
@oscillationDistance = random() * 40 + 40
@ySpeed = random() * 40 + 80
ci = round(random() * (colors.length - 1))
@frontColor = colors[ci][0]
@backColor = colors[ci][1]
@particles = new Array
i = 0
while i < @particleCount
@particles[i] = new EulerMass(@position.x, @position.y - (i * @particleDist), @particleMass, @particleDrag)
i++
return
@Draw = (_g) ->
`var i`
i = 0
while i < @particleCount - 1
p0 = new Vector2(@particles[i].position.x + @xOff, @particles[i].position.y + @yOff)
p1 = new Vector2(@particles[i + 1].position.x + @xOff, @particles[i + 1].position.y + @yOff)
if @Side(@particles[i].position.x, @particles[i].position.y, @particles[i + 1].position.x, @particles[i + 1].position.y, p1.x, p1.y) < 0
_g.fillStyle = @frontColor
_g.strokeStyle = @frontColor
else
_g.fillStyle = @backColor
_g.strokeStyle = @backColor
if i == 0
_g.beginPath()
_g.moveTo @particles[i].position.x, @particles[i].position.y
_g.lineTo @particles[i + 1].position.x, @particles[i + 1].position.y
_g.lineTo (@particles[i + 1].position.x + p1.x) * 0.5, (@particles[i + 1].position.y + p1.y) * 0.5
_g.closePath()
_g.stroke()
_g.fill()
_g.beginPath()
_g.moveTo p1.x, p1.y
_g.lineTo p0.x, p0.y
_g.lineTo (@particles[i + 1].position.x + p1.x) * 0.5, (@particles[i + 1].position.y + p1.y) * 0.5
_g.closePath()
_g.stroke()
_g.fill()
else if i == @particleCount - 2
_g.beginPath()
_g.moveTo @particles[i].position.x, @particles[i].position.y
_g.lineTo @particles[i + 1].position.x, @particles[i + 1].position.y
_g.lineTo (@particles[i].position.x + p0.x) * 0.5, (@particles[i].position.y + p0.y) * 0.5
_g.closePath()
_g.stroke()
_g.fill()
_g.beginPath()
_g.moveTo p1.x, p1.y
_g.lineTo p0.x, p0.y
_g.lineTo (@particles[i].position.x + p0.x) * 0.5, (@particles[i].position.y + p0.y) * 0.5
_g.closePath()
_g.stroke()
_g.fill()
else
_g.beginPath()
_g.moveTo @particles[i].position.x, @particles[i].position.y
_g.lineTo @particles[i + 1].position.x, @particles[i + 1].position.y
_g.lineTo p1.x, p1.y
_g.lineTo p0.x, p0.y
_g.closePath()
_g.stroke()
_g.fill()
i++
return
@Side = (x1, y1, x2, y2, x3, y3) ->
(x1 - x2) * (y3 - y2) - ((y1 - y2) * (x3 - x2))
return
Vector2.Lerp = (_vec0, _vec1, _t) ->
new Vector2((_vec1.x - (_vec0.x)) * _t + _vec0.x, (_vec1.y - (_vec0.y)) * _t + _vec0.y)
Vector2.Distance = (_vec0, _vec1) ->
sqrt Vector2.SqrDistance(_vec0, _vec1)
Vector2.SqrDistance = (_vec0, _vec1) ->
x = _vec0.x - (_vec1.x)
y = _vec0.y - (_vec1.y)
x * x + y * y + z * z
Vector2.Scale = (_vec0, _vec1) ->
new Vector2(_vec0.x * _vec1.x, _vec0.y * _vec1.y)
Vector2.Min = (_vec0, _vec1) ->
new Vector2(Math.min(_vec0.x, _vec1.x), Math.min(_vec0.y, _vec1.y))
Vector2.Max = (_vec0, _vec1) ->
new Vector2(Math.max(_vec0.x, _vec1.x), Math.max(_vec0.y, _vec1.y))
Vector2.ClampMagnitude = (_vec0, _len) ->
vecNorm = _vec0.Normalized
new Vector2(vecNorm.x * _len, vecNorm.y * _len)
Vector2.Sub = (_vec0, _vec1) ->
new Vector2(_vec0.x - (_vec1.x), _vec0.y - (_vec1.y), _vec0.z - (_vec1.z))
ConfettiPaper.bounds = new Vector2(0, 0)
ConfettiRibbon.bounds = new Vector2(0, 0)
confetti = {}
confetti.Context = (id) ->
i = 0
canvas = document.getElementById(id)
canvasParent = document.getElementById 'application'
canvasWidth = canvasParent.offsetWidth
canvasHeight = canvasParent.offsetHeight
canvas.width = canvasWidth
canvas.height = canvasHeight
context = canvas.getContext('2d')
interval = null
confettiRibbons = new Array
ConfettiRibbon.bounds = new Vector2(canvasWidth, canvasHeight)
i = 0
while i < confettiRibbonCount
confettiRibbons[i] = new ConfettiRibbon(random() * canvasWidth, -random() * canvasHeight * 2, ribbonPaperCount, ribbonPaperDist, ribbonPaperThick, 45, 1, 0.05)
i++
confettiPapers = new Array
ConfettiPaper.bounds = new Vector2(canvasWidth, canvasHeight)
i = 0
while i < confettiPaperCount
confettiPapers[i] = new ConfettiPaper(random() * canvasWidth, random() * canvasHeight)
i++
@resize = ->
canvasWidth = canvasParent.offsetWidth
canvasHeight = canvasParent.offsetHeight
canvas.width = canvasWidth
canvas.height = canvasHeight
ConfettiPaper.bounds = new Vector2(canvasWidth, canvasHeight)
ConfettiRibbon.bounds = new Vector2(canvasWidth, canvasHeight)
return
@start = ->
`var context`
@stop()
context = this
@update()
return
@stop = ->
cAF @interval
return
@update = ->
`var i`
i = 0
context.clearRect 0, 0, canvas.width, canvas.height
i = 0
while i < confettiPaperCount
confettiPapers[i].Update duration
confettiPapers[i].Draw context
i++
i = 0
while i < confettiRibbonCount
confettiRibbons[i].Update duration
confettiRibbons[i].Draw context
i++
@interval = rAF(->
confetti.update()
return
)
return
return
confetti = new (confetti.Context)('confetti')
confetti.start()
window.addEventListener 'resize', (event) ->
confetti.resize()
return
return
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment