forked from jgraph/mxgraph
-
Notifications
You must be signed in to change notification settings - Fork 198
New pinch touch zoom [draft] #149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
mcyph
wants to merge
14
commits into
maxGraph:main
Choose a base branch
from
mcyph:new-pinch-touch-zoom
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
41606a6
add start of experimental new pinch touch support
mcyph 76e0018
start conversion of touch story
mcyph 8a3eaa8
move ConnectionHandlerCellMarker to external file from ConnectionHand…
mcyph fa6b76e
minor style updates
mcyph dfdc8c4
update comments+style changes for touch story
mcyph 74f7e85
touch event story fixes
mcyph d7f2a7f
removed unneeded imports from touch event story
mcyph a75156d
add ability to use capture events to InternalEvent's addListener and …
mcyph e8f85e1
add styles to touch story container
mcyph 1b14f1d
replace remaining var's with let
mcyph ee48906
replace remaining var's with let
mcyph b957841
fix eslint errors
mcyph ddec85a
remove support for safari-specific gestures api and fixed touch event…
mcyph c155127
remove references to gestureHandler and eslint fixes
mcyph File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,21 +18,21 @@ limitations under the License. | |
|
|
||
| import InternalMouseEvent from './InternalMouseEvent'; | ||
| import Client from '../../Client'; | ||
| import { isConsumed, isMouseEvent } from '../../util/EventUtils'; | ||
| import { isConsumed } from '../../util/EventUtils'; | ||
| import CellState from '../cell/CellState'; | ||
| import { | ||
| EventCache, | ||
| GestureEvent, | ||
| KeyboardEventListener, | ||
| Listenable, | ||
| MouseEventListener, | ||
| TouchEventListener, | ||
| } from '../../types'; | ||
| import { Graph } from '../Graph'; | ||
|
|
||
| // Checks if passive event listeners are supported | ||
| // see https://github.com/Modernizr/Modernizr/issues/1894 | ||
| let supportsPassive = false; | ||
|
|
||
|
|
||
| try { | ||
| document.addEventListener( | ||
| 'test', | ||
|
|
@@ -54,7 +54,7 @@ try { | |
| * @class InternalEvent | ||
| * | ||
| * Cross-browser DOM event support. For internal event handling, | ||
| * {@link mxEventSource} and the graph event dispatch loop in {@link graph} are used. | ||
| * {@link EventSource} and the graph event dispatch loop in {@link graph} are used. | ||
| * | ||
| * ### Memory Leaks: | ||
| * | ||
|
|
@@ -73,12 +73,13 @@ class InternalEvent { | |
| static addListener( | ||
| element: Listenable, | ||
| eventName: string, | ||
| funct: MouseEventListener | KeyboardEventListener | ||
| funct: MouseEventListener | TouchEventListener | KeyboardEventListener, | ||
| capture = false, | ||
| ) { | ||
| element.addEventListener( | ||
| eventName, | ||
| funct as EventListener, | ||
| supportsPassive ? { passive: false } : false | ||
| supportsPassive ? { passive: false, capture: capture } : capture | ||
| ); | ||
|
|
||
| if (!element.mxListenerList) { | ||
|
|
@@ -95,7 +96,7 @@ class InternalEvent { | |
| static removeListener( | ||
| element: Listenable, | ||
| eventName: string, | ||
| funct: MouseEventListener | KeyboardEventListener | ||
| funct: MouseEventListener | TouchEventListener | KeyboardEventListener | ||
| ) { | ||
| element.removeEventListener(eventName, funct as EventListener, false); | ||
|
|
||
|
|
@@ -321,9 +322,6 @@ class InternalEvent { | |
| * function has two arguments: the mouse event and a boolean that specifies | ||
| * if the wheel was moved up or down. | ||
| * | ||
| * This has been tested with IE 6 and 7, Firefox (all versions), Opera and | ||
| * Safari. It does currently not work on Safari for Mac. | ||
| * | ||
| * ### Example | ||
| * | ||
| * @example | ||
|
|
@@ -342,102 +340,93 @@ class InternalEvent { | |
| */ | ||
| static addMouseWheelListener( | ||
| funct: (event: Event, up: boolean, force?: boolean, cx?: number, cy?: number) => void, | ||
| target: Listenable | ||
| target?: Listenable | ||
| ) { | ||
| if (funct != null) { | ||
| const wheelHandler = (evt: WheelEvent) => { | ||
| // To prevent window zoom on trackpad pinch | ||
| if (evt.ctrlKey) { | ||
| evt.preventDefault(); | ||
| } | ||
|
|
||
| // Handles the event using the given function | ||
| if (Math.abs(evt.deltaX) > 0.5 || Math.abs(evt.deltaY) > 0.5) { | ||
| funct(evt, evt.deltaY == 0 ? -evt.deltaX > 0 : -evt.deltaY > 0); | ||
| } | ||
| }; | ||
| type TouchArray = { clientX: number, clientY: number }[]; | ||
| let touches: TouchArray | null = null; | ||
| let startTouches: TouchArray | null = null; | ||
|
|
||
| target = target != null ? target : window; | ||
|
|
||
| if (Client.IS_SF && !Client.IS_TOUCH) { | ||
| let scale = 1; | ||
|
|
||
| InternalEvent.addListener(target, 'gesturestart', (evt: GestureEvent) => { | ||
| InternalEvent.consume(evt); | ||
| scale = 1; | ||
| }); | ||
|
|
||
| InternalEvent.addListener(target, 'gesturechange', ((evt: GestureEvent) => { | ||
| InternalEvent.consume(evt); | ||
| const getTouchDistance = (touches: TouchArray) => { | ||
| const a = touches[0].clientX - touches[1].clientX; | ||
| const b = touches[0].clientY - touches[1].clientY; | ||
| return Math.sqrt(a * a + b * b); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: use Math.hypot instead (perform the same calculation) |
||
| } | ||
|
|
||
| if (typeof evt.scale === 'number') { | ||
| const diff = scale - evt.scale; | ||
| const touchesToArray = (touches: TouchList): TouchArray => { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: move this closure out of the enclosing function as |
||
| // Safari seems to use the same TouchList object unless | ||
| // the values are copied, so copy+output the values we need | ||
| const out = []; | ||
| for (let i=0; i<touches.length; i++) { | ||
| out.push({ clientX: touches[i].clientX, clientY: touches[i].clientY }); | ||
| } | ||
| return out; | ||
| } | ||
|
|
||
| if (Math.abs(diff) > 0.2) { | ||
| funct(evt, diff < 0, true); | ||
| scale = evt.scale; | ||
| // Adds basic mouse listeners for graph event dispatching | ||
| if (Client.IS_TOUCH) { | ||
| // If a touch device, use the touch events | ||
| InternalEvent.addListener(target, 'touchstart', (evt: TouchEvent) => { | ||
| if (evt.touches && evt.touches.length > 1) { | ||
| InternalEvent.consume(evt); | ||
| startTouches = touchesToArray(evt.touches); | ||
| } | ||
| }, true); | ||
| InternalEvent.addListener(target, 'touchmove', (evt: TouchEvent) => { | ||
| if (!startTouches && evt.touches && evt.touches.length > 1) { | ||
| startTouches = touchesToArray(evt.touches); | ||
| } | ||
| if (startTouches && evt.touches && evt.touches.length > 1) { | ||
| InternalEvent.consume(evt); | ||
| touches = touchesToArray(evt.touches); | ||
|
|
||
| const diff = getTouchDistance(touches) - getTouchDistance(startTouches); | ||
| if (Math.abs(diff) > InternalEvent.PINCH_THRESHOLD) { | ||
| funct(evt, diff > 0, true); | ||
| startTouches = touchesToArray(evt.touches); | ||
| } | ||
| } | ||
| }) as EventListener); | ||
| }, true) | ||
| InternalEvent.addListener(target, 'touchend', (evt: TouchEvent) => { | ||
| if (startTouches) { | ||
| InternalEvent.consume(evt); | ||
| } | ||
| touches = null; | ||
| startTouches = null; | ||
| }, true); | ||
|
|
||
| InternalEvent.addListener(target, 'gestureend', (evt: GestureEvent) => { | ||
| InternalEvent.consume(evt); | ||
| }); | ||
| } else { | ||
| let evtCache: EventCache = []; | ||
| let dx0 = 0; | ||
| let dy0 = 0; | ||
|
|
||
| // Adds basic listeners for graph event dispatching | ||
| InternalEvent.addGestureListeners( | ||
| target, | ||
| ((evt: GestureEvent) => { | ||
| if (!isMouseEvent(evt) && evt.pointerId != null) { | ||
| evtCache.push(evt); | ||
| } | ||
| }) as EventListener, | ||
| ((evt: GestureEvent) => { | ||
| if (!isMouseEvent(evt) && evtCache.length == 2) { | ||
| // Find this event in the cache and update its record with this event | ||
| for (let i = 0; i < evtCache.length; i += 1) { | ||
| if (evt.pointerId == evtCache[i].pointerId) { | ||
| evtCache[i] = evt; | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| // Calculate the distance between the two pointers | ||
| const dx = Math.abs(evtCache[0].clientX - evtCache[1].clientX); | ||
| const dy = Math.abs(evtCache[0].clientY - evtCache[1].clientY); | ||
| const tx = Math.abs(dx - dx0); | ||
| const ty = Math.abs(dy - dy0); | ||
|
|
||
| if ( | ||
| tx > InternalEvent.PINCH_THRESHOLD || | ||
| ty > InternalEvent.PINCH_THRESHOLD | ||
| ) { | ||
| const cx = | ||
| evtCache[0].clientX + (evtCache[1].clientX - evtCache[0].clientX) / 2; | ||
| const cy = | ||
| evtCache[0].clientY + (evtCache[1].clientY - evtCache[0].clientY) / 2; | ||
|
|
||
| funct(evtCache[0], tx > ty ? dx > dx0 : dy > dy0, true, cx, cy); | ||
|
|
||
| // Cache the distance for the next move event | ||
| dx0 = dx; | ||
| dy0 = dy; | ||
| } | ||
| } | ||
| }) as EventListener, | ||
| (evt) => { | ||
| evtCache = []; | ||
| dx0 = 0; | ||
| dy0 = 0; | ||
| InternalEvent.addListener(target, 'mousemove', (evt: TouchEvent) => { | ||
| if (startTouches) { | ||
| InternalEvent.consume(evt); | ||
| } | ||
| ); | ||
| }, true); | ||
| InternalEvent.addListener(target, 'pointermove', (evt: TouchEvent) => { | ||
| if (startTouches) { | ||
| InternalEvent.consume(evt); | ||
| } | ||
| }, true); | ||
| } | ||
|
|
||
| InternalEvent.addListener(target, 'wheel', wheelHandler as EventListener); | ||
| // Fall back to standard mouse wheel if touch events not in progress, or not a touch device | ||
| InternalEvent.addListener(target, 'wheel', ((evt: WheelEvent) => { | ||
| if (startTouches) { | ||
| // If being handled by touch events, ignore | ||
| InternalEvent.consume(evt); | ||
| return; | ||
| } | ||
|
|
||
| // To prevent window zoom on trackpad pinch | ||
| if (evt.ctrlKey) { | ||
| InternalEvent.consume(evt); | ||
| } | ||
|
|
||
| // Handles the event using the given function | ||
| if (Math.abs(evt.deltaX) > 0.5 || Math.abs(evt.deltaY) > 0.5) { | ||
| funct(evt, evt.deltaY == 0 ? -evt.deltaX > 0 : -evt.deltaY > 0); | ||
| } | ||
| }) as EventListener, true); | ||
| } | ||
| } | ||
|
|
||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: move this closure out of the enclosing function as it doesn't depend on any state provided by this function