I could reproduce the same issue:
The above comment by @enet is right. You should at least check whether the firstRender is true and avoid attaching the draw() function to a new timer each time.
Apart from that, you should create a Timer as a separated field. And don't forget to DISPOSE the Timer when the component is disposed.
Finally, there's another issue within your code: You didn't invoke the _context.EndBatchAsync(). As a result, it will NOT draw anything.
To fix the above issues, change your code as below:
...
@implements IDisposable @* A Timer is a resource that should be disposed *@
...
@code{
private Canvas2DContext _context;
protected BECanvasComponent _canvasReference;
private Timer _aTimer = new Timer(500);
public void Dispose()
{
this._aTimer.Dispose();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
this._context = await this._canvasReference.CreateCanvas2DAsync();
var centerX = _canvasReference.Width / 2;
var centerY = _canvasReference.Height / 2;
var radius = 80;
var full = radius * 2;
var amount = 0.0;
var amountToIncrease = 0.1;
if(firstRender)
{
// you might decide to put the above `var amount = 0.0; ...` here too, it depends on your needs
this._aTimer.Elapsed += draw;
this._aTimer.AutoReset = true;
this._aTimer.Enabled = true;
}
async void draw(Object source, ElapsedEventArgs e)
{
await this._context.BeginBatchAsync();
await this._context.ArcAsync(centerX, centerY, radius, 0, amount * Math.PI, false);
await this._context.SetFillStyleAsync("#13a8a4");
await this._context.FillAsync();
await this._context.SetLineWidthAsync(10);
await this._context.SetStrokeStyleAsync("#000000");
await this._context.StrokeAsync();
await this._context.EndBatchAsync(); // add this line !
amount += amountToIncrease;
if (amount > full) amount = 0; // restart
await InvokeAsync(() => { StateHasChanged(); });
}
}
}
Demo
[Edit]
i am wondering why there are those lines inside the circle?
Those lines are the part of closed path last time. Actually, there's a beginPath api that starts a new path by emptying the list of sub-paths.( See docs on MDN). In other words, if you don't need them, invoke _context.BeginPathAsync(); :
await this._context.BeginBatchAsync();
await this._context.BeginPathAsync(); // add this line
await this._context.ArcAsync(centerX, centerY, radius, 0, amount * Math.PI, false);
...
[Demo2] :
By the way, you don't have to notify the state has changed here (because you didn't change the component state):
//await InvokeAsync(() => { StateHasChanged(); });