Introduction
Feeling artistic or need to draw a masterpiece? With Blazor, you can now create your own digital canvas right there in your browser. In this article, we'll walk through the steps of building a signature/ paint app.
What you just witnessed in the demo above is made possible through Canvas. But what exactly is Canvas? It's an <HTML> element that allows dynamic graphics and brings drawing capabilities directly within the browser. So, let your inner artist out and get started.
Articles sections
Let's break down this article into four sections
- Canvas
- Color Picker
- Save Button
- Reset Button
1. Canvas
Alright, let's start by dropping a <canvas> element into our Blazor component. We'll also need to create a reference for it to play around with in JavaScript. You can hook up a reference for any HTML element using the @ref attribute.
<canvas @ref="canvas"></canvas>
@code {
private ElementReference canvas;
}
Code snippet 1. Canvas element
Next, we'll override the OnAfterRenderAsync method to ensure that the canvas is fully rendered on the browser before we proceed with any JavaScript operations. Here, we're calling a JavaScript funtion initializeCanvas. This function initializes our canvas for drawing.
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("initializeCanvas", canvas, selectedColor);
}
}
Code snippet 2. OnAfterRenderAsync
Now, let's set up a JavaScript file to host this function. So, go ahead and drop a fresh JS file into the mix, naming it whatever suits your vibe, like "Signature.js". Just make sure to stash it under the wwwroot folder. Then, go over to Index.razor and add a <script> tag under the <head> tag, like this.
<script src="signature.js" type="text/javascript"></script>
Code snippet 3. JavaScript script tag
InitializeCanvas JS function
Now, let's add the "initializeCanvas" function to JavaScript.
Its purpose? Well, it's all about enabling users to create signatures or objects on the canvas. When the user presses down the mouse and drags, a line is drawn. Then, once the user releases the mouse, the line drawing is complete.
initializeCanvas = (canvas) => {
const ctx = canvas.getContext('2d');
canvas.addEventListener('mousedown', (e) => {
ctx.beginPath();
isMouseDown = true;
});
canvas.addEventListener('mousemove', (e) => {
if (isMouseDown) {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
ctx.lineTo(x, y);
ctx.stroke();
}
});
canvas.addEventListener('mouseup', (e) => {
isMouseDown = false;
});
};
Code snippet 4. JS method initializeCanvas
Explanation of code snippet 4: First up, we grab hold of the 2D pane using the getContext() method. Then, we toss in a mousedown listener to catch that initial click on the canvas. As the mouse moves, we track its coordinates relative to the canvas with the mouse move listener. With this intel, we call the lineTo() function to sketch out the line, followed by we call the stroke() function to add the color. Finally, we make use of the mouseup listener to figure out if the mouse has gone up; with all that, we wrap up drawing a line. Smooth, huh?
2. Color picker
let's shift our focus to enhancing our lines with colors.
Returning to our Razor file, simply add <input> tag and set its type to "color".
<input type="color"
@bind="selectedColor"
@oninput="ChangeColor" />
Code snippet 5. Color input tag
We've applied two important attributes here
- @bind="selectedColor": This attribute establishes a two-way data binding to the "selectedColor" property. In simple terms, any changes made to the color picker will automatically update the "selected color" property and vice versa.
- @oninput="ChangeColor": This attribute specifies the event handler method to be called when the input value changes.
private string selectedColor = "#000000"; // Default color is black
private async Task ChangeColor(ChangeEventArgs e)
{
selectedColor = e.Value.ToString();
await JSRuntime.InvokeVoidAsync("changeCanvasColor", canvas, selectedColor);
}
Code snippet 6. Bound color and method of Color picker
In this code snippet 6, we've declared a "selectedColor" property. The "ChangeColor" method is invoked whenever the color picker value changes. It updates the "selectedColor" property with the new value selected by the user. Then, we need to invoke a JavaScript function named "changeCanvasColor," passing along the canvas reference and the newly selected color. This JavaScript method will update the stroke color on the canvas accordingly. Go ahead and add JS funtion to signature.js as follows.
changeCanvasColor = (canvas, color) => {
const ctx = canvas.getContext('2d');
ctx.strokeStyle = color;
};
Code snippet 7. JavaScript - changeCanvasColor function
3. Save button
After putting in all that hard work to create your masterpiece, it's only natural that you'd want to preserve it for display. That's where the save button comes into play. When clicked, it captures a snapshot of the canvas and downloads it directly to your downloads folder.
<button class="savebutton-style" @onclick="SaveSignature">Save</button>
Code snippet 8. HTML Save button
private async Task SaveSignature()
{
// Get the canvas data URL
string imageData = await JSRuntime.InvokeAsync<string>("getSignatureImage", canvas);
// Trigger file download
await JSRuntime.InvokeVoidAsync("downloadFile", imageData, "signature.png");
}
Code snippet 9. C# Save button triggering
When you click the save button, two JavaScript methods are triggered. Firstly, the "getSignatureImage" function reads your masterpiece from the canvas. Secondly, the "downloadFile" method is invoked to actually download that masterpiece into your downloads folder.
In the world of JavaScript, we'll add two methods, "getSignatureImage" and "download file"
getSignatureImage = (canvas) => {
return canvas.toDataURL();
};
downloadFile = (dataURL, filename) => {
// Convert base64 to Blob
const byteString = atob(dataURL.split(',')[1]);
const mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0];
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
const blob = new Blob([ab], { type: mimeString });
// Create anchor element and trigger download
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = filename;
link.click();
};
Code snippet 10. JS Save button methods
The "getSignatureImage" function is a straightforward one; it simply calls "toDataURL" to capture the current snapshot of the canvas.
The real magic happens in the "downloadFile" method. This function transforms the canvas pixels into a byte string, creates a blob, assigns it a name, and then saves it in your downloads folder.
4. Reset button
so what if your painting is pixel perfect, you can always start fresh by resetting the canvas. Just add a button in HTML and call js function clearances
Simply add a button in your component and call the JavaScript function "clearCanvas" to wipe the slate clean.
<button class="resetbutton-style" @onclick="ResetSignature">Reset</button>
private async Task ResetSignature()
{
await JSRuntime.InvokeVoidAsync("clearCanvas", canvas);
imageData = null;
}
Code snippet 10. HTML and C# code to reset the canvas
In JS, create the function clearCanvas, which simply clears the entire canvas, you need to pass the top-left and bottom-right coordinate to specify the canvas size.
clearCanvas = (canvas) => {
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
};
Code snippet 11. JS to reset the canvas
Conclusion
Congratulations! You've successfully built a signature/paint app using Blazor. You've learned how to set up a canvas, integrate a color picker, enable saving functionality, and implement a reset feature. The versatility of the canvas extends beyond this app – it's capable of serving numerous purposes. Consider this app as a starting point for its capabilities.
You can download the complete code, including all CSS files and everything needed to run the app, from the attached file.
You'll find the same code available on my GitHub repository, you can find many more projects in my main repo Blazor Simplified. Happy coding!