fix(pen): eraser lag/stuck-red/reliability; zoom glitch-reject; native input diag
All checks were successful
CI / Windows build (push) Successful in 17m58s
All checks were successful
CI / Windows build (push) Successful in 17m58s
Eraser (regression from the preview I added):
- LAG: the preview did setState on every hover/erase-move (rebuilding the whole
canvas) and recomputed perfect_freehand getStroke per overlapped stroke per
frame. Now the cursor is a ValueNotifier driving the preview layer's repaint
directly (no canvas rebuild), and the highlight is a plain polyline of the
point-runs inside the radius (no getStroke).
- STUCK RED ("一直红着"): the cursor was never cleared. Preview is now
active-erase-only and cleared on pen up/cancel.
- "选中了的笔画也不见得能删掉": radius was strokeWidth*2 (tiny) so a pass removed
~2 points and the stroke survived. Now a decisive fixed 0.02 (page-width
fraction). The highlight traces exactly the point-run that splitStrokeByCircle
removes, so what turns red is what gets deleted.
Zoom: replace the per-frame scale CLAMP with glitch REJECTION — drop a frame
demanding an implausible per-frame scale jump (>1.4x or <0.71x; a real pinch is
≲1.15x/frame). A dropped frame catches up the next frame (absolute tracking), so
no lag, but the Windows multi-touch spike never shows. Pairs with the existing
pointer-count re-baseline.
Native diagnostic: ObservePenMessage now counts WM_POINTER* / PT_PEN / legacy
mouse messages it sees and emits them on the channel; PenInputService exposes
`debugSummary` and the overlay shows `native ptr=… pen=… mouse=… msg=0x…`. This
will tell us on-device whether WM_POINTER ever reaches the observer (→ buttons
recoverable) or Flutter is on a non-pointer path (→ not).
Dart: analyze clean, 66/66 tests, linux build green. Native compiles on CI.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -40,9 +40,11 @@ const Set<PointerDeviceKind> _kPanZoomDevices = <PointerDeviceKind>{
|
||||
PointerDeviceKind.unknown,
|
||||
};
|
||||
|
||||
/// Per-frame multiplicative scale-change clamp (flicker guard).
|
||||
const double _kMaxScaleChangePerFrame = 1.35;
|
||||
const double _kMinScaleChangePerFrame = 1 / _kMaxScaleChangePerFrame;
|
||||
/// A real pinch changes scale only modestly per frame (≲1.15x at 60fps). A frame
|
||||
/// demanding far more than this is a Windows multi-touch position glitch, not
|
||||
/// intent — that frame is dropped so the zoom can't pop and snap back.
|
||||
const double _kScaleGlitchHi = 1.4;
|
||||
const double _kScaleGlitchLo = 1 / _kScaleGlitchHi;
|
||||
|
||||
const double _kDrag = 0.0000135;
|
||||
|
||||
@@ -177,14 +179,14 @@ class _PenInteractiveViewerState extends State<PenInteractiveViewer>
|
||||
case _GestureType.scale:
|
||||
assert(_scaleStart != null);
|
||||
final double desiredScale = _scaleStart! * details.scale;
|
||||
// Flicker guard: clamp the per-frame change so a single-frame touch
|
||||
// jitter can't pop the zoom and snap back. Absolute tracking means a
|
||||
// real pinch just resumes next frame.
|
||||
final double scaleChange = clampDouble(
|
||||
desiredScale / scale,
|
||||
_kMinScaleChangePerFrame,
|
||||
_kMaxScaleChangePerFrame,
|
||||
);
|
||||
final double scaleChange = desiredScale / scale;
|
||||
// Glitch rejection: drop a frame that demands an implausible per-frame
|
||||
// scale jump (a Windows multi-touch position glitch). The next good
|
||||
// frame resumes from the true finger positions, so the spike never
|
||||
// shows — unlike clamping, which still applied a visible partial jump.
|
||||
if (scaleChange > _kScaleGlitchHi || scaleChange < _kScaleGlitchLo) {
|
||||
return;
|
||||
}
|
||||
_transformer.value = _matrixScale(_transformer.value, scaleChange);
|
||||
|
||||
// Keep the focal point anchored under the fingers across the scale.
|
||||
|
||||
Reference in New Issue
Block a user