Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion src/confetti.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,25 @@
'#ffa62d',
'#ff36ff'
],
enableDoubleSided: false,
frontColors: [
'#4ddcff',
'#ba74ff',
'#ff7e95',
'#aaff7a',
'#fdff76',
'#ffb84d',
'#ff66ff'
],
backColors: [
'#990099',
'#b35900',
'#bdbd00',
'#5fbf3a',
'#cc3f5e',
'#6b36c2',
'#1399cc'
],
// probably should be true, but back-compat
disableForReducedMotion: false,
scalar: 1
Expand Down Expand Up @@ -348,6 +367,9 @@
angle2D: -radAngle + ((0.5 * radSpread) - (Math.random() * radSpread)),
tiltAngle: (Math.random() * (0.75 - 0.25) + 0.25) * Math.PI,
color: opts.color,
colorFront: opts.colorFront,
colorBack: opts.colorBack,
enableDoubleSided: opts.enableDoubleSided,
shape: opts.shape,
tick: 0,
totalTicks: opts.ticks,
Expand Down Expand Up @@ -396,7 +418,12 @@
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) + ')';
var color = fetti.color
if (fetti.enableDoubleSided){
color = fetti.tiltCos >= 0 ? fetti.colorFront : fetti.colorBack;
};

context.fillStyle = 'rgba(' + color.r + ', ' + color.g + ', ' + color.b + ', ' + (1 - progress) + ')';

context.beginPath();

Expand Down Expand Up @@ -565,6 +592,9 @@
var gravity = prop(options, 'gravity', Number);
var drift = prop(options, 'drift', Number);
var colors = prop(options, 'colors', colorsToRgb);
var enableDoubleSided = prop(options, 'enableDoubleSided', Boolean);
var frontColors = prop(options, 'frontColors', colorsToRgb);
var backColors = prop(options, 'backColors', colorsToRgb);
var ticks = prop(options, 'ticks', Number);
var shapes = prop(options, 'shapes');
var scalar = prop(options, 'scalar');
Expand All @@ -586,6 +616,9 @@
spread: spread,
startVelocity: startVelocity,
color: colors[temp % colors.length],
colorFront: frontColors[temp % frontColors.length],
colorBack: backColors[temp % backColors.length],
enableDoubleSided: enableDoubleSided,
shape: shapes[randomInt(0, shapes.length)],
ticks: ticks,
decay: decay,
Expand Down
65 changes: 61 additions & 4 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,27 @@ test('shoots blue confetti', async t => {
t.deepEqual(pixels, ['#0000ff', '#ffffff']);
});

test('shoots double-sided confetti', async t => {
const page = t.context.page = await fixturePage();

t.context.buffer = await confettiImage(page, {
enableDoubleSided: true,
frontColors: ['#ff0000'],
backColors: ['#0000ff'],
particleCount: 100
});
t.context.image = await reduceImg(t.context.buffer);

const pixels = await uniqueColors(t.context.image);

// At least one of the colors should be present, along with the background
t.true(
pixels.includes('#ff0000') || pixels.includes('#0000ff'),
'Expected at least one of the confetti colors to be present'
);
t.true(pixels.includes('#ffffff'), 'Expected background color to be present');
});

test('shoots circle confetti', async t => {
const page = t.context.page = await fixturePage();

Expand Down Expand Up @@ -387,7 +408,8 @@ test('shoots default scaled confetti', async t => {
const pixels = await totalPixels(t.context.image);

const expected = 124;
t.true(pixels > expected * .99 && pixels < expected * 1.01, `${pixels}±1% ≠ ${expected}`);
// Allow for a 5% margin of error to account for rendering differences
t.true(pixels > expected * 0.95 && pixels < expected * 1.05, `${pixels}±5% ≠ ${expected}`);
});

test('shoots larger scaled confetti', async t => {
Expand Down Expand Up @@ -525,6 +547,41 @@ test('shoots confetti repeatedly using requestAnimationFrame', async t => {
t.deepEqual(await uniqueColors(await reduceImg(img3)), ['#0000ff', '#ffffff']);
t.deepEqual(await uniqueColors(await reduceImg(img4)), ['#0000ff', '#ffffff']);
});
/*
* Double-sided confetti tests
*/

test('double-sided confetti falls back to main colors', async t => {
const page = t.context.page = await fixturePage();

t.context.buffer = await confettiImage(page, {
enableDoubleSided: true,
colors: ['#ff0000'], // only main colors specified
particleCount: 50
});
t.context.image = await reduceImg(t.context.buffer);
const pixels = await uniqueColors(t.context.image);

// Should use main color for both sides when front/back not specified
t.true(pixels.includes('#ff0000'), 'Expected main color to be present');
});

test('double-sided confetti works with default colors when front/back not specified', async t => {
const page = t.context.page = await fixturePage();

t.context.buffer = await confettiImage(page, {
enableDoubleSided: true,
colors: ['#ff0000'], // only main colors specified
particleCount: 100
});
t.context.image = await reduceImg(t.context.buffer);

const pixels = await uniqueColors(t.context.image);

// Should fall back to main colors for both sides
t.true(pixels.includes('#ff0000'), 'Expected main color to be present');
t.true(pixels.includes('#ffffff'), 'Expected background color to be present');
});

test('uses promises when available', async t => {
const page = t.context.page = await fixturePage();
Expand Down Expand Up @@ -883,7 +940,7 @@ test('[custom canvas] can use a custom canvas without resizing', async t => {
t.deepEqual(beforeSize, afterSize);
});

const resizeTest = async (t, createOpts, createName = 'confetti.create') => {
async function resizeTest(t, createOpts, createName = 'confetti.create') {
const time = 50;

const page = t.context.page = await fixturePage();
Expand Down Expand Up @@ -940,7 +997,7 @@ const resizeTest = async (t, createOpts, createName = 'confetti.create') => {
t.deepEqual(await uniqueColors(first), ['#ffffff']);
t.deepEqual(await uniqueColors(second), ['#0000ff', '#ffffff']);
t.deepEqual(await uniqueColors(third), ['#0000ff', '#ffffff']);
};
}

test('[custom canvas] resizes the custom canvas when the window resizes', async t => {
await resizeTest(t, {
Expand Down Expand Up @@ -1246,4 +1303,4 @@ test('[esm] exposed confetti method has a `shapeFromText` property', async t =>
const page = t.context.page = await fixturePage('fixtures/page.module.html');

t.is(await page.evaluate(`typeof confettiAlias.shapeFromText`), 'function');
});
});