Skip to content

Add SKTouchCanvasView Blazor library with touch/pointer event support#364

Closed
Copilot wants to merge 10 commits intomainfrom
copilot/fix-dragging-not-drawing
Closed

Add SKTouchCanvasView Blazor library with touch/pointer event support#364
Copilot wants to merge 10 commits intomainfrom
copilot/fix-dragging-not-drawing

Conversation

Copy link
Contributor

Copilot AI commented Feb 26, 2026

  • Add SKTouchCanvasView Blazor library with touch/pointer support
  • Fix touch drawing bugs from Add SKTouchCanvasView Blazor library with touch event support #357
  • Address review feedback (CSS, auto-invalidate, TypeScript, no container)
  • Use Microsoft.TypeScript.MSBuild; keep .js and .map in source control
  • Fix touch coordinate scaling in sample app — multiply touch coords by DPI so lines draw where the user touches

Touch page with DPI scaling


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

…ing and navigation

- Copy all source files from PR #357: SKTouchCanvasView component, touch types,
  JS interop, tests, Touch demo page, nav menu updates, solution/filter updates
- Fix touch/mouse dragging not drawing: auto-invalidate canvas after handled
  touch events in SKTouchCanvasView.OnPointerEvent
- Fix ResizeObserver error on navigation: don't manually dispose inner
  SKCanvasView (let Blazor manage its lifecycle)
- Add setPointerCapture on pointerdown for reliable drag tracking

Co-authored-by: mattleibow <1096616+mattleibow@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix dragging issue that prevents line drawing Add SKTouchCanvasView Blazor library with touch drawing fixes Feb 26, 2026
github-actions bot pushed a commit that referenced this pull request Feb 26, 2026
Copy link
Collaborator

@mattleibow mattleibow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot please fix the issues

</div>
</div>

<style>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot can you move the css into a separate file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to Touch.razor.css in 026c1f8.

