Blazor WebAssembly Timer Example (Analog Clock)

Here is an example that uses the timer (System.Threading.Timer) to set an analog clock. It runs as client-side Blazor WASM code. This code is compatible with .NET Core, .NET 5 and later (.NET 6).

All measurements are based off of percentages so it will scale to practically any size.

For an example of the Timer class refreshing data from a WebApi on the screen, see this article.

Here is the clock specific CSS code that should be added to the Blazor WASM app.

.clock-container {
	background-color: midnightblue;
	display: flex;
	justify-content: center;
	align-items: center;
	width: 100%;
	min-height: 75vmin;
}

.clock {
	position: relative;
	overflow: hidden;
	background-color: inherit;
	height: 70vmin;
	width: 70vmin;
	border-radius: 50%;
	box-shadow: 0 -12px 12px rgba(255,255,255,.1),
				inset 0 -12px 12px rgba(255,255,255,.1),
				0 12px 12px rgba(0,0,0,.1),
				inset 0 12px 12px rgba(0,0,0,.1);
}
	.clock div {
		position: absolute;
		top: 0;
		left: 0;
		height: 100%;
		width: 100%;
		background-color: transparent;
	}
		.clock div div {
			left: 50%;
			width: 0;
		}
	.clock span {
		position: absolute;
		font-family: Arial;
		font-size: 5vmin;
		font-weight: bold;
		color: lime;
	}
	.clock .h12 {
		left: 50%;
		top: 3%;
		transform: translateX(-50%);
	}
	.clock .h12::before {
		content: "12";
	}
	.clock .h3 {
		left: 97%;
		top: 50%;
		transform: translate(-100%, -50%);
	}
	.clock .h3::before {
		content: "3";
	}
	.clock .h6 {
		left: 50%;
		top: 97%;
		transform: translate(-50%, -100%);
	}
	.clock .h6::before {
		content: "6";
	}
	.clock .h9 {
		left: 3%;
		top: 50%;
		transform: translateY(-50%);
	}
	.clock .h9::before {
		content: "9";
	}
	.clock .ctr {
		width: 3%;
		height: 3%;
		border-radius: 50%;
		left: 50%;
		top: 50%;
		transform: translate(-50%, -50%);
		background-color: wheat;
	}

.clock .hour div {
	top: 20%;
	height: 30%;
	border: 2px solid wheat;
	margin-left: -2px;
}

.clock .minute div {
	top: 10%;
	height: 40%;
	border: 2px solid wheat;
	margin-left: -2px;
}

.clock .second div {
	top: 5%;
	height: 65%;
	border: 1px solid red;
	margin-left: -1px;
}

Here is the Index.razor page that uses the Timer. It needs to call StateHasChanged() because there is no user-fired event.

@page "/"

<div class="clock-container">
	<div class="clock">
		<span class="h12"></span>
		<span class="h3"></span>
		<span class="h6"></span>
		<span class="h9"></span>
		<div class="hour" style="transform:rotate(@(hr)deg);"><div></div></div>
		<div class="minute" style="transform:rotate(@(min)deg);"><div></div></div>
		<div class="second" style="transform:rotate(@(sec)deg);"><div></div></div>
		<span class="ctr"></span>
	</div>
</div>

@code {
	System.Threading.Timer timer;
	double hr, min, sec;

	// NOTE: this math can be simplified!!!
	private void SetClock(object stateInfo)
	{
		var time = DateTime.Now;
		hr = 360.0 * time.Hour / 12 + 30.0 * time.Minute / 60.0;
		min = 360.0 * time.Minute / 60 + 6.0 * time.Second / 60.0;
		sec = 360.0 * time.Second / 60 + 6.0 * time.Millisecond / 1000.0;
		StateHasChanged(); // MUST CALL StateHasChanged() BECAUSE THIS IS TRIGGERED BY A TIMER INSTEAD OF A USER EVENT
	}

	protected override async Task OnInitializedAsync()
	{
		await base.OnInitializedAsync();

		SetClock(null);

		timer = new System.Threading.Timer(SetClock, new System.Threading.AutoResetEvent(false), 0, 10); // 10 milliseconds
	}
}

Here is the same code for Index.razor that has been slightly modified to work with Blazor Server (SignalR, not WASM). It is very taxing on the server and this cannot be a recommended solution. It certainly will not scale.

@page "/"

<div class="clock-container">
	<div class="clock">
		<span class="h12"></span>
		<span class="h3"></span>
		<span class="h6"></span>
		<span class="h9"></span>
		<div class="hour" style="transform:rotate(@(hr)deg);"><div></div></div>
		<div class="minute" style="transform:rotate(@(min)deg);"><div></div></div>
		<div class="second" style="transform:rotate(@(sec)deg);"><div></div></div>
		<span class="ctr"></span>
	</div>
</div>

@code {
	System.Threading.Timer timer;
	double hr, min, sec;

	// NOTE: this math can be simplified!!!
	private void SetClock(object stateInfo)
	{
		var time = DateTime.Now;
		hr = 360.0 * time.Hour / 12 + 30.0 * time.Minute / 60.0;
		min = 360.0 * time.Minute / 60 + 6.0 * time.Second / 60.0;
		sec = 360.0 * time.Second / 60 + 6.0 * time.Millisecond / 1000.0;
		InvokeAsync(() => StateHasChanged()); // MUST CALL StateHasChanged() BECAUSE THIS IS TRIGGERED BY A TIMER INSTEAD OF A USER EVENT
	}
	protected override void OnAfterRender(bool firstRender)
	{
		base.OnAfterRender(firstRender);

		SetClock(null);

		timer = new System.Threading.Timer(SetClock, new System.Threading.AutoResetEvent(false), 0, 1000);

	}
}

Coding Video

https://youtu.be/9YXVZru1Khw


Cookies are simple text files stored on the user's computer. They are used for adding features and security to this site.
OK