Skip to content

Commit 977c4d6

Browse files
Copilotmattleibow
andcommitted
Add new Blazor sample pages from PR #3528 (sample changes only)
Co-authored-by: mattleibow <1096616+mattleibow@users.noreply.github.com>
1 parent 4818029 commit 977c4d6

File tree

8 files changed

+511
-104
lines changed

8 files changed

+511
-104
lines changed

samples/Basic/BlazorWebAssembly/SkiaSharpSample/Layout/MainLayout.razor.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22
position: relative;
33
display: flex;
44
flex-direction: column;
5+
height: 100vh;
6+
overflow: hidden;
57
}
68

79
main {
810
flex: 1;
11+
display: flex;
12+
flex-direction: column;
13+
min-height: 0;
914
}
1015

1116
.sidebar {

samples/Basic/BlazorWebAssembly/SkiaSharpSample/Layout/NavMenu.razor

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@
1111
<nav class="flex-column">
1212
<div class="nav-item px-3">
1313
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
14-
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> SkiaSharp
14+
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
1515
</NavLink>
1616
</div>
1717
<div class="nav-item px-3">
18-
<NavLink class="nav-link" href="raster">
19-
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Raster Canvas
18+
<NavLink class="nav-link" href="gpu">
19+
<span class="bi bi-gpu-card-nav-menu" aria-hidden="true"></span> GPU
2020
</NavLink>
2121
</div>
2222
<div class="nav-item px-3">
23-
<NavLink class="nav-link" href="gpu">
24-
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> GPU Canvas
23+
<NavLink class="nav-link" href="drawing">
24+
<span class="bi bi-pencil-nav-menu" aria-hidden="true"></span> Drawing
2525
</NavLink>
2626
</div>
2727
</nav>

samples/Basic/BlazorWebAssembly/SkiaSharpSample/Layout/NavMenu.razor.css

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@
2525
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
2626
}
2727

28-
.bi-plus-square-fill-nav-menu {
29-
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
28+
.bi-gpu-card-nav-menu {
29+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-gpu-card' viewBox='0 0 16 16'%3E%3Cpath d='M4 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0m7.5-1.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3'/%3E%3Cpath d='M0 1.5A.5.5 0 0 1 .5 1h1a.5.5 0 0 1 .5.5V4h13.5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-.5.5H2v2.5a.5.5 0 0 1-1 0V2H.5a.5.5 0 0 1-.5-.5m5.5 4a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5M9 8a2.5 2.5 0 1 0 5 0 2.5 2.5 0 0 0-5 0'/%3E%3Cpath d='M3 12.5h3.5v1a.5.5 0 0 1-.5.5H3.5a.5.5 0 0 1-.5-.5zm4 0h4v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
3030
}
3131

32-
.bi-list-nested-nav-menu {
33-
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
32+
.bi-pencil-nav-menu {
33+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-pencil-fill' viewBox='0 0 16 16'%3E%3Cpath d='M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.5.5 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11z'/%3E%3C/svg%3E");
3434
}
3535

3636
.nav-item {
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
@page "/drawing"
2+
3+
@* Drawing Canvas Demo
4+
Demonstrates SKCanvasView with pointer events, wheel delta for
5+
brush size control, and basic stroke rendering.
6+
Shows multi-stroke drawing with color picker and on-demand rendering
7+
via manual Invalidate() (no render loop). *@
8+
9+
<h1>Drawing Canvas</h1>
10+
11+
<p>
12+
Draw with touch or mouse. Use the scroll wheel to change brush size.
13+
</p>
14+
15+
<div class="canvas-container border rounded p-2">
16+
17+
<SKCanvasView @ref="skiaView"
18+
OnPaintSurface="OnPaintSurface" IgnorePixelScaling="true"
19+
@onpointerdown="OnPointerDown" @onpointermove="OnPointerMove" @onpointerup="OnPointerUp"
20+
@onwheel="OnWheel" @onwheel:preventDefault="true" />
21+
22+
<div class="overlay-info">
23+
Brush: @(brushSize.ToString("F2"))px
24+
</div>
25+
26+
<div class="overlay-toolbar">
27+
@foreach (var (name, color) in colors)
28+
{
29+
<div class="color-swatch @(currentColor == color ? "selected" : "")"
30+
style="background:@name"
31+
@onclick="() => SetColor(color)" />
32+
}
33+
<button class="btn btn-outline-light btn-sm" @onclick="ClearCanvas">Clear</button>
34+
</div>
35+
36+
</div>
37+
38+
@code {
39+
40+
SKCanvasView skiaView = null!;
41+
float brushSize = 25f;
42+
SKColor currentColor = SKColors.Black;
43+
List<DrawingStroke> strokes = [];
44+
DrawingStroke? currentStroke = null;
45+
SKPoint lastPoint;
46+
47+
// The paint that is reused for drawing strokes.
48+
readonly SKPaint strokePaint = new()
49+
{
50+
IsAntialias = true,
51+
Style = SKPaintStyle.Stroke,
52+
StrokeCap = SKStrokeCap.Round,
53+
StrokeJoin = SKStrokeJoin.Round,
54+
};
55+
56+
// The available brush colors with their CSS names.
57+
static readonly (string Name, SKColor Color)[] colors =
58+
{
59+
("black", SKColors.Black),
60+
("red", SKColors.Red),
61+
("blue", SKColors.Blue),
62+
("green", SKColors.Green),
63+
("orange", SKColors.Orange),
64+
("purple", SKColors.Purple),
65+
};
66+
67+
void OnPaintSurface(SKPaintSurfaceEventArgs e)
68+
{
69+
var canvas = e.Surface.Canvas;
70+
71+
// Clear to white before drawing the strokes.
72+
canvas.Clear(SKColors.White);
73+
74+
// Draw completed strokes.
75+
foreach (var stroke in strokes)
76+
{
77+
strokePaint.Color = stroke.Color;
78+
strokePaint.StrokeWidth = stroke.StrokeWidth;
79+
canvas.DrawPath(stroke.Path, strokePaint);
80+
}
81+
82+
// Draw in-progress stroke.
83+
if (currentStroke is not null)
84+
{
85+
strokePaint.Color = currentStroke.Color;
86+
strokePaint.StrokeWidth = currentStroke.StrokeWidth;
87+
canvas.DrawPath(currentStroke.Path, strokePaint);
88+
}
89+
90+
// Brush size indicator at last pointer position.
91+
strokePaint.Color = SKColors.Gray;
92+
strokePaint.StrokeWidth = 1;
93+
canvas.DrawCircle(lastPoint, brushSize / 2f, strokePaint);
94+
}
95+
96+
void OnPointerDown(PointerEventArgs e)
97+
{
98+
if (!e.IsPrimary)
99+
return;
100+
lastPoint = new SKPoint((float)e.OffsetX, (float)e.OffsetY);
101+
currentStroke = new DrawingStroke(new(), currentColor, brushSize);
102+
currentStroke.Path.MoveTo(lastPoint);
103+
skiaView.Invalidate();
104+
}
105+
106+
void OnPointerMove(PointerEventArgs e)
107+
{
108+
if (!e.IsPrimary)
109+
return;
110+
lastPoint = new SKPoint((float)e.OffsetX, (float)e.OffsetY);
111+
if (currentStroke is not null)
112+
currentStroke.Path.LineTo(lastPoint);
113+
skiaView.Invalidate();
114+
}
115+
116+
void OnPointerUp(PointerEventArgs e)
117+
{
118+
if (!e.IsPrimary)
119+
return;
120+
if (currentStroke is not null)
121+
{
122+
strokes.Add(currentStroke);
123+
currentStroke = null;
124+
skiaView.Invalidate();
125+
}
126+
}
127+
128+
void OnWheel(WheelEventArgs e)
129+
{
130+
// DeltaY > 0 = scroll down = decrease brush size; DeltaY < 0 = scroll up = increase.
131+
// Normalize ~100 CSS pixels per notch to ~1px brush change per notch.
132+
brushSize -= (float)(e.DeltaY / 100.0);
133+
brushSize = Math.Clamp(brushSize, 1f, 50f);
134+
StateHasChanged();
135+
}
136+
137+
void SetColor(SKColor color)
138+
{
139+
currentColor = color;
140+
skiaView.Invalidate();
141+
StateHasChanged();
142+
}
143+
144+
void ClearCanvas()
145+
{
146+
foreach (var stroke in strokes)
147+
{
148+
stroke.Path.Dispose();
149+
}
150+
strokes.Clear();
151+
currentStroke?.Path.Dispose();
152+
currentStroke = null;
153+
skiaView.Invalidate();
154+
StateHasChanged();
155+
}
156+
157+
// Represents a single drawing stroke with its path, color, and width.
158+
// Path is owned — callers must dispose when removing strokes.
159+
record DrawingStroke(SKPath Path, SKColor Color, float StrokeWidth = 25);
160+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
.canvas-container {
2+
position: relative;
3+
}
4+
5+
.overlay-info {
6+
position: absolute;
7+
top: 12px;
8+
left: 50%;
9+
transform: translateX(-50%);
10+
z-index: 1;
11+
background: rgba(0, 0, 0, 0.5);
12+
border: 1px solid rgba(0, 0, 0, 0.4);
13+
border-radius: 8px;
14+
padding: 6px 16px;
15+
backdrop-filter: blur(4px);
16+
color: white;
17+
font-size: 0.85rem;
18+
font-weight: 500;
19+
}
20+
21+
.overlay-toolbar {
22+
position: absolute;
23+
bottom: 16px;
24+
left: 50%;
25+
transform: translateX(-50%);
26+
display: flex;
27+
gap: 8px;
28+
align-items: center;
29+
z-index: 1;
30+
background: rgba(0, 0, 0, 0.5);
31+
border: 1px solid rgba(0, 0, 0, 0.4);
32+
border-radius: 8px;
33+
padding: 8px 16px;
34+
backdrop-filter: blur(4px);
35+
}
36+
37+
.color-swatch {
38+
width: 26px;
39+
height: 26px;
40+
border-radius: 50%;
41+
cursor: pointer;
42+
border: 2px solid transparent;
43+
}
44+
45+
.color-swatch.selected {
46+
border-color: white;
47+
}

0 commit comments

Comments
 (0)