-
Notifications
You must be signed in to change notification settings - Fork 621
Add touch/pointer event support to Blazor views and revamp sample app #3528
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
5d76338
Initial plan
Copilot 2494ea8
Add touch/pointer event support to Blazor SKCanvasView
Copilot b202acb
Add GC.SuppressFinalize in DisposeAsync per review feedback
Copilot 731c882
Remove accidentally committed nuget.exe and add .nuget/ to gitignore
Copilot 9ac847b
Add touch/pointer event support to Blazor SKGLView
Copilot 11f458f
Move touch event types to SkiaSharp.Views.Shared for MAUI/Blazor sync
Copilot a678a30
Make touch callback synchronous and fix TS formatting
Copilot e2462c8
Refactor touch interop to match existing Blazor interop patterns
Copilot 3ea113c
Always register touch interop with JS-side enabled toggle; update Bla…
Copilot f89b93c
Toggle touch via Start/Stop instead of enabled flag; defer until Enab…
Copilot f451965
Update base href in index.html
mattleibow 5c46f47
Fix JSObject disposal and implement Handled feedback loop
mattleibow 0e0be19
Fix wheel stopPropagation, deltaY=0 edge case, and double-start guard
mattleibow 6b18d05
Normalize wheel delta to v120 standard (120 = one mouse notch)
mattleibow 5b4095f
Revamp Blazor sample app to showcase library features
mattleibow a501ad8
Revamp Blazor sample app to showcase library features
mattleibow 571c060
Move inline styles to scoped .razor.css files
mattleibow bd4755c
Move @code to code-behind files and simplify demos
mattleibow 9bd00da
Move code back inline into .razor files
mattleibow 3d3e663
Use raw string literals for SkSL via code-behind files
mattleibow e697e8e
Move code back inline with @"" verbatim strings in .razor files
mattleibow f9d0e4d
Align GPU and Drawing pages with Home.razor code style
mattleibow 526bc69
colors
mattleibow 4c959be
Delete SkiaSharpSample.sln
mattleibow 9aba0c0
Guard SKTouchEventArgs with #if __MAUI__ || __BLAZOR__
mattleibow File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 |
|---|---|---|
|
|
@@ -316,3 +316,4 @@ fastlane/screenshots | |
|
|
||
| # Docs worktree (docs branch) | ||
| .docs | ||
| .nuget/ | ||
This file contains hidden or 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 hidden or 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 hidden or 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
158 changes: 158 additions & 0 deletions
158
samples/Basic/BlazorWebAssembly/SkiaSharpSample/Pages/Drawing.razor
This file contains hidden or 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,158 @@ | ||
| @page "/drawing" | ||
|
|
||
| @* Drawing Canvas Demo | ||
| Demonstrates SKCanvasView with touch/pointer events, wheel delta for | ||
| brush size control, and basic stroke rendering. | ||
| Shows EnableTouchEvents, Touch callback, SKTouchEventArgs, and | ||
| manual Invalidate() for on-demand rendering (no render loop). *@ | ||
|
|
||
| <h1>Drawing Canvas</h1> | ||
|
|
||
| <p> | ||
| Draw with touch or mouse. Use the scroll wheel to change brush size. | ||
| </p> | ||
|
|
||
| <div class="canvas-container border rounded p-2"> | ||
|
|
||
| <SKCanvasView @ref="skiaView" | ||
| OnPaintSurface="OnPaintSurface" IgnorePixelScaling="true" | ||
| Touch="OnTouch" EnableTouchEvents="true" /> | ||
|
|
||
| <div class="overlay-info"> | ||
| Brush: @(brushSize.ToString("F2"))px | ||
| </div> | ||
|
|
||
| <div class="overlay-toolbar"> | ||
| @foreach (var (name, color) in colors) | ||
| { | ||
| <div class="color-swatch @(currentColor == color ? "selected" : "")" | ||
| style="background:@name" | ||
| @onclick="() => SetColor(color)" /> | ||
| } | ||
| <button class="btn btn-outline-light btn-sm" @onclick="ClearCanvas">Clear</button> | ||
| </div> | ||
|
|
||
| </div> | ||
|
|
||
| @code { | ||
|
|
||
| SKCanvasView skiaView = null!; | ||
| float brushSize = 25f; | ||
| SKColor currentColor = SKColors.Black; | ||
| List<DrawingStroke> strokes = []; | ||
| DrawingStroke? currentStroke = null; | ||
| SKPoint lastPoint; | ||
|
|
||
| // The paint that is reused for drawing strokes. | ||
| readonly SKPaint strokePaint = new() | ||
| { | ||
| IsAntialias = true, | ||
| Style = SKPaintStyle.Stroke, | ||
| StrokeCap = SKStrokeCap.Round, | ||
| StrokeJoin = SKStrokeJoin.Round, | ||
| }; | ||
|
|
||
| // The available brush colors with their CSS names. | ||
| static readonly (string Name, SKColor Color)[] colors = | ||
| { | ||
| ("black", SKColors.Black), | ||
| ("red", SKColors.Red), | ||
| ("blue", SKColors.Blue), | ||
| ("green", SKColors.Green), | ||
| ("orange", SKColors.Orange), | ||
| ("purple", SKColors.Purple), | ||
| }; | ||
|
|
||
| void OnPaintSurface(SKPaintSurfaceEventArgs e) | ||
| { | ||
| var canvas = e.Surface.Canvas; | ||
|
|
||
| // Clear to white before drawing the strokes. | ||
| canvas.Clear(SKColors.White); | ||
|
|
||
| // Draw completed strokes. | ||
| foreach (var stroke in strokes) | ||
| { | ||
| strokePaint.Color = stroke.Color; | ||
| strokePaint.StrokeWidth = stroke.StrokeWidth; | ||
| canvas.DrawPath(stroke.Path, strokePaint); | ||
| } | ||
|
|
||
| // Draw in-progress stroke. | ||
| if (currentStroke is not null) | ||
| { | ||
| strokePaint.Color = currentStroke.Color; | ||
| strokePaint.StrokeWidth = currentStroke.StrokeWidth; | ||
| canvas.DrawPath(currentStroke.Path, strokePaint); | ||
| } | ||
|
|
||
| // Brush size indicator at last pointer position. | ||
| strokePaint.Color = SKColors.Gray; | ||
| strokePaint.StrokeWidth = 1; | ||
| canvas.DrawCircle(lastPoint, brushSize / 2f, strokePaint); | ||
| } | ||
|
|
||
| void OnTouch(SKTouchEventArgs e) | ||
| { | ||
| lastPoint = e.Location; | ||
|
|
||
| switch (e.ActionType) | ||
| { | ||
| case SKTouchAction.Pressed: | ||
| currentStroke = new DrawingStroke(new(), currentColor, brushSize); | ||
| currentStroke.Path.MoveTo(e.Location); | ||
| e.Handled = true; | ||
| break; | ||
|
|
||
| case SKTouchAction.Moved: | ||
| if (currentStroke is not null) | ||
| { | ||
| currentStroke.Path.LineTo(e.Location); | ||
| e.Handled = true; | ||
| } | ||
| break; | ||
|
|
||
| case SKTouchAction.Released: | ||
| if (currentStroke is not null) | ||
| { | ||
| strokes.Add(currentStroke); | ||
| currentStroke = null; | ||
| } | ||
| e.Handled = true; | ||
| break; | ||
|
|
||
| case SKTouchAction.WheelChanged: | ||
| brushSize += e.WheelDelta / 120f; | ||
| brushSize = Math.Clamp(brushSize, 1f, 50f); | ||
| e.Handled = true; | ||
| break; | ||
| } | ||
|
|
||
| skiaView.Invalidate(); | ||
| StateHasChanged(); | ||
| } | ||
|
|
||
| void SetColor(SKColor color) | ||
| { | ||
| currentColor = color; | ||
| skiaView.Invalidate(); | ||
| StateHasChanged(); | ||
| } | ||
|
|
||
| void ClearCanvas() | ||
| { | ||
| foreach (var stroke in strokes) | ||
| { | ||
| stroke.Path.Dispose(); | ||
| } | ||
| strokes.Clear(); | ||
| currentStroke?.Path.Dispose(); | ||
| currentStroke = null; | ||
| skiaView.Invalidate(); | ||
| StateHasChanged(); | ||
| } | ||
|
|
||
| // Represents a single drawing stroke with its path, color, and width. | ||
| // Path is owned — callers must dispose when removing strokes. | ||
| record DrawingStroke(SKPath Path, SKColor Color, float StrokeWidth = 25); | ||
| } | ||
47 changes: 47 additions & 0 deletions
47
samples/Basic/BlazorWebAssembly/SkiaSharpSample/Pages/Drawing.razor.css
This file contains hidden or 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,47 @@ | ||
| .canvas-container { | ||
| position: relative; | ||
| } | ||
|
|
||
| .overlay-info { | ||
| position: absolute; | ||
| top: 12px; | ||
| left: 50%; | ||
| transform: translateX(-50%); | ||
| z-index: 1; | ||
| background: rgba(0, 0, 0, 0.5); | ||
| border: 1px solid rgba(0, 0, 0, 0.4); | ||
| border-radius: 8px; | ||
| padding: 6px 16px; | ||
| backdrop-filter: blur(4px); | ||
| color: white; | ||
| font-size: 0.85rem; | ||
| font-weight: 500; | ||
| } | ||
|
|
||
| .overlay-toolbar { | ||
| position: absolute; | ||
| bottom: 16px; | ||
| left: 50%; | ||
| transform: translateX(-50%); | ||
| display: flex; | ||
| gap: 8px; | ||
| align-items: center; | ||
| z-index: 1; | ||
| background: rgba(0, 0, 0, 0.5); | ||
| border: 1px solid rgba(0, 0, 0, 0.4); | ||
| border-radius: 8px; | ||
| padding: 8px 16px; | ||
| backdrop-filter: blur(4px); | ||
| } | ||
|
|
||
| .color-swatch { | ||
| width: 26px; | ||
| height: 26px; | ||
| border-radius: 50%; | ||
| cursor: pointer; | ||
| border: 2px solid transparent; | ||
| } | ||
|
|
||
| .color-swatch.selected { | ||
| border-color: white; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.