18 KiB
Canvas Interaction Improvements
This document tracks the ongoing improvements to Node-RED's canvas interaction across different devices, input methods, and browser zoom settings.
Objectives
Improve canvas interaction to work consistently and intuitively across:
- Browser zoom levels: 100%, 125%, 150%, 200%, etc.
- Input devices: Mouse, trackpad, and touchscreen
- Platforms: Desktop (Windows, macOS, Linux) and mobile/tablet devices
What Has Been Implemented
Zoom Button & Hotkey Enhancements (Session 2)
Zoom-to-Fit Feature (commits: 6f164a8, 788d0a3, ed71cf9, 732c828)
- ✅ New zoom-to-fit button in footer toolbar
- ✅ Shows all nodes with padding, properly centered in viewport
- ✅ Keyboard shortcut: Ctrl+1 / Cmd+1 for zoom-to-fit
- ✅ Respects maximum zoom level (won't zoom in beyond 1.0)
- ✅ Works immediately on page load (refreshes active nodes first)
- ✅ Smooth animated pan when zoom doesn't need to change
- ✅ Pan animation duration matches zoom animation (200-350ms)
Smooth Zoom Button Animation (commits: 935ff62, f07907e)
- ✅ Smooth 200-350ms zoom transitions for button/hotkey zoom
- ✅ Performance optimized: only updates transforms during animation
- ✅ No viewport jumping or drift
- ✅ Focal point locking for sequential zooms (1 second timeout)
- ✅ Maintains viewport center across multiple rapid button presses
Dynamic Zoom Animation Duration (commit: 594c0d6)
- ✅ Animation duration scales with zoom distance (logarithmic)
- ✅ Consistent perceived velocity across all zoom levels
- ✅ Range: 200-350ms (faster for small changes, slower for large)
- ✅ Pan-only animations also scale with distance traveled
- ✅ Reference: doubling/halving zoom takes ~250ms
Visual Feedback Enhancements (commits: 6261213, ce5a031, 0247a91)
- ✅ Grab cursor (open hand) when spacebar pressed
- ✅ Grabbing cursor (closed hand) during active pan
- ✅ Works for spacebar+click and middle-click pan modes
- ✅ Minimap auto-shows during zoom button/hotkey operations
- ✅ Minimap auto-shows during zoom-to-fit and zoom reset
Zoom Limits (commit: 45c2a79)
- ✅ Maximum zoom level set to 1.0 (100%, no zooming in beyond 1:1)
- ✅ All zoom methods respect this limit
- ✅ Zoom-to-fit properly clamps to max zoom
Bug Fixes (commits: be1da36, 7fdff0c, 6e49e96, e3de29d)
- ✅ Fixed grey padding at canvas bottom (SVG margin reset)
- ✅ Fixed zoom button direction (were reversed)
- ✅ Fixed viewport drift without focal point
- ✅ Fixed zoom center calculation consistency
Zoom Functionality
Smooth Zoom Animation (commits: bdfa06b, a12b65b)
- ✅ 125ms smooth zoom transitions with ease-out curves
- ✅ Natural acceleration/deceleration for zoom operations
- ✅ Reduced acceleration from 2x to 1.2x max for better control
- ✅ Asymmetric zoom speeds (zoom out 40-50% slower than zoom in)
- ✅ Gentler acceleration range (0.7-1.1) for smoother transitions
- ✅ No jarring animations during mouse wheel zoom
Zoom Input Methods (commits: e7a028b, bdfa06b)
- ✅ Mouse wheel zoom
- ✅ Alt+scroll zoom mode (keyboard modifier alternative)
- ✅ Space+scroll zoom mode (keyboard modifier alternative)
- ✅ Trackpad pinch-to-zoom (browsers translate to Ctrl+wheel events)
- ✅ Touch screen pinch-to-zoom with proper center tracking (direct touch events)
- ✅ UI zoom buttons (corrected zoom in/out direction)
- ✅ Zoom-to-fit button (zooms out to show all nodes with padding, respects minimum zoom)
Note: Ctrl+wheel is used for trackpad pinch gestures on desktop. Browsers automatically translate two-finger pinch gestures on trackpads into Ctrl+wheel events. This is separate from touchscreen pinch-to-zoom, which uses direct touch events (touchstart/touchmove/touchend).
Zoom Focal Point (commits: e42b09de, feec7ec, e7a028b)
- ✅ Cursor-centered zoom (focuses on cursor position)
- ✅ Store focal point in workspace coordinates instead of screen coordinates
- ✅ Prevents focal point drift when scroll changes due to canvas boundaries
- ✅ Maintains consistent zoom focus even when view shifts at edges
- ✅ Fixed focal point during pinch gestures
Zoom Direction & Behavior (commits: 37f9786, bdfa06b)
- ✅ Fixed trackpad zoom direction (spreading fingers zooms in, pinching zooms out)
- ✅ Matches standard macOS trackpad behavior
- ✅ Proper ratio-based scaling for pinch gestures
- ✅ Scale lock issues fixed with improved tolerance handling
Dynamic Zoom Limits (commits: 7918693, f13ed66)
- ✅ Calculate minimum zoom dynamically based on viewport size
- ✅ Ensure canvas always covers entire viewport (no empty space visible)
- ✅ Use 'cover' behavior: canvas fills viewport completely
- ✅ Recalculate minimum zoom on window resize
- ✅ Automatically adjust zoom if current level falls below new minimum after resize
- ✅ Prevent zooming out beyond what's needed to fill viewport
Panning Functionality
Pan Input Methods (commit: feec7ec)
- ✅ Two-finger pan gesture for touch devices
- ✅ Spacebar+left-click panning for desktop
- ✅ Mode locking to prevent laggy gesture switching
- ✅ Lock into pan or zoom mode based on initial movement
- ✅ Better gesture detection thresholds (10px for zoom, 5px for pan)
Scroll Behavior (commit: e7a028b)
- ✅ Momentum scrolling with edge bounce animation
- ✅ Enhanced spacebar handling to prevent scroll artifacts
UI/UX Enhancements
Gesture State Management (commits: e42b09de, bdfa06b, 121982e)
- ✅ Improved gesture state management for trackpad and touch gestures
- ✅ Proper state cleanup when cursor leaves canvas
- ✅ Clear touchStartTime timeout when entering two-finger pan mode
- ✅ Prevent interference between long-press detection and pan gestures
UI Pinch-Zoom Prevention (commit: e0c5b84)
- ✅ Prevent UI pinch-to-zoom while keeping canvas zoomable
- ✅ Apply
touch-action: pan-x pan-yto html, body, and editor elements - ✅ Apply
touch-action: noneto canvas for custom gestures - ✅ JavaScript prevention for trackpad pinch on non-canvas areas
- ✅ Block Ctrl+wheel events outside the workspace chart
Minimap Navigation (commits: 53dce6a, 5e056a4)
- ✅ Auto-show minimap on zoom and pan operations
- ✅ Auto-show minimap on flow startup and workspace changes
- ✅ Minimap appears for 2 seconds during navigation then fades out
- ✅ Smooth fade in/out animations for minimap visibility
- ✅ Minimap stays visible if manually toggled with button
- ✅ Emit
view:navigateevents for all zoom and pan operations - ✅ Check for active nodes before showing on workspace change
Visual Polish (commit: 53dce6a)
- ✅ Hide scrollbars on canvas while keeping it scrollable
- ✅ Clean visual appearance without visible scrollbars
Code Architecture
New Modules (commit: bdfa06b)
- ✅
view-zoom-animator.js- Zoom animation utilities (223 lines) - ✅
view-zoom-constants.js- Zoom configuration constants (21 lines) - ✅ Updated Gruntfile to include new zoom modules in build
Current Expectations
Cross-Device Consistency
- Zoom and pan should feel natural on mouse, trackpad, and touchscreen
- Gestures should be responsive without lag or mode switching artifacts
- Zoom focal point should remain stable regardless of input method
Browser Zoom Compatibility
- Canvas interaction should work correctly at all browser zoom levels
- UI elements should remain accessible and functional
- No layout breaking or interaction dead zones
Visual Feedback
- Minimap should provide contextual navigation feedback
- Smooth animations should make interactions feel polished
- No visual glitches or artifacts during zoom/pan operations
Performance
- All interactions should be smooth (60fps target)
- No janky animations or delayed responses
- Efficient gesture detection without excessive computation
Recent Fixes
Grey Padding at Canvas Bottom (Latest)
Issue: When scrolled to the bottom of the canvas, 5 pixels of grey space appeared below the grid, allowing users to scroll slightly beyond the canvas boundary.
Root Cause: Default browser margins on SVG elements caused the viewport's scrollHeight to be 8005px instead of 8000px, creating extra scrollable area beyond the canvas.
Solution:
- Added explicit
padding: 0andmargin: 0to#red-ui-workspace-chartcontainer - Added
display: block,margin: 0, andpadding: 0to SVG element via#red-ui-workspace-chart > svgselector - The
display: blockprevents inline element spacing issues
Files Changed:
workspace.scss:41-42, 52-57- Added margin/padding resets for container and SVG
Result: Canvas now has exact 8000px scrollable area with no grey padding visible at bottom.
Spacebar Hold Scrolling Bug
Issue: When holding spacebar down, the canvas would move down unexpectedly, making the space+scroll interaction buggy.
Root Cause: The preventDefault() was only called on the first spacebar keydown event. When spacebar is held, browsers fire repeated keydown events. After the first keydown set spacebarPressed = true, subsequent keydown events weren't prevented because the condition e.type === "keydown" && !spacebarPressed failed, allowing browser's default space-scroll behavior.
Solution:
- Moved
preventDefault()andstopPropagation()outside the conditional checks - Now blocks ALL spacebar events (both keydown repeats and keyup), not just the first keydown
Files Changed:
view.js:611-619- Restructured spacebar event handler to always prevent default
Result: Holding spacebar no longer causes unwanted canvas scrolling.
Minimap Auto-Show Behavior
Issue: Minimap was showing on selection changes and when entering pan mode (before actual panning), causing unnecessary flashing.
Solution:
- Removed
view:selection-changedevent listener - minimap no longer shows when selecting nodes - Removed
view:navigateemissions from pan mode entry points (touch long-press, spacebar+click, middle-click) - Added
view:navigateemission to regular touchpad scroll handler for consistent behavior - Kept emissions only during actual panning movement and zooming
Files Changed:
view-navigator.js:195-198- Removed selection-changed listenerview.js:483, 1529, 1539- Removed navigate events from pan mode entryview.js:876- Added navigate event to touchpad scroll handler
Result: Minimap now appears only during actual panning (touchpad or mouse) and zooming, not on selection or pan mode entry.
Diagonal Trackpad Panning
Issue: Trackpad scrolling was restricted to horizontal OR vertical movement, not both simultaneously.
Root Cause: Browser's native scroll behavior on overflow: auto containers locks into one axis at a time, even before JavaScript wheel events fire.
Solution:
- Added
evt.preventDefault()andevt.stopPropagation()to regular scroll handling - Manually apply both
deltaXanddeltaYto scrollLeft/scrollTop simultaneously - Prevents browser's axis-locked scroll behavior from taking over
- Also updated CSS
touch-actionfrompan-x pan-ytomanipulation(though this primarily affects touch events, not trackpad)
Files Changed:
view.js:864-890- Added manual diagonal scroll handlingbase.scss:22, 33- Changed touch-action to manipulation
Result: Trackpad can now pan diagonally without axis-locking.
Known Issues & Future Work
To Be Tested
- Comprehensive testing across different browser zoom levels (100%, 125%, 150%, 200%)
- Cross-browser testing (Chrome, Firefox, Safari, Edge)
- Testing on different touchscreen devices (tablets, touch-enabled laptops)
- Testing with different trackpad sensitivities and gesture settings
- Diagonal trackpad panning (fixed)
Potential Improvements
- Additional fine-tuning of zoom speeds and acceleration curves based on user feedback
- Consider adding keyboard shortcuts for zoom reset (Ctrl+0 / Cmd+0)
- Evaluate need for custom zoom level indicator in UI
- Consider adding preferences for zoom/pan sensitivity
Edge Cases to Monitor
- Behavior when canvas content is very small or very large
- Interaction with browser accessibility features
- Performance with extremely large flows (100+ nodes)
- Multi-monitor scenarios with different DPI settings
Testing Checklist
When verifying canvas interaction improvements:
-
Zoom Testing
- Mouse wheel zoom in/out
- Alt+scroll zoom (keyboard modifier)
- Space+scroll zoom (keyboard modifier)
- Trackpad pinch gesture (spread = zoom in, pinch = zoom out, generates Ctrl+wheel)
- Touch screen pinch gesture (direct touch events)
- UI zoom buttons (zoom in, zoom out, reset) - smooth animated, focal point locking
- Zoom-to-fit button (shows all nodes with padding, respects max zoom of 1.0)
- Zoom-to-fit hotkey (Ctrl+1 / Cmd+1)
- Zoom hotkeys (Ctrl+Plus, Ctrl+Minus, Ctrl+0)
- Zoom focal point stays on viewport center for button/hotkey zooms
- Dynamic zoom limits prevent empty space
- Maximum zoom capped at 1.0 (100%)
- Animation duration scales with zoom distance (200-350ms)
- Sequential zooms maintain same focal point (1 second timeout)
- Zoom-to-fit works immediately after page load
- Pan animation when zoom-to-fit doesn't need zoom change
-
Pan Testing
- Two-finger pan on trackpad/touch
- Diagonal panning works (not axis-locked)
- Spacebar+click pan on desktop
- Middle-click pan on desktop
- Momentum scrolling with edge bounce
- No lag when switching between pan and zoom
-
UI/UX Testing
- Minimap auto-shows on flow startup and workspace changes
- Minimap auto-shows during panning and zooming
- Minimap auto-shows during zoom button/hotkey/zoom-to-fit
- Minimap does not show on selection changes
- Minimap fades after 2 seconds
- No scrollbars visible on canvas
- No pinch-zoom on UI elements
- Gesture state cleanup on cursor exit
- Grab cursor (open hand) shows when spacebar held
- Grabbing cursor (closed hand) shows during active pan (spacebar or middle-click)
- No grey padding visible at canvas bottom
-
Browser Zoom Testing
- Test at 100% browser zoom
- Test at 125% browser zoom
- Test at 150% browser zoom
- Test at 200% browser zoom
- Verify all interactions work at each zoom level
Files Modified
Key files involved in canvas interaction improvements:
packages/node_modules/@node-red/editor-client/src/js/ui/view.js- Main view controllerpackages/node_modules/@node-red/editor-client/src/js/ui/view-zoom-animator.js- Zoom animationspackages/node_modules/@node-red/editor-client/src/js/ui/view-zoom-constants.js- Zoom configurationpackages/node_modules/@node-red/editor-client/src/js/ui/view-navigator.js- Minimap controllerpackages/node_modules/@node-red/editor-client/src/sass/workspace.scss- Canvas stylingpackages/node_modules/@node-red/editor-client/src/sass/base.scss- Base UI stylingGruntfile.js- Build configuration
Commit History
Interaction improvements (20 commits total on claude/issue-44-20250925-0754):
Session 1: Previous commits (commits 1-13)
e7a028b- feat: Add enhanced zoom and scroll featuresbdfa06b- Implement smooth zoom functionality with pinch-to-zoom support37f9786- Fix trackpad zoom direction - spreading fingers now zooms ine42b09d- Fix zoom focal point stability at canvas edgesa12b65b- Improve zoom smoothness and controlfeec7ec- Add two-finger panning and spacebar+click panninge0c5b84- Prevent UI pinch-to-zoom while keeping canvas zoomable121982e- Fix zoom gesture detection after two-finger panning7918693- Implement dynamic zoom limits to match canvas boundariesf13ed66- Add dynamic minimum zoom recalculation on viewport resize53dce6a- Hide scrollbars and add auto-show/hide minimap on navigation875db2c- Enable diagonal trackpad panning by preventing axis-locked scroll- (previous) - Improve minimap auto-show behavior to only trigger during actual navigation
Session 2: Zoom button/hotkey improvements (commits 14-20)
ad00ca23e- Add scroll spacer to fix scrollable area at minimum zoom48f0f3be- Fix minimap viewport position at non-1.0 zoom levelsbe1da360- Fix grey padding at canvas bottom by resetting SVG margins6f164a8a- Add zoom-to-fit button to show all nodes at once7fdff0ca- Fix zoom button handlers - zoom in/out were reversede46cfc94- Move zoom-to-fit button between reset and zoom-in95304e26- Revert "Move zoom-to-fit button between reset and zoom-in"788d0a38- Add Ctrl+1/Cmd+1 keyboard shortcut for zoom-to-fit5c090786- Remove animation from zoom buttons for instant, smooth zooming6e49e962- Fix viewport drift when using zoom buttons without focal pointe3de29d8- Fix zoom center calculation to use oldScaleFactor consistently935ff622- Fix zoom button animation and improve performancef07907e1- Add focal point locking for sequential button/hotkey zooms0247a910- Add minimap auto-show for zoom button/hotkey interactions62612139- Add grab/grabbing cursor for spacebar pan modece5a0313- Add grabbing cursor for middle-click pan mode594c0d66- Make zoom animation duration relative to maintain consistent velocity45c2a798- Set maximum zoom level to 1.0ed71cf91- Fix zoom-to-fit to properly center nodes in viewport732c8283- Refresh active nodes before zoom-to-fit to work immediately (includes pan animation matching zoom duration, 200-350ms range)5e056a4d- Add minimap auto-show on flow startup and workspace changes