Permalink
Switch branches/tags
Nothing to show
Find file
d1f4b43 Feb 20, 2017
239 lines (201 sloc) 5.99 KB
import React from 'react';
import { css } from 'glamor';
import { TweenMax, Power4 } from 'gsap';
import { primaryHSL, secondaryHSL } from '../../style/color';
import * as physics2D from '../../vendor/physics-2d-plugin';
import {
random as loRandom,
uniqueId as loUniqueId,
pull as loPull,
debounce,
} from 'lodash';
const canvasContainer = css({
position: 'fixed',
top: 0,
left: 0,
width: '100%',
height: '100%',
zIndex: -1,
});
const canvasStyles = css({
width: '100%',
height: '100%',
});
const DECAY = 3.5;
const SPREAD = 50;
const GRAVITY = 1200;
export class Confetti extends React.Component {
constructor() {
super();
this.angle = 270;
this.confettiSpriteIds = [];
this.confettiSprites = {};
this.addConfettiParticles = this.addConfettiParticles.bind(this);
this.updateConfettiParticle = this.updateConfettiParticle.bind(this);
this.tweenConfettiParticle = this.tweenConfettiParticle.bind(this);
this.shootConfetti = this.shootConfetti.bind(this);
this.drawConfetti = this.drawConfetti.bind(this);
this.renderCanvas = this.renderCanvas.bind(this);
this.handleMousedown = this.handleMousedown.bind(this);
this.handleMouseup = this.handleMouseup.bind(this);
this.setCanvasSize = this.setCanvasSize.bind(this);
}
shouldComponentUpdate(nextProps) {
if (this.props.shoot !== nextProps.shoot) {
const canvas = this.refs.canvas;
this.shoot = true;
this.shootConfetti();
setTimeout(() => {
this.shoot = false;
const finalSlide = document.getElementById('final-slide');
canvas.addEventListener('mousedown', this.handleMousedown);
canvas.addEventListener('mouseup', this.handleMouseup);
canvas.addEventListener('touchstart', this.handleMousedown);
canvas.addEventListener('touchend', this.handleMouseup);
finalSlide.addEventListener('mousedown', this.handleMousedown);
finalSlide.addEventListener('mouseup', this.handleMouseup);
finalSlide.addEventListener('touchstart', this.handleMousedown);
finalSlide.addEventListener('touchend', this.handleMouseup);
}, 2000);
return true;
}
return false;
}
componentDidMount() {
const canvas = this.refs.canvas;
this.dpr = window.devicePixelRatio || 1;
this.ctx = canvas.getContext('2d');
this.ctx.scale(this.dpr, this.dpr);
TweenMax.ticker.addEventListener('tick', this.renderCanvas);
window.addEventListener('resize', debounce(this.setCanvasSize, 200));
this.setCanvasSize();
}
handleMousedown() {
this.shoot = true;
}
handleMouseup() {
this.shoot = false;
}
setCanvasSize() {
const canvas = this.refs.canvas;
canvas.width = window.innerWidth * this.dpr;
canvas.height = window.innerHeight * this.dpr;
}
addConfettiParticles(amount, angle, velocity, canvasW, canvasH) {
let i = 0;
let evenPurple = true;
let oddPurple = false;
while (i < amount) {
// sprite
const isEven = i % 2 === 0
const r = loRandom(4, 6) * this.dpr;
const d = loRandom(15, 25) * this.dpr;
let colorBase = {};
if (isEven) {
colorBase = evenPurple ? primaryHSL : secondaryHSL;
evenPurple = !evenPurple;
} else {
colorBase = oddPurple ? primaryHSL : secondaryHSL;
oddPurple = !oddPurple;
}
const l = loRandom(colorBase.l, colorBase.l*0.9).toFixed(2);
const color = `hsl(${colorBase.h}, ${colorBase.s*100}%, ${l*100}%)`;
const tilt = loRandom(10, -10);
const tiltAngleIncremental = loRandom(0.07, 0.05);
const tiltAngle = 0;
const id = loUniqueId();
const sprite = {
[id]: {
// angle: isEven ? (angle + 20) : (angle - 20),
angle: isEven ? (angle + 30) : (angle - 30),
velocity,
// x: isEven ? canvasW * .1 : canvasW * .9,
x: isEven ? 0 : canvasW,
// y: canvasH * .9,
y: canvasH,
r,
d,
color,
tilt,
tiltAngleIncremental,
tiltAngle,
},
};
Object.assign(this.confettiSprites, sprite);
this.confettiSpriteIds.push(id);
this.tweenConfettiParticle(id);
i++;
}
}
tweenConfettiParticle(id) {
const minAngle = this.confettiSprites[id].angle - SPREAD / 2;
const maxAngle = this.confettiSprites[id].angle + SPREAD / 2;
const minVelocity = this.confettiSprites[id].velocity / 4;
const maxVelocity = this.confettiSprites[id].velocity;
// Physics Props
const velocity = loRandom(minVelocity, maxVelocity);
const angle = loRandom(minAngle, maxAngle);
const gravity = GRAVITY;
// const friction = loRandom(0.1, 0.25);
const friction = loRandom(0.01, 0.05);
const d = 0;
TweenMax.to(this.confettiSprites[id], DECAY, {
physics2D: {
velocity,
angle,
gravity,
friction,
},
d,
ease: Power4.easeIn,
onComplete: () => {
// remove confetti sprite and id
loPull(this.confettiSpriteIds, id);
delete this.confettiSprites[id];
},
});
}
updateConfettiParticle(id) {
const sprite = this.confettiSprites[id];
const tiltAngle = 0.0005 * sprite.d;
sprite.angle += 0.01;
sprite.tiltAngle += tiltAngle;
sprite.tiltAngle += sprite.tiltAngleIncremental;
sprite.tilt = (Math.sin(sprite.tiltAngle - (sprite.r / 2))) * sprite.r * 2;
sprite.y += Math.sin(sprite.angle + sprite.r / 2) * 2;
sprite.x += Math.cos(sprite.angle) / 2;
}
drawConfetti() {
this.confettiSpriteIds.forEach(id => {
const sprite = this.confettiSprites[id];
this.ctx.beginPath();
this.ctx.lineWidth = sprite.d / 2;
this.ctx.strokeStyle = sprite.color;
this.ctx.moveTo(sprite.x + sprite.tilt + sprite.r, sprite.y);
this.ctx.lineTo(sprite.x + sprite.tilt, sprite.y + sprite.tilt + sprite.r);
this.ctx.stroke();
this.updateConfettiParticle(id);
});
}
renderCanvas() {
const canvas = this.refs.canvas;
this.ctx.clearRect(0, 0, canvas.width, canvas.height);
this.drawConfetti();
}
shootConfetti() {
const canvas = this.refs.canvas;
requestAnimationFrame(this.shootConfetti);
if (this.shoot) {
this.addConfettiParticles(10, 270, 5000, canvas.width, canvas.height);
}
}
render() {
return (
<div {...canvasContainer}>
<canvas {...canvasStyles}
ref="canvas"
></canvas>
</div>
)
}
}