add mouse scroll to zoom functionality

This commit is contained in:
2025-08-11 21:59:53 +00:00
parent dbe17805e0
commit 2b942893c0
2 changed files with 59 additions and 42 deletions
+3 -2
View File
@@ -208,8 +208,9 @@ class LazyCanvasController with ChangeNotifier {
} }
/// Increment or decrement the scale by an additive delta value. /// Increment or decrement the scale by an additive delta value.
void updateScalebyDelta(double delta) { void updateScalebyDelta(double delta, {Offset? focalPoint}) {
final focalPoint = Offset(canvasSize.width / 2, canvasSize.height / 2); // added focalPoint param
focalPoint ??= Offset(canvasSize.width / 2, canvasSize.height / 2);
final newScale = _scale + delta; final newScale = _scale + delta;
_gsTopLeftOffset = newGsTopLeftOnScaling(_gsTopLeftOffset, focalPoint, _scale, newScale); _gsTopLeftOffset = newGsTopLeftOnScaling(_gsTopLeftOffset, focalPoint, _scale, newScale);
_scale = newScale; _scale = newScale;
+56 -40
View File
@@ -1,5 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; // added for LogicalKeyboardKey
import 'package:flutter/gestures.dart'; // added for PointerScrollEvent
import '../utils/offset_extensions.dart'; import '../utils/offset_extensions.dart';
import '../utils/styles.dart'; import '../utils/styles.dart';
@@ -33,46 +35,61 @@ class _LazyCanvasState extends State<LazyCanvas> with TickerProviderStateMixin<L
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
widget.controller.setBuildContext(context); widget.controller.setBuildContext(context);
return GestureDetector( return Listener(
behavior: HitTestBehavior.translucent, behavior: HitTestBehavior.translucent, // ensure scroll signals are captured even on empty space
onScaleUpdate: widget.controller.onScaleUpdate, onPointerSignal: (event) {
onScaleStart: widget.controller.onScaleStart, if (event is PointerScrollEvent) {
child: ListenableBuilder( final pressed = RawKeyboard.instance.keysPressed;
listenable: widget.controller, if (pressed.contains(LogicalKeyboardKey.controlLeft) || pressed.contains(LogicalKeyboardKey.controlRight)) {
builder: (_, _) { // Typical mouse wheel up gives negative dy on many platforms; invert if needed
final childrenWithPositions = widget.controller.widgetsWithScreenPositions(); final delta = (-event.scrollDelta.dy) * 0.0015; // sensitivity
final ssPositions = childrenWithPositions.map((e) => e.ssPosition).toList(); if (delta != 0) {
final childrenIds = childrenWithPositions.map((e) => e.id).toList(); widget.controller.updateScalebyDelta(delta, focalPoint: event.localPosition);
final children = childrenWithPositions.map((e) => e.child).toList(); }
final canvas = _CanvasRenderObject(
childrenIds: childrenIds,
canvasBackground: widget.controller.background,
ssPositions: ssPositions,
scale: widget.controller.scale,
gridSpaceOffset: widget.controller.offset,
onCanvasSizeChange: widget.controller.onCanvasSizeChange,
onChildSizeChange: widget.controller.onChildSizeChange,
children: children,
);
if (widget.controller.debug) {
return Stack(
children: [
canvas,
Positioned(
top: 16,
left: 16,
child: Text(
'Offset: ${widget.controller.offset.coord()}\nScale: ${widget.controller.scale.toStringAsFixed(1)}',
style: monospaceStyle,
),
),
],
);
} else {
return canvas;
} }
}, }
},
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onScaleUpdate: widget.controller.onScaleUpdate,
onScaleStart: widget.controller.onScaleStart,
child: ListenableBuilder(
listenable: widget.controller,
builder: (_, _) {
final childrenWithPositions = widget.controller.widgetsWithScreenPositions();
final ssPositions = childrenWithPositions.map((e) => e.ssPosition).toList();
final childrenIds = childrenWithPositions.map((e) => e.id).toList();
final children = childrenWithPositions.map((e) => e.child).toList();
final canvas = _CanvasRenderObject(
childrenIds: childrenIds,
canvasBackground: widget.controller.background,
ssPositions: ssPositions,
scale: widget.controller.scale,
gridSpaceOffset: widget.controller.offset,
onCanvasSizeChange: widget.controller.onCanvasSizeChange,
onChildSizeChange: widget.controller.onChildSizeChange,
children: children,
);
if (widget.controller.debug) {
return Stack(
children: [
canvas,
Positioned(
top: 16,
left: 16,
child: Text(
'Offset: ${widget.controller.offset.coord()}\nScale: ${widget.controller.scale.toStringAsFixed(1)}',
style: monospaceStyle,
),
),
],
);
} else {
return canvas;
}
},
),
), ),
); );
} }
@@ -217,7 +234,6 @@ class _CanvasRenderBox extends RenderBox
childParentData.id = _childrenIds[index]; childParentData.id = _childrenIds[index];
childParentData.scale = _scale; childParentData.scale = _scale;
index++; index++;
// loosen so like a stack can take it's own size inside parent
child.layout(constraints.loosen(), parentUsesSize: true); child.layout(constraints.loosen(), parentUsesSize: true);
// notify the controller about the size of the child // notify the controller about the size of the child