in pontoon/base/static/js/lib/confetti.browser.js [371:482]
function updateFetti(context, fetti) {
fetti.x += Math.cos(fetti.angle2D) * fetti.velocity + fetti.drift;
fetti.y += Math.sin(fetti.angle2D) * fetti.velocity + fetti.gravity;
fetti.velocity *= fetti.decay;
if (fetti.flat) {
fetti.wobble = 0;
fetti.wobbleX = fetti.x + (10 * fetti.scalar);
fetti.wobbleY = fetti.y + (10 * fetti.scalar);
fetti.tiltSin = 0;
fetti.tiltCos = 0;
fetti.random = 1;
} else {
fetti.wobble += fetti.wobbleSpeed;
fetti.wobbleX = fetti.x + ((10 * fetti.scalar) * Math.cos(fetti.wobble));
fetti.wobbleY = fetti.y + ((10 * fetti.scalar) * Math.sin(fetti.wobble));
fetti.tiltAngle += 0.1;
fetti.tiltSin = Math.sin(fetti.tiltAngle);
fetti.tiltCos = Math.cos(fetti.tiltAngle);
fetti.random = Math.random() + 2;
}
var progress = (fetti.tick++) / fetti.totalTicks;
var x1 = fetti.x + (fetti.random * fetti.tiltCos);
var y1 = fetti.y + (fetti.random * fetti.tiltSin);
var x2 = fetti.wobbleX + (fetti.random * fetti.tiltCos);
var y2 = fetti.wobbleY + (fetti.random * fetti.tiltSin);
context.fillStyle = 'rgba(' + fetti.color.r + ', ' + fetti.color.g + ', ' + fetti.color.b + ', ' + (1 - progress) + ')';
context.beginPath();
if (canUsePaths && fetti.shape.type === 'path' && typeof fetti.shape.path === 'string' && Array.isArray(fetti.shape.matrix)) {
context.fill(transformPath2D(
fetti.shape.path,
fetti.shape.matrix,
fetti.x,
fetti.y,
Math.abs(x2 - x1) * 0.1,
Math.abs(y2 - y1) * 0.1,
Math.PI / 10 * fetti.wobble
));
} else if (fetti.shape.type === 'bitmap') {
var rotation = Math.PI / 10 * fetti.wobble;
var scaleX = Math.abs(x2 - x1) * 0.1;
var scaleY = Math.abs(y2 - y1) * 0.1;
var width = fetti.shape.bitmap.width * fetti.scalar;
var height = fetti.shape.bitmap.height * fetti.scalar;
var matrix = new DOMMatrix([
Math.cos(rotation) * scaleX,
Math.sin(rotation) * scaleX,
-Math.sin(rotation) * scaleY,
Math.cos(rotation) * scaleY,
fetti.x,
fetti.y
]);
// apply the transform matrix from the confetti shape
matrix.multiplySelf(new DOMMatrix(fetti.shape.matrix));
var pattern = context.createPattern(bitmapMapper.transform(fetti.shape.bitmap), 'no-repeat');
pattern.setTransform(matrix);
context.globalAlpha = (1 - progress);
context.fillStyle = pattern;
context.fillRect(
fetti.x - (width / 2),
fetti.y - (height / 2),
width,
height
);
context.globalAlpha = 1;
} else if (fetti.shape === 'circle') {
context.ellipse ?
context.ellipse(fetti.x, fetti.y, Math.abs(x2 - x1) * fetti.ovalScalar, Math.abs(y2 - y1) * fetti.ovalScalar, Math.PI / 10 * fetti.wobble, 0, 2 * Math.PI) :
ellipse(context, fetti.x, fetti.y, Math.abs(x2 - x1) * fetti.ovalScalar, Math.abs(y2 - y1) * fetti.ovalScalar, Math.PI / 10 * fetti.wobble, 0, 2 * Math.PI);
} else if (fetti.shape === 'star') {
var rot = Math.PI / 2 * 3;
var innerRadius = 4 * fetti.scalar;
var outerRadius = 8 * fetti.scalar;
var x = fetti.x;
var y = fetti.y;
var spikes = 5;
var step = Math.PI / spikes;
while (spikes--) {
x = fetti.x + Math.cos(rot) * outerRadius;
y = fetti.y + Math.sin(rot) * outerRadius;
context.lineTo(x, y);
rot += step;
x = fetti.x + Math.cos(rot) * innerRadius;
y = fetti.y + Math.sin(rot) * innerRadius;
context.lineTo(x, y);
rot += step;
}
} else {
context.moveTo(Math.floor(fetti.x), Math.floor(fetti.y));
context.lineTo(Math.floor(fetti.wobbleX), Math.floor(y1));
context.lineTo(Math.floor(x2), Math.floor(y2));
context.lineTo(Math.floor(x1), Math.floor(fetti.wobbleY));
}
context.closePath();
context.fill();
return fetti.tick < fetti.totalTicks;
}