Skip to content

Commit

Permalink
Normalize wheel behavior across browsers (#117)
Browse files Browse the repository at this point in the history
  • Loading branch information
taneliang authored Aug 6, 2020
1 parent 38ab942 commit ce988a4
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 19 deletions.
4 changes: 2 additions & 2 deletions src/layout/HorizontalPanAndZoomView.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export class HorizontalPanAndZoomView extends View {
_handleWheelPlain(interaction: WheelPlainInteraction) {
const {
location,
event: {deltaX, deltaY},
delta: {deltaX, deltaY},
} = interaction.payload;
if (!rectContainsPoint(location, this.frame)) {
return; // Not scrolling on view
Expand Down Expand Up @@ -209,7 +209,7 @@ export class HorizontalPanAndZoomView extends View {
) {
const {
location,
event: {deltaY},
delta: {deltaY},
} = interaction.payload;
if (!rectContainsPoint(location, this.frame)) {
return; // Not scrolling on view
Expand Down
2 changes: 1 addition & 1 deletion src/layout/VerticalScrollView.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export class VerticalScrollView extends View {
_handleWheelPlain(interaction: WheelPlainInteraction) {
const {
location,
event: {deltaX, deltaY},
delta: {deltaX, deltaY},
} = interaction.payload;
if (!rectContainsPoint(location, this.frame)) {
return; // Not scrolling on view
Expand Down
29 changes: 13 additions & 16 deletions src/useCanvasInteraction.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// @flow

import type {Point} from './layout';
import type {NormalizedWheelDelta} from './util/normalizeWheel';

import {useEffect} from 'react';
import {normalizeWheel} from './util/normalizeWheel';

export type MouseDownInteraction = {|
type: 'mousedown',
Expand Down Expand Up @@ -30,27 +32,31 @@ export type WheelPlainInteraction = {|
payload: {|
event: WheelEvent,
location: Point,
delta: NormalizedWheelDelta,
|},
|};
export type WheelWithShiftInteraction = {|
type: 'wheel-shift',
payload: {|
event: WheelEvent,
location: Point,
delta: NormalizedWheelDelta,
|},
|};
export type WheelWithControlInteraction = {|
type: 'wheel-control',
payload: {|
event: WheelEvent,
location: Point,
delta: NormalizedWheelDelta,
|},
|};
export type WheelWithMetaInteraction = {|
type: 'wheel-meta',
payload: {|
event: WheelEvent,
location: Point,
delta: NormalizedWheelDelta,
|},
|};

Expand Down Expand Up @@ -137,37 +143,28 @@ export function useCanvasInteraction(
event.preventDefault();
event.stopPropagation();

const location = localToCanvasCoordinates({x: event.x, y: event.y});
const delta = normalizeWheel(event);

if (event.shiftKey) {
interactor({
type: 'wheel-shift',
payload: {
event,
location: localToCanvasCoordinates({x: event.x, y: event.y}),
},
payload: {event, location, delta},
});
} else if (event.ctrlKey) {
interactor({
type: 'wheel-control',
payload: {
event,
location: localToCanvasCoordinates({x: event.x, y: event.y}),
},
payload: {event, location, delta},
});
} else if (event.metaKey) {
interactor({
type: 'wheel-meta',
payload: {
event,
location: localToCanvasCoordinates({x: event.x, y: event.y}),
},
payload: {event, location, delta},
});
} else {
interactor({
type: 'wheel-plain',
payload: {
event,
location: localToCanvasCoordinates({x: event.x, y: event.y}),
},
payload: {event, location, delta},
});
}

Expand Down
99 changes: 99 additions & 0 deletions src/util/normalizeWheel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in
* https://github.com/facebookarchive/fixed-data-table/blob/master/LICENSE.
* An additional grant of patent rights can be found in the PATENTS file in the
* same directory.
*
* Adapted from: https://github.com/facebookarchive/fixed-data-table/blob/master/src/vendor_upstream/dom/normalizeWheel.js
*
* TODO: Figure out the license of this code. It was originally licensed by
* Facebook with a BSD-style license. Since this profiler project is going into
* another Facebook repository, I suppose we can just relicense it under
* React's license?
*
* @flow
*/

export type NormalizedWheelDelta = {|
deltaX: number,
deltaY: number,
|};

// Reasonable defaults
const LINE_HEIGHT = 40;
const PAGE_HEIGHT = 800;

/**
* Mouse wheel (and 2-finger trackpad) support on the web sucks. It is
* complicated, thus this doc is long and (hopefully) detailed enough to answer
* your questions.
*
* If you need to react to the mouse wheel in a predictable way, this code is
* like your bestest friend. * hugs *
*
* In your event callback, use this code to get sane interpretation of the
* deltas. This code will return an object with properties:
*
* - deltaX -- normalized distance (to pixels) - x plane
* - deltaY -- " - y plane
*
* Wheel values are provided by the browser assuming you are using the wheel to
* scroll a web page by a number of lines or pixels (or pages). Values can vary
* significantly on different platforms and browsers, forgetting that you can
* scroll at different speeds. Some devices (like trackpads) emit more events
* at smaller increments with fine granularity, and some emit massive jumps with
* linear speed or acceleration.
*
* This code does its best to normalize the deltas for you:
*
* - delta* is normalizing the desired scroll delta in pixel units.
*
* - positive value indicates scrolling DOWN/RIGHT, negative UP/LEFT. This
* should translate to positive value zooming IN, negative zooming OUT.
* This matches the 'wheel' event.
*
* Implementation info:
*
* The basics of the standard 'wheel' event is that it includes a unit,
* deltaMode (pixels, lines, pages), and deltaX, deltaY and deltaZ.
* See: http://www.w3.org/TR/DOM-Level-3-Events/#events-wheelevents
*
* Examples of 'wheel' event if you scroll slowly (down) by one step with an
* average mouse:
*
* OS X + Chrome (mouse) - 4 pixel delta (wheelDelta -120)
* OS X + Safari (mouse) - N/A pixel delta (wheelDelta -12)
* OS X + Firefox (mouse) - 0.1 line delta (wheelDelta N/A)
* Win8 + Chrome (mouse) - 100 pixel delta (wheelDelta -120)
* Win8 + Firefox (mouse) - 3 line delta (wheelDelta -120)
*
* On the trackpad:
*
* OS X + Chrome (trackpad) - 2 pixel delta (wheelDelta -6)
* OS X + Firefox (trackpad) - 1 pixel delta (wheelDelta N/A)
*/
export function normalizeWheel(event: WheelEvent): NormalizedWheelDelta {
let deltaX = event.deltaX;
let deltaY = event.deltaY;

if (
// $FlowFixMe DOM_DELTA_LINE missing from WheelEvent
event.deltaMode === WheelEvent.DOM_DELTA_LINE
) {
// delta in LINE units
deltaX *= LINE_HEIGHT;
deltaY *= LINE_HEIGHT;
} else if (
// $FlowFixMe DOM_DELTA_PAGE missing from WheelEvent
event.deltaMode === WheelEvent.DOM_DELTA_PAGE
) {
// delta in PAGE units
deltaX *= PAGE_HEIGHT;
deltaY *= PAGE_HEIGHT;
}

return {deltaX, deltaY};
}

0 comments on commit ce988a4

Please sign in to comment.