<div id="canvas-container-div">
<canvas id="canvas"></canvas>
</div>
<div id="restart-button-div">
<img id="restart-icon" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/409445/restart.svg">
</div>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/409445/jquery-3.4.1.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/409445/verletExpress.js"></script>
body {
margin: 0;
}
#canvas-container-div {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
width: 100%;
height: 100%;
}
#canvas {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
width: 100%;
height: 100%;
background: #000000;
}
#restart-button-div {
position: absolute;
bottom: 25px;
right: 25px;
width: 50px;
height: 50Px;
background: white;
opacity: 0.7;
cursor: pointer;
border-radius: 100%;
}
#restart-icon {
position: absolute;
top: 50%;
left: 47%;
transform: translate( -50%, -50% );
width: 35px;
height: 45px;
}
/////--- Initialization ---/////
$( window ).on( "load", ()=> {
///Elements
var canvas = document.getElementById("canvas");
var canvasWidth = canvas.clientWidth;
var canvasHeight = canvas.clientHeight;
var restartButton = document.getElementById("restart-button-div")
///VerletExpressJS
VX.initialize( "2d", "canvas", "canvas", canvasWidth, canvasHeight );
VX.xRange = { min: null, max: null };
VX.yRange = { min: -VX.interfaceHeight*0.2, max: null };
VX.breeze = 1;
///Settings
var beginWithBurst = true;
var confettis = []; var confettiCount = 0;
var confettiVolumeInit = 150;
var confettiVolumeMax = confettiVolumeInit+25;
var confettiWidthMin = VX.interfaceHeight*0.01;
var confettiWidthMax = VX.interfaceHeight*0.015;
var confettiWidth = function() { return VX.rfb( confettiWidthMin, confettiWidthMax ); };
var confettiInitX = function() { return VX.rfb( 0, VX.interfaceWidth ) }; // initial confetti top left corner x position
var confettiInitY = function() { return VX.rfb( -confettiWidthMax, VX.interfaceHeight ) }; // initial confetti top left corner y position
var confettiVelocityBase = confettiWidthMax*0.5;
var initialBurstIntensity = confettiWidthMax*5;
var refreshed = false;
///colors
var backgroundColor = "#111111";
var confettiAlpha = 1; // confetti alpha
var janeStreetPalette = [ `rgba( 32, 133, 239, ${confettiAlpha} )`,
`rgba( 0, 194, 143, ${confettiAlpha} )`,
`rgba( 129, 208, 4, ${confettiAlpha} )`,
`rgba( 86, 171, 15, ${confettiAlpha} )` ];
var pastelPartyPalette = [ `rgba( 168, 100, 253, ${confettiAlpha} )`,
`rgba( 41, 205, 255, ${confettiAlpha} )`,
`rgba( 120, 255, 68, ${confettiAlpha} )`,
`rgba( 253, 255, 106, ${confettiAlpha} )`,
`rgba( 255, 113, 141, ${confettiAlpha} )` ];
var tropicalSunsetPalette = [ `rgba( 254,218,132, ${confettiAlpha} )`,
`rgba( 255,155,131, ${confettiAlpha} )`,
`rgba( 151,99,147, ${confettiAlpha} )`,
`rgba( 104,84,137, ${confettiAlpha} )`,
`rgba( 67,69,127, ${confettiAlpha} )` ];
var goldPalette = [ `rgba( 166,124,0, ${confettiAlpha} )`,
`rgba( 191,155,48, ${confettiAlpha} )`,
`rgba( 255,191,0, ${confettiAlpha} )`,
`rgba( 255,207,64, ${confettiAlpha} )`,
`rgba( 255,220,115, ${confettiAlpha} )` ];
var randomPalette = generateRandomPalette(5);
document.getElementById("canvas").style.background = backgroundColor;
var colors = randomPalette;
///Interaction
var mouseCoords = { cx: null, cy: null, px: null, py: null };
var mouseZoneRadius = confettiWidthMax*5;
var mouseStrength = 0.1;
var popperBurstVolumeMax = 50;
var popperBurstIntensity = confettiWidthMax*2;
var popperBurstZoneRadius = confettiWidthMax*10;
/////--- Functions ---/////
function generateRandomPalette( colorCount ) {
var palette = [];
for ( var i=0; i<colorCount; i++ ) {
palette.push( `rgba( ${VX.rib(0,255)}, ${VX.rib(0,255)}, ${VX.rib(0,255)}, ${confettiAlpha} )` );
}
return palette
}
function Confetti( initX, initY ) {
confettiCount++;
this.id = confettiCount;
this.width = confettiWidth();
this.color = colors[ VX.rib( 0, colors.length-1 ) ];
this.pt1 = VX.addPoint( { x: initX, y: initY } );
this.pt1.px += VX.rfb( -confettiVelocityBase/2, confettiVelocityBase/2 ); // adds initialx velocity
this.pt1.py += VX.rfb( -confettiVelocityBase, 0 ); // adds initial y velocity
this.pt2 = VX.addPoint( { x: initX+this.width, y: initY } );
this.pt3 = VX.addPoint( { x: initX+this.width, y: initY+this.width } );
this.pt4 = VX.addPoint( { x: initX, y: initY+this.width } );
this.sp1 = VX.addSpan( this.pt1, this.pt2 );
this.sp2 = VX.addSpan( this.pt2, this.pt3 );
this.sp3 = VX.addSpan( this.pt3, this.pt4 );
this.sp4 = VX.addSpan( this.pt4, this.pt1 );
this.sp5 = VX.addSpan( this.pt1, this.pt3 );
this.sk = VX.addSkin( [ this.pt1, this.pt2, this.pt3, this.pt4 ], { fillColor: this.color, outlineColor: "rgba(0,0,0,0)", outlineThickness: 0 } );
}
function runInitialBurst() {
for ( var i=0; i<confettiVolumeInit; i++ ) {
var initAngle = VX.rfb(0,Math.PI*2);
var initDistance = VX.rfb( 0, VX.interfaceWidth*0.1 );
var initConfettiX = VX.interfaceWidth*0.5 + Math.cos( initAngle - Math.PI/2 ) * initDistance;
var initConfettiY = VX.interfaceHeight*0.6 + Math.sin( initAngle - Math.PI/2 ) * initDistance;
confettis.push( new Confetti( initConfettiX, initConfettiY ) );
var velocity = VX.rfb( -initialBurstIntensity, initialBurstIntensity );
var velocityAngle = VX.rfb( 0, Math.PI*2 );
confettis[confettis.length-1].pt1.px = initConfettiX + Math.cos( velocityAngle-Math.PI/2 ) * velocity;
confettis[confettis.length-1].pt1.py = initConfettiY + Math.sin( velocityAngle-Math.PI/2 ) * velocity + VX.interfaceWidth*0.01;
}
}
function runInitialFall() {
for ( var i=0; i<confettiVolumeInit; i++ ) {
confettis.push( new Confetti( confettiInitX(), confettiInitY() ) );
}
confettiInitY = function() { return -confettiWidthMax*3; };
}
function beginConfetti() {
beginWithBurst ? runInitialBurst() : runInitialFall();
confettiInitY = ()=> { return -confettiWidthMax*3; };
setTimeout( ()=> { refreshed = false }, 1 );
}
function removeConfettiById( id ) {
for ( var i=0; i<confettis.length; i++ ){
var c = confettis[i];
if ( c.id == id ) {
VX.removePoint( c.pt1.id ); VX.removePoint( c.pt2.id ); VX.removePoint( c.pt3.id ); VX.removePoint( c.pt4.id );
VX.removeSpan( c.sp1.id ); VX.removeSpan( c.sp2.id ); VX.removeSpan( c.sp3.id ); VX.removeSpan( c.sp4.id ); VX.removeSpan( c.sp5.id );
VX.removeSkin( c.sk.id );
confettis.splice(i,1);
}
}
}
function removeConfettiByIndex( index ) {
var c = confettis[index];
VX.removePoint( c.pt1.id ); VX.removePoint( c.pt2.id ); VX.removePoint( c.pt3.id ); VX.removePoint( c.pt4.id );
VX.removeSpan( c.sp1.id ); VX.removeSpan( c.sp2.id ); VX.removeSpan( c.sp3.id ); VX.removeSpan( c.sp4.id );
VX.removeSpan( c.sp5.id );
VX.removeSkin( c.sk.id );
confettis.splice( index, 1 );
}
function replaceFallenConfettis() {
for ( var i=0; i<confettis.length; i++ ) {
var c = confettis[i];
if ( c.pt1.cy > VX.interfaceHeight*1.1) {
removeConfettiById( c.id );
if ( confettis.length < confettiVolumeInit ) {
confettis.push( new Confetti( confettiInitX(), confettiInitY() ) );
}
}
}
}
function updateMouseCurrentCoords( event ) {
mouseCoords.cx = event.clientX;
mouseCoords.cy = event.clientY;
}
function updateMousePreviousCoords() {
mouseCoords.px = mouseCoords.cx;
mouseCoords.py = mouseCoords.cy;
}
function moveConfettisOnMouseOver() {
for ( var i=0; i<confettis.length; i++ ) {
var c = confettis[i];
var xDiff = Math.abs( mouseCoords.cx - c.pt1.cx );
var yDiff = Math.abs( mouseCoords.cy - c.pt1.cy );
var mouseVelocityX = mouseCoords.cx - mouseCoords.px;
var mouseVelocityY = mouseCoords.cy - mouseCoords.py;
if ( xDiff <= mouseZoneRadius && yDiff <= mouseZoneRadius && mouseCoords.px != null && mouseCoords.py != null ) {
c.pt1.px -= mouseVelocityX*mouseStrength;
c.pt1.py -= mouseVelocityY*mouseStrength;
}
}
}
function popperBurst( event ) {
//blast nearby confettis
for ( var i=0; i<confettis.length; i++ ) {
var c = confettis[i];
var xDiff = c.pt1.cx - mouseCoords.cx;
var yDiff = c.pt1.cy - mouseCoords.cy;
var dist = Math.sqrt( Math.abs(xDiff*xDiff) + Math.abs(yDiff*yDiff) );
var intensity = (popperBurstZoneRadius-dist)*popperBurstIntensity*0.0005;
if ( dist <= popperBurstZoneRadius ) {
c.pt1.px = c.pt1.cx - xDiff*intensity;
c.pt1.py = c.pt1.cy - yDiff*intensity;
}
}
//new confettis burst
var volume = confettis.length < confettiVolumeMax-popperBurstVolumeMax ? popperBurstVolumeMax : confettiVolumeMax-confettis.length;
for ( var i=0; i<volume; i++ ) {
confettis.push( new Confetti( mouseCoords.cx, mouseCoords.cy ) );
confettis[confettis.length-1].pt1.px = mouseCoords.cx;
confettis[confettis.length-1].pt1.py = mouseCoords.cy;
var velocity = VX.rfb( -popperBurstIntensity, popperBurstIntensity );
var angle = VX.rfb( 0, Math.PI*2 );
confettis[confettis.length-1].pt1.px = mouseCoords.cx + Math.cos( angle-Math.PI/2 ) * velocity;
confettis[confettis.length-1].pt1.py = mouseCoords.cy + Math.sin( angle-Math.PI/2 ) * velocity;
}
}
function refresh() {
VX.points = [];
VX.spans = [];
VX.skins = [];
confettis = [];
colors = colors = randomPalette ? generateRandomPalette(5) : colors;
refreshed = true;
beginConfetti();
}
/////--- Adapted Functions ---/////
var mv = 5; // maximum velocity
VX.updatePoints = function() {
for ( var i=0; i<VX.points.length; i++ ) {
var p = VX.points[i];
var xv = ( p.cx - p.px ) * VX.friction;
var yv = ( p.cy - p.py ) * VX.friction;
p.px = p.cx;
p.py = p.cy;
p.cx += xv;
p.cy += yv;
p.cy += VX.gravity * p.mass;
if ( VX.worldTime % VX.rib( 100, 200 ) == 0 ) { p.cx += VX.rfb( -VX.breeze, VX.breeze ); }
}
}
VX.runOnFrameRefresh = function() {
replaceFallenConfettis();
moveConfettisOnMouseOver();
updateMousePreviousCoords();
}
/////--- Scaling ---/////
function scaleToWindow() {
VX.interfaceWidth = VX.canvas.width = window.innerWidth;
VX.interfaceHeight = VX.canvas.height = window.innerHeight;
}
scaleToWindow();
/////--- Events ---/////
window.addEventListener( "resize", scaleToWindow );
restartButton.addEventListener( "mousedown", refresh );
document.addEventListener( "mousedown", ()=> { if ( !refreshed ) popperBurst() });
document.addEventListener( "mousemove", updateMouseCurrentCoords );
/////--- Run ---/////
beginConfetti();
});
This Pen doesn't use any external CSS resources.