tournament podium confetti
parent
6d67516f84
commit
a69f2900a0
|
@ -0,0 +1,404 @@
|
|||
var retina = window.devicePixelRatio,
|
||||
|
||||
// Math shorthands
|
||||
PI = Math.PI,
|
||||
sqrt = Math.sqrt,
|
||||
round = Math.round,
|
||||
random = Math.random,
|
||||
cos = Math.cos,
|
||||
sin = Math.sin,
|
||||
|
||||
// Local WindowAnimationTiming interface
|
||||
rAF = window.requestAnimationFrame,
|
||||
cAF = window.cancelAnimationFrame || window.cancelRequestAnimationFrame,
|
||||
_now = Date.now || function() {
|
||||
return new Date().getTime();
|
||||
};
|
||||
|
||||
// Local WindowAnimationTiming interface polyfill
|
||||
(function(w) {
|
||||
/**
|
||||
* Fallback implementation.
|
||||
*/
|
||||
var prev = _now();
|
||||
|
||||
function fallback(fn) {
|
||||
var curr = _now();
|
||||
var ms = Math.max(0, 16 - (curr - prev));
|
||||
var req = setTimeout(fn, ms);
|
||||
prev = curr;
|
||||
return req;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel.
|
||||
*/
|
||||
var cancel = w.cancelAnimationFrame ||
|
||||
w.webkitCancelAnimationFrame ||
|
||||
w.clearTimeout;
|
||||
|
||||
rAF = w.requestAnimationFrame ||
|
||||
w.webkitRequestAnimationFrame ||
|
||||
fallback;
|
||||
|
||||
cAF = function(id) {
|
||||
cancel.call(w, id);
|
||||
};
|
||||
}(window));
|
||||
|
||||
(function() {
|
||||
var 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"]
|
||||
];
|
||||
|
||||
function Vector2(_x, _y) {
|
||||
this.x = _x, this.y = _y;
|
||||
this.Length = function() {
|
||||
return sqrt(this.SqrLength());
|
||||
}
|
||||
this.SqrLength = function() {
|
||||
return this.x * this.x + this.y * this.y;
|
||||
}
|
||||
this.Add = function(_vec) {
|
||||
this.x += _vec.x;
|
||||
this.y += _vec.y;
|
||||
}
|
||||
this.Sub = function(_vec) {
|
||||
this.x -= _vec.x;
|
||||
this.y -= _vec.y;
|
||||
}
|
||||
this.Div = function(_f) {
|
||||
this.x /= _f;
|
||||
this.y /= _f;
|
||||
}
|
||||
this.Mul = function(_f) {
|
||||
this.x *= _f;
|
||||
this.y *= _f;
|
||||
}
|
||||
this.Normalize = function() {
|
||||
var sqrLen = this.SqrLength();
|
||||
if (sqrLen != 0) {
|
||||
var factor = 1.0 / sqrt(sqrLen);
|
||||
this.x *= factor;
|
||||
this.y *= factor;
|
||||
}
|
||||
}
|
||||
this.Normalized = function() {
|
||||
var sqrLen = this.SqrLength();
|
||||
if (sqrLen != 0) {
|
||||
var factor = 1.0 / sqrt(sqrLen);
|
||||
return new Vector2(this.x * factor, this.y * factor);
|
||||
}
|
||||
return new Vector2(0, 0);
|
||||
}
|
||||
}
|
||||
Vector2.Lerp = function(_vec0, _vec1, _t) {
|
||||
return new Vector2((_vec1.x - _vec0.x) * _t + _vec0.x, (_vec1.y - _vec0.y) * _t + _vec0.y);
|
||||
}
|
||||
Vector2.Distance = function(_vec0, _vec1) {
|
||||
return sqrt(Vector2.SqrDistance(_vec0, _vec1));
|
||||
}
|
||||
Vector2.SqrDistance = function(_vec0, _vec1) {
|
||||
var x = _vec0.x - _vec1.x;
|
||||
var y = _vec0.y - _vec1.y;
|
||||
return (x * x + y * y + z * z);
|
||||
}
|
||||
Vector2.Scale = function(_vec0, _vec1) {
|
||||
return new Vector2(_vec0.x * _vec1.x, _vec0.y * _vec1.y);
|
||||
}
|
||||
Vector2.Min = function(_vec0, _vec1) {
|
||||
return new Vector2(Math.min(_vec0.x, _vec1.x), Math.min(_vec0.y, _vec1.y));
|
||||
}
|
||||
Vector2.Max = function(_vec0, _vec1) {
|
||||
return new Vector2(Math.max(_vec0.x, _vec1.x), Math.max(_vec0.y, _vec1.y));
|
||||
}
|
||||
Vector2.ClampMagnitude = function(_vec0, _len) {
|
||||
var vecNorm = _vec0.Normalized;
|
||||
return new Vector2(vecNorm.x * _len, vecNorm.y * _len);
|
||||
}
|
||||
Vector2.Sub = function(_vec0, _vec1) {
|
||||
return new Vector2(_vec0.x - _vec1.x, _vec0.y - _vec1.y, _vec0.z - _vec1.z);
|
||||
}
|
||||
|
||||
function EulerMass(_x, _y, _mass, _drag) {
|
||||
this.position = new Vector2(_x, _y);
|
||||
this.mass = _mass;
|
||||
this.drag = _drag;
|
||||
this.force = new Vector2(0, 0);
|
||||
this.velocity = new Vector2(0, 0);
|
||||
this.AddForce = function(_f) {
|
||||
this.force.Add(_f);
|
||||
}
|
||||
this.Integrate = function(_dt) {
|
||||
var acc = this.CurrentForce(this.position);
|
||||
acc.Div(this.mass);
|
||||
var posDelta = new Vector2(this.velocity.x, this.velocity.y);
|
||||
posDelta.Mul(_dt);
|
||||
this.position.Add(posDelta);
|
||||
acc.Mul(_dt);
|
||||
this.velocity.Add(acc);
|
||||
this.force = new Vector2(0, 0);
|
||||
}
|
||||
this.CurrentForce = function(_pos, _vel) {
|
||||
var totalForce = new Vector2(this.force.x, this.force.y);
|
||||
var speed = this.velocity.Length();
|
||||
var dragVel = new Vector2(this.velocity.x, this.velocity.y);
|
||||
dragVel.Mul(this.drag * this.mass * speed);
|
||||
totalForce.Sub(dragVel);
|
||||
return totalForce;
|
||||
}
|
||||
}
|
||||
|
||||
function ConfettiPaper(_x, _y) {
|
||||
this.pos = new Vector2(_x, _y);
|
||||
this.rotationSpeed = (random() * 600 + 800);
|
||||
this.angle = DEG_TO_RAD * random() * 360;
|
||||
this.rotation = DEG_TO_RAD * random() * 360;
|
||||
this.cosA = 1.0;
|
||||
this.size = 5.0;
|
||||
this.oscillationSpeed = (random() * 1.5 + 0.5);
|
||||
this.xSpeed = 40.0;
|
||||
this.ySpeed = (random() * 60 + 50.0);
|
||||
this.corners = new Array();
|
||||
this.time = random();
|
||||
var ci = round(random() * (colors.length - 1));
|
||||
this.frontColor = colors[ci][0];
|
||||
this.backColor = colors[ci][1];
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var dx = cos(this.angle + DEG_TO_RAD * (i * 90 + 45));
|
||||
var dy = sin(this.angle + DEG_TO_RAD * (i * 90 + 45));
|
||||
this.corners[i] = new Vector2(dx, dy);
|
||||
}
|
||||
this.Update = function(_dt) {
|
||||
this.time += _dt;
|
||||
this.rotation += this.rotationSpeed * _dt;
|
||||
this.cosA = cos(DEG_TO_RAD * this.rotation);
|
||||
this.pos.x += cos(this.time * this.oscillationSpeed) * this.xSpeed * _dt
|
||||
this.pos.y += this.ySpeed * _dt;
|
||||
if (this.pos.y > ConfettiPaper.bounds.y) {
|
||||
this.pos.x = random() * ConfettiPaper.bounds.x;
|
||||
this.pos.y = 0;
|
||||
}
|
||||
}
|
||||
this.Draw = function(_g) {
|
||||
if (this.cosA > 0) {
|
||||
_g.fillStyle = this.frontColor;
|
||||
} else {
|
||||
_g.fillStyle = this.backColor;
|
||||
}
|
||||
_g.beginPath();
|
||||
_g.moveTo((this.pos.x + this.corners[0].x * this.size) * retina, (this.pos.y + this.corners[0].y * this.size * this.cosA) * retina);
|
||||
for (var i = 1; i < 4; i++) {
|
||||
_g.lineTo((this.pos.x + this.corners[i].x * this.size) * retina, (this.pos.y + this.corners[i].y * this.size * this.cosA) * retina);
|
||||
}
|
||||
_g.closePath();
|
||||
_g.fill();
|
||||
}
|
||||
}
|
||||
ConfettiPaper.bounds = new Vector2(0, 0);
|
||||
|
||||
function ConfettiRibbon(_x, _y, _count, _dist, _thickness, _angle, _mass, _drag) {
|
||||
this.particleDist = _dist;
|
||||
this.particleCount = _count;
|
||||
this.particleMass = _mass;
|
||||
this.particleDrag = _drag;
|
||||
this.particles = new Array();
|
||||
var ci = round(random() * (colors.length - 1));
|
||||
this.frontColor = colors[ci][0];
|
||||
this.backColor = colors[ci][1];
|
||||
this.xOff = (cos(DEG_TO_RAD * _angle) * _thickness);
|
||||
this.yOff = (sin(DEG_TO_RAD * _angle) * _thickness);
|
||||
this.position = new Vector2(_x, _y);
|
||||
this.prevPosition = new Vector2(_x, _y);
|
||||
this.velocityInherit = (random() * 2 + 4);
|
||||
this.time = random() * 100;
|
||||
this.oscillationSpeed = (random() * 2 + 2);
|
||||
this.oscillationDistance = (random() * 40 + 40);
|
||||
this.ySpeed = (random() * 40 + 80);
|
||||
for (var i = 0; i < this.particleCount; i++) {
|
||||
this.particles[i] = new EulerMass(_x, _y - i * this.particleDist, this.particleMass, this.particleDrag);
|
||||
}
|
||||
this.Update = function(_dt) {
|
||||
var i = 0;
|
||||
this.time += _dt * this.oscillationSpeed;
|
||||
this.position.y += this.ySpeed * _dt;
|
||||
this.position.x += cos(this.time) * this.oscillationDistance * _dt;
|
||||
this.particles[0].position = this.position;
|
||||
var dX = this.prevPosition.x - this.position.x;
|
||||
var dY = this.prevPosition.y - this.position.y;
|
||||
var delta = sqrt(dX * dX + dY * dY);
|
||||
this.prevPosition = new Vector2(this.position.x, this.position.y);
|
||||
for (i = 1; i < this.particleCount; i++) {
|
||||
var dirP = Vector2.Sub(this.particles[i - 1].position, this.particles[i].position);
|
||||
dirP.Normalize();
|
||||
dirP.Mul((delta / _dt) * this.velocityInherit);
|
||||
this.particles[i].AddForce(dirP);
|
||||
}
|
||||
for (i = 1; i < this.particleCount; i++) {
|
||||
this.particles[i].Integrate(_dt);
|
||||
}
|
||||
for (i = 1; i < this.particleCount; i++) {
|
||||
var rp2 = new Vector2(this.particles[i].position.x, this.particles[i].position.y);
|
||||
rp2.Sub(this.particles[i - 1].position);
|
||||
rp2.Normalize();
|
||||
rp2.Mul(this.particleDist);
|
||||
rp2.Add(this.particles[i - 1].position);
|
||||
this.particles[i].position = rp2;
|
||||
}
|
||||
if (this.position.y > ConfettiRibbon.bounds.y + this.particleDist * this.particleCount) {
|
||||
this.Reset();
|
||||
}
|
||||
}
|
||||
this.Reset = function() {
|
||||
this.position.y = -random() * ConfettiRibbon.bounds.y;
|
||||
this.position.x = random() * ConfettiRibbon.bounds.x;
|
||||
this.prevPosition = new Vector2(this.position.x, this.position.y);
|
||||
this.velocityInherit = random() * 2 + 4;
|
||||
this.time = random() * 100;
|
||||
this.oscillationSpeed = random() * 2.0 + 1.5;
|
||||
this.oscillationDistance = (random() * 40 + 40);
|
||||
this.ySpeed = random() * 40 + 80;
|
||||
var ci = round(random() * (colors.length - 1));
|
||||
this.frontColor = colors[ci][0];
|
||||
this.backColor = colors[ci][1];
|
||||
this.particles = new Array();
|
||||
for (var i = 0; i < this.particleCount; i++) {
|
||||
this.particles[i] = new EulerMass(this.position.x, this.position.y - i * this.particleDist, this.particleMass, this.particleDrag);
|
||||
}
|
||||
}
|
||||
this.Draw = function(_g) {
|
||||
for (var i = 0; i < this.particleCount - 1; i++) {
|
||||
var p0 = new Vector2(this.particles[i].position.x + this.xOff, this.particles[i].position.y + this.yOff);
|
||||
var p1 = new Vector2(this.particles[i + 1].position.x + this.xOff, this.particles[i + 1].position.y + this.yOff);
|
||||
if (this.Side(this.particles[i].position.x, this.particles[i].position.y, this.particles[i + 1].position.x, this.particles[i + 1].position.y, p1.x, p1.y) < 0) {
|
||||
_g.fillStyle = this.frontColor;
|
||||
_g.strokeStyle = this.frontColor;
|
||||
} else {
|
||||
_g.fillStyle = this.backColor;
|
||||
_g.strokeStyle = this.backColor;
|
||||
}
|
||||
if (i == 0) {
|
||||
_g.beginPath();
|
||||
_g.moveTo(this.particles[i].position.x * retina, this.particles[i].position.y * retina);
|
||||
_g.lineTo(this.particles[i + 1].position.x * retina, this.particles[i + 1].position.y * retina);
|
||||
_g.lineTo(((this.particles[i + 1].position.x + p1.x) * 0.5) * retina, ((this.particles[i + 1].position.y + p1.y) * 0.5) * retina);
|
||||
_g.closePath();
|
||||
_g.stroke();
|
||||
_g.fill();
|
||||
_g.beginPath();
|
||||
_g.moveTo(p1.x * retina, p1.y * retina);
|
||||
_g.lineTo(p0.x * retina, p0.y * retina);
|
||||
_g.lineTo(((this.particles[i + 1].position.x + p1.x) * 0.5) * retina, ((this.particles[i + 1].position.y + p1.y) * 0.5) * retina);
|
||||
_g.closePath();
|
||||
_g.stroke();
|
||||
_g.fill();
|
||||
} else if (i == this.particleCount - 2) {
|
||||
_g.beginPath();
|
||||
_g.moveTo(this.particles[i].position.x * retina, this.particles[i].position.y * retina);
|
||||
_g.lineTo(this.particles[i + 1].position.x * retina, this.particles[i + 1].position.y * retina);
|
||||
_g.lineTo(((this.particles[i].position.x + p0.x) * 0.5) * retina, ((this.particles[i].position.y + p0.y) * 0.5) * retina);
|
||||
_g.closePath();
|
||||
_g.stroke();
|
||||
_g.fill();
|
||||
_g.beginPath();
|
||||
_g.moveTo(p1.x * retina, p1.y * retina);
|
||||
_g.lineTo(p0.x * retina, p0.y * retina);
|
||||
_g.lineTo(((this.particles[i].position.x + p0.x) * 0.5) * retina, ((this.particles[i].position.y + p0.y) * 0.5) * retina);
|
||||
_g.closePath();
|
||||
_g.stroke();
|
||||
_g.fill();
|
||||
} else {
|
||||
_g.beginPath();
|
||||
_g.moveTo(this.particles[i].position.x * retina, this.particles[i].position.y * retina);
|
||||
_g.lineTo(this.particles[i + 1].position.x * retina, this.particles[i + 1].position.y * retina);
|
||||
_g.lineTo(p1.x * retina, p1.y * retina);
|
||||
_g.lineTo(p0.x * retina, p0.y * retina);
|
||||
_g.closePath();
|
||||
_g.stroke();
|
||||
_g.fill();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.Side = function(x1, y1, x2, y2, x3, y3) {
|
||||
return ((x1 - x2) * (y3 - y2) - (y1 - y2) * (x3 - x2));
|
||||
}
|
||||
}
|
||||
ConfettiRibbon.bounds = new Vector2(0, 0);
|
||||
confetti = {};
|
||||
confetti.Context = function(id) {
|
||||
var i = 0;
|
||||
var canvas = document.getElementById(id);
|
||||
var canvasParent = canvas.parentNode;
|
||||
var canvasWidth = canvasParent.offsetWidth;
|
||||
var canvasHeight = canvasParent.offsetHeight;
|
||||
canvas.width = canvasWidth * retina;
|
||||
canvas.height = canvasHeight * retina;
|
||||
var context = canvas.getContext('2d');
|
||||
var interval = null;
|
||||
var confettiRibbons = new Array();
|
||||
ConfettiRibbon.bounds = new Vector2(canvasWidth, canvasHeight);
|
||||
for (i = 0; i < confettiRibbonCount; i++) {
|
||||
confettiRibbons[i] = new ConfettiRibbon(random() * canvasWidth, -random() * canvasHeight * 2, ribbonPaperCount, ribbonPaperDist, ribbonPaperThick, 45, 1, 0.05);
|
||||
}
|
||||
var confettiPapers = new Array();
|
||||
ConfettiPaper.bounds = new Vector2(canvasWidth, canvasHeight);
|
||||
for (i = 0; i < confettiPaperCount; i++) {
|
||||
confettiPapers[i] = new ConfettiPaper(random() * canvasWidth, random() * canvasHeight);
|
||||
}
|
||||
this.resize = function() {
|
||||
canvasWidth = canvasParent.offsetWidth;
|
||||
canvasHeight = canvasParent.offsetHeight;
|
||||
canvas.width = canvasWidth * retina;
|
||||
canvas.height = canvasHeight * retina;
|
||||
ConfettiPaper.bounds = new Vector2(canvasWidth, canvasHeight);
|
||||
ConfettiRibbon.bounds = new Vector2(canvasWidth, canvasHeight);
|
||||
}
|
||||
this.start = function() {
|
||||
this.stop()
|
||||
var context = this;
|
||||
this.update();
|
||||
}
|
||||
this.stop = function() {
|
||||
cAF(this.interval);
|
||||
}
|
||||
this.update = function() {
|
||||
var i = 0;
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
for (i = 0; i < confettiPaperCount; i++) {
|
||||
confettiPapers[i].Update(duration);
|
||||
confettiPapers[i].Draw(context);
|
||||
}
|
||||
for (i = 0; i < confettiRibbonCount; i++) {
|
||||
confettiRibbons[i].Update(duration);
|
||||
confettiRibbons[i].Draw(context);
|
||||
}
|
||||
this.interval = rAF(function() {
|
||||
confetti.update();
|
||||
});
|
||||
}
|
||||
}
|
||||
var id = 'confetti';
|
||||
var confetti = new confetti.Context(id);
|
||||
confetti.start();
|
||||
setTimeout(function() {
|
||||
$('#' + id).fadeOut(20000, function() {
|
||||
confetti.stop();
|
||||
$(this).remove();
|
||||
});
|
||||
}, 7000);
|
||||
window.addEventListener('resize', function(event) {
|
||||
confetti.resize();
|
||||
});
|
||||
})();
|
|
@ -92,9 +92,6 @@ ol.scheduled_tournaments a {
|
|||
#tournament .description {
|
||||
padding: 20px;
|
||||
}
|
||||
#tournament .podium {
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
#tournament .podium > div {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
|
@ -161,6 +158,20 @@ ol.scheduled_tournaments a {
|
|||
width: 525px;
|
||||
min-height: 480px;
|
||||
}
|
||||
#tournament div.big_top {
|
||||
position: relative;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
#tournament div.big_top #confetti {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
#tournament div.tournament_show div.game_list {
|
||||
margin: 0 0 0 15px;
|
||||
padding-top: 20px;
|
||||
|
|
|
@ -13,10 +13,7 @@ module.exports = {
|
|||
end: function(data) {
|
||||
if (!data.me) return;
|
||||
if (!data.isRecentlyFinished) return;
|
||||
|
||||
var storageKey = 'tournament.end.sound.' + data.id;
|
||||
if (lichess.storage.get(storageKey)) return;
|
||||
lichess.storage.set(storageKey, 1);
|
||||
if (!lichess.once('tournament.end.sound.' + data.id)) return;
|
||||
|
||||
var soundKey = 'Other';
|
||||
if (data.me.rank < 4) soundKey = '1st';
|
||||
|
|
|
@ -7,12 +7,31 @@ var playerInfo = require('./playerInfo');
|
|||
var pagination = require('../pagination');
|
||||
var header = require('./header');
|
||||
|
||||
function confetti(data) {
|
||||
if (!data.me) return;
|
||||
if (!data.isRecentlyFinished) return;
|
||||
if (data.me.rank > 3) return;
|
||||
if (!lichess.once('tournament.end.canvas.' + data.id)) return;
|
||||
return m('canvas', {
|
||||
id: 'confetti',
|
||||
height: 1,
|
||||
widht: 1,
|
||||
config: function(el, isUpdate) {
|
||||
if (isUpdate) return;
|
||||
lichess.loadScript('/assets/javascripts/confetti.js');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
main: function(ctrl) {
|
||||
var pag = pagination.players(ctrl);
|
||||
return [
|
||||
header(ctrl),
|
||||
arena.podium(ctrl),
|
||||
m('div.big_top', [
|
||||
confetti(ctrl.data),
|
||||
header(ctrl),
|
||||
arena.podium(ctrl)
|
||||
]),
|
||||
arena.standing(ctrl, pag)
|
||||
];
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue