diff --git a/src/WebApp/Components/App.razor b/src/WebApp/Components/App.razor index 4455b92b9..ec5aad921 100644 --- a/src/WebApp/Components/App.razor +++ b/src/WebApp/Components/App.razor @@ -14,6 +14,8 @@ + + diff --git a/src/WebApp/Components/Pages/Item/ItemPage.razor b/src/WebApp/Components/Pages/Item/ItemPage.razor index f61a11b94..8b38c7da3 100644 --- a/src/WebApp/Components/Pages/Item/ItemPage.razor +++ b/src/WebApp/Components/Pages/Item/ItemPage.razor @@ -3,6 +3,7 @@ @inject CatalogService CatalogService @inject BasketState BasketState @inject NavigationManager Nav +@inject IJSRuntime JS @inject IProductImageUrlProvider ProductImages @if (item is not null) @@ -99,6 +100,7 @@ else if (notFound) { await BasketState.AddAsync(item); await UpdateNumInCartAsync(); + await TriggerConfettiAsync(); } } @@ -107,4 +109,16 @@ else if (notFound) var items = await BasketState.GetBasketItemsAsync(); numInCart = items.FirstOrDefault(row => row.ProductId == ItemId)?.Quantity ?? 0; } + + private async Task TriggerConfettiAsync() + { + try + { + await JS.InvokeVoidAsync("eshopConfetti.launch"); + } + catch (JSDisconnectedException) + { + // If the connection has been interrupted, there's nowhere to display confetti. + } + } } diff --git a/src/WebApp/wwwroot/js/confetti.js b/src/WebApp/wwwroot/js/confetti.js new file mode 100644 index 000000000..fc3f319e9 --- /dev/null +++ b/src/WebApp/wwwroot/js/confetti.js @@ -0,0 +1,39 @@ +window.eshopConfetti = window.eshopConfetti || { + launch: () => { + if (typeof confetti !== "function") { + return; + } + + const duration = 2000; + const animationEnd = Date.now() + duration; + const defaults = { + spread: 360, + ticks: 180, + gravity: 0.8, + decay: 0.94, + startVelocity: 60, + zIndex: 1000, + scalar: 1.2, + }; + + const randomInRange = (min, max) => Math.random() * (max - min) + min; + + const shoot = () => { + confetti({ + ...defaults, + particleCount: 180, + origin: { x: randomInRange(0.1, 0.9), y: randomInRange(0.1, 0.3) }, + colors: ["#ff577f", "#ff884b", "#ffd384", "#fff9b0", "#62cdff", "#8c52ff"], + }); + }; + + const frame = () => { + shoot(); + if (Date.now() < animationEnd) { + requestAnimationFrame(frame); + } + }; + + frame(); + }, +};