// changes made by the handler are rendered immediately.
if (args.Handled)
{
_skCanvasView?.Invalidate();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot this must be in app code and not the library as the user may just track and not update.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed auto-invalidate from the library. The demo app now calls _canvasView?.Invalidate() directly in its OnTouch handler. Fixed in 026c1f8.

// Handles pointer events on the SkiaSharp canvas and forwards them to .NET
// using the same event model as the MAUI SKTouchEventArgs API.

export function initializeTouchEvents(element, dotNetRef) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Please use typescript

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Converted to TypeScript (SKTouchInterop.ts). The compiled .js is generated via tsc and the .ts/tsconfig.json are excluded from static web assets in the csproj. Fixed in 026c1f8.

@inherits ComponentBase
@implements IAsyncDisposable

<div @ref="_containerRef"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot is there a way to do this without the container? For layout, we can do it in the app razor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplified — removed the layout-affecting Style/Class parameters and the position: relative/position: absolute styling. The wrapper div now only sets touch-action: none; user-select: none for pointer event capture. We still need a minimal element to get the @ref for JS interop since SKCanvasView doesn't expose its ElementReference. Layout is fully controlled by the app. Fixed in 026c1f8.

…eScript, simplify container

- Move Touch.razor inline CSS to separate Touch.razor.css file
- Remove auto-invalidate from library OnPointerEvent; app calls Invalidate() directly
- Convert SKTouchInterop.js to TypeScript (SKTouchInterop.ts) with proper types
- Simplify container div: removed Style/Class parameters, use minimal touch-action wrapper
- Properly remove event listeners in disposeTouchEvents instead of relying on clone trick

Co-authored-by: mattleibow <1096616+mattleibow@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 26, 2026

@copilot please fix the issues

All four review issues addressed in 026c1f8:

  • CSS extracted to Touch.razor.css
  • Auto-invalidate removed from library, moved to app code
  • JS converted to TypeScript
  • Container simplified (no layout styling, just touch-action for pointer capture)

Touch page after drawing

@mattleibow
Copy link
Collaborator

@copilot please look ath the SKCanvasView razor: https://github.com/mono/SkiaSharp/blob/main/source/SkiaSharp.Views/SkiaSharp.Views.Blazor/SKCanvasView.razor

<canvas @ref="htmlCanvas" @attributes="AdditionalAttributes" />

And then we use

es2015 es2015 True

in the msbuild proj instead of a tsconfig file.

Are we producing a .map file in additin to the .js?

Copilot AI and others added 4 commits February 26, 2026 11:59
…lement from JS

Since upstream SKCanvasView splatts AdditionalAttributes directly onto its
<canvas> element, we pass a data-sk-touch-id marker through and have JS
find the element by attribute selector. This eliminates the wrapper div
entirely — no extra DOM nodes, layout fully controlled by the app.

JS now sets touch-action:none and user-select:none directly on the canvas.

Co-authored-by: mattleibow <1096616+mattleibow@users.noreply.github.com>
…pts/

- Add Microsoft.TypeScript.MSBuild (5.7.3) to compile .ts during dotnet build
- Move SKTouchInterop.ts from wwwroot/ to Scripts/ (source directory)
- Move tsconfig.json to project root with outDir=wwwroot, rootDir=Scripts
- Delete pre-compiled .js — now generated automatically during build
- Add generated wwwroot/*.js to .gitignore

Co-authored-by: mattleibow <1096616+mattleibow@users.noreply.github.com>
….MSBuild

Microsoft.TypeScript.MSBuild conflicts with Razor SDK static web asset
discovery in multi-target builds. Use a simple Exec target with
Inputs/Outputs for incremental compilation instead.

Also fix MergedAttributes to set internal data-sk-touch-id after user
attributes so it cannot be overwritten.

Co-authored-by: mattleibow <1096616+mattleibow@users.noreply.github.com>
Co-authored-by: mattleibow <1096616+mattleibow@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 26, 2026

@copilot please look ath the SKCanvasView razor: https://github.com/mono/SkiaSharp/blob/main/source/SkiaSharp.Views/SkiaSharp.Views.Blazor/SKCanvasView.razor

...

Done in 5a76897433ca3e. Since the upstream SKCanvasView.razor is just <canvas @ref="htmlCanvas" @attributes="AdditionalAttributes" />, I removed the wrapper div entirely. Our SKTouchCanvasView.razor is now just:

<SkiaSharp.Views.Blazor.SKCanvasView
    @ref="_skCanvasView"
    OnPaintSurface="OnPaintSurfaceInternal"
    EnableRenderLoop="@EnableRenderLoop"
    IgnorePixelScaling="@IgnorePixelScaling"
    @attributes="MergedAttributes" />

A data-sk-touch-id GUID is passed through AdditionalAttributes → the <canvas> element, and JS finds it via querySelector. We need the GUID because @ref="_skCanvasView" gives a C# component reference (useful for Invalidate()/Dpi), not a DOM ElementReference — and SKCanvasView.htmlCanvas is a private field with no public accessor. JS sets touch-action: none / user-select: none directly on the canvas.

Also: deleted the pre-compiled .js, moved .ts to Scripts/, and added an MSBuild Exec target so dotnet build compiles TypeScript automatically (with Inputs/Outputs for incremental builds). Generated JS is gitignored.

…s and .map in source control

- Replace custom Exec target with Microsoft.TypeScript.MSBuild 5.7.3 (PrivateAssets=all)
- Move SKTouchInterop.ts from Scripts/ back to wwwroot/ (matching upstream convention)
- Remove tsconfig.json; use TypeScript MSBuild properties in csproj instead
- Enable source maps (TypeScriptSourceMap=True)
- Remove .gitignore entry — .js and .js.map are now committed to source control
- All 3 files (.ts, .js, .js.map) live side-by-side in wwwroot/ like upstream SkiaSharp

Co-authored-by: mattleibow <1096616+mattleibow@users.noreply.github.com>
Copilot AI changed the title Add SKTouchCanvasView Blazor library with touch drawing fixes Add SKTouchCanvasView Blazor library with touch/pointer event support Feb 26, 2026
@mattleibow
Copy link
Collaborator

@copilot the touches are not in the same place as the lines. If I touch the bottom right, the lines are drawn in the upper left region. Basically the content is not scaled. We don't want to do scaling in the canvas view, so we need to apply scaling in the app.

…draw where the user touches

Touch events from JS arrive in CSS pixels, but the SkiaSharp canvas surface
renders at device pixel ratio when IgnorePixelScaling is false (default).
The app now scales touch coordinates, stroke widths, and legend elements by
DPI. This is done in the app per the reviewer's guidance — the library
should not apply scaling.

Co-authored-by: mattleibow <1096616+mattleibow@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 26, 2026

@copilot the touches are not in the same place as the lines. If I touch the bottom right, the lines are drawn in the upper left region. Basically the content is not scaled. We don't want to do scaling...

Fixed in 15b7452. The sample app now scales touch coordinates by _canvasView.Dpi before recording them, and scales stroke widths + legend elements in OnPaintSurface by the same factor. No changes to the library — scaling is purely in app code.

Touch page with DPI scaling

github-actions bot pushed a commit that referenced this pull request Feb 26, 2026
github-actions bot pushed a commit that referenced this pull request Feb 26, 2026
@mattleibow mattleibow closed this Feb 28, 2026
@mattleibow mattleibow deleted the copilot/fix-dragging-not-drawing branch February 28, 2026 03:05
github-actions bot pushed a commit that referenced this pull request Feb 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants