-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
278 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
demo/Jimmys20.BlazorComponents.Demo/Pages/SpinningWheel.razor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
@page "/spinningwheel" | ||
@inject IDialogService Dialog | ||
|
||
<PageTitle>Spinning wheel</PageTitle> | ||
|
||
<h1>Spinning wheel</h1> | ||
|
||
<JmSpinningWheel @ref="_spinningWheelRef" | ||
NumberOfSlots="_numberOfSlots" | ||
SlotNames="_slotNames" | ||
Size="600" | ||
SpinStarted="OnSpinStarted" | ||
SpinCompleted="OnSpinCompleted" /> | ||
|
||
<JmButton Color="Color.Primary" Clicked="Spin">Spin</JmButton> | ||
|
||
@code { | ||
private JmSpinningWheel _spinningWheelRef; | ||
private int _numberOfSlots = 18; | ||
private string[] _slotNames = | ||
[ | ||
"50", "100", "150", "200", "1000", "500", "550", "600", "650", | ||
"700", "10", "800", "2000", "60", "995", "500", "10000", "900" | ||
]; | ||
|
||
private int _numberOfTimes = 5; | ||
private bool _shouldRandomizeNumberOfSpins = true; | ||
|
||
private async Task Spin() | ||
{ | ||
await _spinningWheelRef.Spin(_numberOfTimes, _shouldRandomizeNumberOfSpins); | ||
} | ||
|
||
private void OnSpinStarted() | ||
{ | ||
Console.WriteLine("Spin started"); | ||
} | ||
|
||
private async Task OnSpinCompleted(string nameOfSelectedSlot) | ||
{ | ||
//Console.WriteLine($"Spin completed - nameOfSelectedSlot: {nameOfSelectedSlot}"); | ||
await Dialog.Alert($"Spin completed - nameOfSelectedSlot: {nameOfSelectedSlot}"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
namespace Jimmys20.BlazorComponents.Extensions; | ||
|
||
internal static class RandomExtensions | ||
{ | ||
public static double NextDouble(this Random random, double minValue, double maxValue) | ||
{ | ||
return random.NextDouble() * (maxValue - minValue) + minValue; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
src/Jimmys20.BlazorComponents/SpinningWheel/Internal/Sector.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace Jimmys20.BlazorComponents.SpinningWheel.Internal; | ||
|
||
internal class Sector | ||
{ | ||
public string Color { get; init; } | ||
public string Label { get; init; } | ||
} |
3 changes: 3 additions & 0 deletions
3
src/Jimmys20.BlazorComponents/SpinningWheel/JmSpinningWheel.razor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
@namespace Jimmys20.BlazorComponents | ||
|
||
<canvas @ref="_canvasRef" id="wheel" width="@Size" height="@Size" @attributes="AdditionalAttributes"></canvas> |
180 changes: 180 additions & 0 deletions
180
src/Jimmys20.BlazorComponents/SpinningWheel/JmSpinningWheel.razor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
using Excubo.Blazor.Canvas; | ||
using Excubo.Blazor.Canvas.Contexts; | ||
using Jimmys20.BlazorComponents.Extensions; | ||
using Jimmys20.BlazorComponents.SpinningWheel.Internal; | ||
using Microsoft.AspNetCore.Components; | ||
using Microsoft.JSInterop; | ||
|
||
namespace Jimmys20.BlazorComponents; | ||
|
||
public partial class JmSpinningWheel : IAsyncDisposable | ||
{ | ||
//private static readonly string[] Colors = ["#f82", "#0bf", "#fb0", "#0fb", "#b0f", "#f0b", "#bf0"]; | ||
private static readonly string[] Colors = | ||
[ | ||
"#ffcc01", "#675ca1", "#059dde", "#a7d02a", "#26cda2", "#903288", "#222025", "#e50305", "#eb7008" | ||
]; | ||
|
||
private IJSObjectReference _module; | ||
private DotNetObjectReference<JmSpinningWheel> _selfReference; | ||
private ElementReference _canvasRef; | ||
private Context2D _context = null; | ||
private bool _isSpinning = false; | ||
private double _angle = 0; | ||
private int _selectedSlotIndex = -1; | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
[Parameter] public int NumberOfSlots { get; set; } = 18; | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
[Parameter] public IList<string> SlotNames { get; set; } | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
//[Parameter] public string SelectedSlot { get; set; } | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
[Parameter] public EventCallback SpinStarted { get; set; } | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
[Parameter] public EventCallback<string> SpinCompleted { get; set; } | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
[Parameter] public int Size { get; set; } | ||
|
||
/// <summary> | ||
/// Captures values that don't match any other parameter. | ||
/// </summary> | ||
[Parameter(CaptureUnmatchedValues = true)] public IDictionary<string, object> AdditionalAttributes { get; set; } | ||
|
||
[Inject] private IJSRuntime JS { get; set; } | ||
|
||
private double Diameter => Size; | ||
private double Radius => Diameter / 2; | ||
private double Arc => Math.Tau / NumberOfSlots; | ||
|
||
private static double Mod(double n, double m) => (n % m + m) % m; | ||
|
||
protected override async Task OnAfterRenderAsync(bool firstRender) | ||
{ | ||
if (firstRender) | ||
{ | ||
_module = await JS.InvokeAsync<IJSObjectReference>("import", | ||
"./_content/Jimmys20.BlazorComponents/js/spinning-wheel.js"); | ||
_selfReference = DotNetObjectReference.Create(this); | ||
|
||
_context = await JS.GetContext2DAsync(_canvasRef); | ||
await DrawWheel(); | ||
} | ||
} | ||
|
||
public async Task Redraw() | ||
{ | ||
await _context.ClearRectAsync(0, 0, Size, Size); | ||
await DrawWheel(); | ||
} | ||
|
||
private async Task DrawWheel() | ||
{ | ||
await using Batch2D batch = _context.CreateBatch(); | ||
|
||
for (var i = 0; i < NumberOfSlots; i++) | ||
{ | ||
var sector = new Sector | ||
{ | ||
Color = Colors[i % Colors.Length], | ||
Label = SlotNames[i % SlotNames.Count], | ||
}; | ||
|
||
await DrawSector(batch, sector, i); | ||
} | ||
} | ||
|
||
private async Task DrawSector(Batch2D batch, Sector sector, int i) | ||
{ | ||
double angle = Arc * i; | ||
|
||
await batch.SaveAsync(); | ||
|
||
await batch.BeginPathAsync(); | ||
await batch.FillStyleAsync(sector.Color); | ||
await batch.MoveToAsync(Radius, Radius); | ||
await batch.ArcAsync(Radius, Radius, Radius, angle, angle + Arc); | ||
await batch.LineToAsync(Radius, Radius); | ||
await batch.FillAsync(FillRule.NonZero); | ||
|
||
await batch.TranslateAsync(Radius, Radius); | ||
await batch.RotateAsync(angle + Arc / 2); | ||
await batch.TextAlignAsync(TextAlign.Right); | ||
await batch.FillStyleAsync("#fff"); | ||
await batch.FontAsync("bold 30px sans-serif"); | ||
await batch.FillTextAsync(sector.Label, Radius - 10, 10); | ||
|
||
await batch.RestoreAsync(); | ||
} | ||
|
||
public async Task Spin(int numberOfTimes, bool shouldRandomizeNumberOfSpins = false) | ||
{ | ||
if (_isSpinning) | ||
{ | ||
return; | ||
} | ||
|
||
_isSpinning = true; | ||
|
||
var angleAbsolute = Mod(_angle, Math.Tau); | ||
|
||
_selectedSlotIndex = Random.Shared.Next(0, NumberOfSlots); | ||
var angleNew = Math.Tau - Arc * _selectedSlotIndex; | ||
angleNew -= Random.Shared.NextDouble(0, Arc); | ||
angleNew = Mod(angleNew, Math.Tau); | ||
|
||
var angleDifference = Mod(angleNew - angleAbsolute, Math.Tau); | ||
|
||
var revolutions = Math.Tau * (shouldRandomizeNumberOfSpins ? Random.Shared.Next(1, numberOfTimes + 1) : numberOfTimes); | ||
|
||
_angle += angleDifference + revolutions; | ||
|
||
var duration = Random.Shared.Next(4000, 5000); | ||
await _module.InvokeVoidAsync("spin", _canvasRef, _angle, duration, _selfReference); | ||
|
||
await SpinStarted.InvokeAsync(); | ||
} | ||
|
||
[JSInvokable] | ||
public async Task HandleAnimationFinish() | ||
{ | ||
_isSpinning = false; | ||
//_selectedSlotIndex = -1; | ||
|
||
var nameOfSelectedSlot = SlotNames[_selectedSlotIndex]; | ||
await SpinCompleted.InvokeAsync(nameOfSelectedSlot); | ||
} | ||
|
||
public async ValueTask DisposeAsync() | ||
{ | ||
_selfReference?.Dispose(); | ||
|
||
if (_module != null) | ||
{ | ||
await _module.InvokeVoidAsync("cancelAnimations", _canvasRef); | ||
await _module.DisposeAsync(); | ||
} | ||
|
||
if (_context != null) | ||
{ | ||
await _context.DisposeAsync(); | ||
} | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
src/Jimmys20.BlazorComponents/SpinningWheel/JmSpinningWheel.razor.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#wheel { | ||
display: block; | ||
border-radius: 50%; | ||
transform: rotate(-90deg); | ||
} |
15 changes: 15 additions & 0 deletions
15
src/Jimmys20.BlazorComponents/wwwroot/js/spinning-wheel.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
export function spin(canvas, angle, duration, componentInstance) { | ||
const spinAnimation = canvas.animate([{ rotate: `${angle}rad` }], { | ||
duration: duration, | ||
easing: 'cubic-bezier(0.2, 0, 0.1, 1)', | ||
fill: 'forwards' | ||
}); | ||
|
||
spinAnimation.addEventListener('finish', () => { | ||
componentInstance.invokeMethodAsync('HandleAnimationFinish'); | ||
}); | ||
} | ||
|
||
export function cancelAnimations(canvas) { | ||
canvas.getAnimations().forEach(animation => animation.cancel()); | ||
} |