Last active
September 1, 2016 19:23
-
-
Save pketh/c6a016b8582beb66b079e36878bef1d7 to your computer and use it in GitHub Desktop.
canvas success confetti
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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