From 071831346557b5e44191607289b9d04ab370aea1 Mon Sep 17 00:00:00 2001 From: Aleksei Ilin Date: Sat, 29 Aug 2026 21:42:59 +0200 Subject: [PATCH] fix: reset gesture state on touchcancel --- src/Event.ts | 20 ++++++++++++++++++++ src/common/EventHandler.ts | 24 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/Event.ts b/src/Event.ts index 7193c8ae5..59f4c7da5 100644 --- a/src/Event.ts +++ b/src/Event.ts @@ -649,6 +649,26 @@ export default class Event implements EventHandler { return false } + touchCancelEvent(e: MouseTouchEvent): boolean { + const { widget } = this._findWidgetByEvent(e) + if (widget !== null) { + const event = this._makeWidgetEvent(e, widget) + // notify the widget the gesture is over, same as a touch end + widget.dispatchEvent('mouseUpEvent', event) + } + this._startScrollCoordinate = null + this._prevYAxisRanges.clear() + this._xAxisStartScaleCoordinate = null + this._xAxisStartScaleDistance = 0 + this._xAxisScale = 1 + this._yAxisStartScaleDistance = 0 + this._touchCoordinate = null + this._touchCancelCrosshair = false + this._touchZoomed = false + this._chart.getChartStore().setCrosshair() + return true + } + tapEvent(e: MouseTouchEvent): boolean { const { pane, widget } = this._findWidgetByEvent(e) let consumed = false diff --git a/src/common/EventHandler.ts b/src/common/EventHandler.ts index 0d459b5d9..4bd1568a5 100644 --- a/src/common/EventHandler.ts +++ b/src/common/EventHandler.ts @@ -56,6 +56,7 @@ export interface EventHandler { mouseUpEvent?: MouseTouchEventCallback touchEndEvent?: MouseTouchEventCallback + touchCancelEvent?: MouseTouchEventCallback mouseDownOutsideEvent?: MouseTouchEventCallback @@ -514,6 +515,26 @@ export default class EventHandlerImp { } } + private _touchCancelHandler(touchCancelEvent: TouchEvent): void { + const touch = this._touchWithId(touchCancelEvent.changedTouches, this._activeTouchId) + this._activeTouchId = null + this._lastTouchEventTimeStamp = this._eventTimeStamp(touchCancelEvent) + this._clearLongTapTimeout() + this._touchMoveStartCoordinate = null + // a cancelled gesture must not resolve into a tap + this._cancelTap = true + this._resetTapTimeout() + + if (this._unsubscribeRootTouchEvents !== null) { + this._unsubscribeRootTouchEvents() + this._unsubscribeRootTouchEvents = null + } + + if (touch !== null) { + this._processEvent(this._makeCompatEvent(touchCancelEvent, touch), this._handler.touchCancelEvent) + } + } + private _mouseUpHandler(mouseUpEvent: MouseEvent): void { if (mouseUpEvent.button !== MouseEventButton.Left) { return @@ -589,14 +610,17 @@ export default class EventHandlerImp { { const boundTouchMoveWithDownHandler = this._touchMoveHandler.bind(this) const boundTouchEndHandler = this._touchEndHandler.bind(this) + const boundTouchCancelHandler = this._touchCancelHandler.bind(this) this._unsubscribeRootTouchEvents = () => { rootElement.removeEventListener('touchmove', boundTouchMoveWithDownHandler) rootElement.removeEventListener('touchend', boundTouchEndHandler) + rootElement.removeEventListener('touchcancel', boundTouchCancelHandler) } rootElement.addEventListener('touchmove', boundTouchMoveWithDownHandler, { passive: false }) rootElement.addEventListener('touchend', boundTouchEndHandler, { passive: false }) + rootElement.addEventListener('touchcancel', boundTouchCancelHandler, { passive: false }) this._clearLongTapTimeout() this._longTapTimeoutId = setTimeout(this._longTapHandler.bind(this, downEvent), Delay.LongTap)