From a9a247402eb110469caff8ed2bdec90f40a24d1e Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Tue, 16 Jul 2019 15:48:34 -0700 Subject: [PATCH 01/11] feat/reduced-motion: change animation duration to 0 when user has reduced motion turned on in OS --- src/ui/camera.js | 2 +- src/util/browser.js | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/ui/camera.js b/src/ui/camera.js index 5a9489842b4..380adfaccec 100644 --- a/src/ui/camera.js +++ b/src/ui/camera.js @@ -679,7 +679,7 @@ class Camera extends Evented { easing: defaultEasing }, options); - if (options.animate === false) options.duration = 0; + if (options.animate === false || browser.prefersReducedMotion) options.duration = 0; const tr = this.transform, startZoom = this.getZoom(), diff --git a/src/util/browser.js b/src/util/browser.js index 004609d877f..38b4584016b 100755 --- a/src/util/browser.js +++ b/src/util/browser.js @@ -19,6 +19,8 @@ const cancel = window.cancelAnimationFrame || let linkEl; +let _reducedMotionQuery: MediaQueryList; + /** * @private */ @@ -52,8 +54,17 @@ const exported = { return linkEl.href; }, + hardwareConcurrency: window.navigator.hardwareConcurrency || 4, - get devicePixelRatio() { return window.devicePixelRatio; } + + get devicePixelRatio() { return window.devicePixelRatio; }, + get prefersReducedMotion(): boolean { + //Lazily initialize media query + if ( _reducedMotionQuery == null ){ + _reducedMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)') + } + return _reducedMotionQuery.matches; + }, }; export default exported; From b8448ad34bfae0966e7dfc81632979cc9df1e1f4 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Tue, 16 Jul 2019 16:48:07 -0700 Subject: [PATCH 02/11] Fix failing tests --- src/util/browser.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/util/browser.js b/src/util/browser.js index 38b4584016b..89b77e601e4 100755 --- a/src/util/browser.js +++ b/src/util/browser.js @@ -54,14 +54,14 @@ const exported = { return linkEl.href; }, - hardwareConcurrency: window.navigator.hardwareConcurrency || 4, get devicePixelRatio() { return window.devicePixelRatio; }, get prefersReducedMotion(): boolean { + if (!window.matchMedia) return false; //Lazily initialize media query - if ( _reducedMotionQuery == null ){ - _reducedMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)') + if (_reducedMotionQuery == null) { + _reducedMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)'); } return _reducedMotionQuery.matches; }, From b6078c2e0a89c6ccff528c5ca9ef4c7453aca71f Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Wed, 17 Jul 2019 17:49:57 -0700 Subject: [PATCH 03/11] address CR comments and add check to flyTo --- src/ui/camera.js | 9 ++++++++- src/util/browser.js | 8 ++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/ui/camera.js b/src/ui/camera.js index 380adfaccec..cf938efea1a 100644 --- a/src/ui/camera.js +++ b/src/ui/camera.js @@ -7,7 +7,8 @@ import { warnOnce, clamp, wrap, - ease as defaultEasing + ease as defaultEasing, + pick } from '../util/util'; import { number as interpolate } from '../style-spec/util/interpolate'; import browser from '../util/browser'; @@ -857,6 +858,12 @@ class Camera extends Evented { * @see [Fly to a location based on scroll position](https://www.mapbox.com/mapbox-gl-js/example/scroll-fly-to/) */ flyTo(options: Object, eventData?: Object) { + // Fall throwugh to jumpTo is user has set prefers-reduced-motion + if (browser.prefersReducedMotion) { + this.jumpTo(pick(options, ['center', 'zoom', 'bearing', 'pitch', 'around']), eventData); + return; + } + // This method implements an “optimal path” animation, as detailed in: // // Van Wijk, Jarke J.; Nuij, Wim A. A. “Smooth and efficient zooming and panning.” INFOVIS diff --git a/src/util/browser.js b/src/util/browser.js index 89b77e601e4..6192bc6fada 100755 --- a/src/util/browser.js +++ b/src/util/browser.js @@ -19,7 +19,7 @@ const cancel = window.cancelAnimationFrame || let linkEl; -let _reducedMotionQuery: MediaQueryList; +let reducedMotionQuery: MediaQueryList; /** * @private @@ -60,10 +60,10 @@ const exported = { get prefersReducedMotion(): boolean { if (!window.matchMedia) return false; //Lazily initialize media query - if (_reducedMotionQuery == null) { - _reducedMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)'); + if (reducedMotionQuery == null) { + reducedMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)'); } - return _reducedMotionQuery.matches; + return reducedMotionQuery.matches; }, }; From 874c65060c6a9eb4e58f98bad6c6984c4df0653e Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Wed, 17 Jul 2019 18:15:45 -0700 Subject: [PATCH 04/11] fix CI errors --- src/ui/camera.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/ui/camera.js b/src/ui/camera.js index cf938efea1a..5b41182d94f 100644 --- a/src/ui/camera.js +++ b/src/ui/camera.js @@ -7,8 +7,7 @@ import { warnOnce, clamp, wrap, - ease as defaultEasing, - pick + ease as defaultEasing } from '../util/util'; import { number as interpolate } from '../style-spec/util/interpolate'; import browser from '../util/browser'; @@ -860,8 +859,14 @@ class Camera extends Evented { flyTo(options: Object, eventData?: Object) { // Fall throwugh to jumpTo is user has set prefers-reduced-motion if (browser.prefersReducedMotion) { - this.jumpTo(pick(options, ['center', 'zoom', 'bearing', 'pitch', 'around']), eventData); - return; + this.jumpTo({ + center: options.center, + zoom: options.zoom, + bearing: options.bearing, + pitch: options.pitch, + around: options.around + }, eventData); + return; } // This method implements an “optimal path” animation, as detailed in: From c96f0421c69c7940bc39d7ad5c1e4bc1fb1ab210 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Thu, 18 Jul 2019 10:55:14 -0700 Subject: [PATCH 05/11] fix flow error --- src/ui/camera.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ui/camera.js b/src/ui/camera.js index 5b41182d94f..9974a0bb244 100644 --- a/src/ui/camera.js +++ b/src/ui/camera.js @@ -859,14 +859,13 @@ class Camera extends Evented { flyTo(options: Object, eventData?: Object) { // Fall throwugh to jumpTo is user has set prefers-reduced-motion if (browser.prefersReducedMotion) { - this.jumpTo({ + return this.jumpTo({ center: options.center, zoom: options.zoom, bearing: options.bearing, pitch: options.pitch, around: options.around }, eventData); - return; } // This method implements an “optimal path” animation, as detailed in: From 2a84613c611abbcd9db66c53fc3703905cd1f7da Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Thu, 18 Jul 2019 16:35:54 -0700 Subject: [PATCH 06/11] add unit tests --- src/ui/camera.js | 14 +++++--------- test/unit/ui/camera.test.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/ui/camera.js b/src/ui/camera.js index 9974a0bb244..2b4dc74778c 100644 --- a/src/ui/camera.js +++ b/src/ui/camera.js @@ -7,7 +7,8 @@ import { warnOnce, clamp, wrap, - ease as defaultEasing + ease as defaultEasing, + pick } from '../util/util'; import { number as interpolate } from '../style-spec/util/interpolate'; import browser from '../util/browser'; @@ -857,15 +858,10 @@ class Camera extends Evented { * @see [Fly to a location based on scroll position](https://www.mapbox.com/mapbox-gl-js/example/scroll-fly-to/) */ flyTo(options: Object, eventData?: Object) { - // Fall throwugh to jumpTo is user has set prefers-reduced-motion + // Fall through to jumpTo is user has set prefers-reduced-motion if (browser.prefersReducedMotion) { - return this.jumpTo({ - center: options.center, - zoom: options.zoom, - bearing: options.bearing, - pitch: options.pitch, - around: options.around - }, eventData); + const coercedOptions = (pick(options, ['center', 'zoom', 'bearing', 'pitch', 'around']): CameraOptions); + return this.jumpTo(coercedOptions, eventData); } // This method implements an “optimal path” animation, as detailed in: diff --git a/test/unit/ui/camera.test.js b/test/unit/ui/camera.test.js index d355a7d716c..a83c9559f6d 100644 --- a/test/unit/ui/camera.test.js +++ b/test/unit/ui/camera.test.js @@ -29,6 +29,18 @@ test('camera', (t) => { return camera; } + function assertTransitionTime(test, camera, min, max) { + let startTime; + camera + .on('movestart', () => { startTime = new Date(); }) + .on('moveend', () => { + const endTime = new Date(); + const timeDiff = endTime - startTime; + test.ok(timeDiff >= min && timeDiff < max, 'Camera transition time exceeded specified range'); + test.end(); + }); + } + t.test('#jumpTo', (t) => { // Choose initial zoom to avoid center being constrained by mercator latitude limits. const camera = createCamera({zoom: 1}); @@ -869,6 +881,14 @@ test('camera', (t) => { }, 0); }); + t.test('duration is 0 when prefers-reduced-motions: reduce is set', (t) => { + const camera = createCamera(); + const stub = t.stub(browser, 'prefersReducedMotion'); + stub.get(() => true); + assertTransitionTime(t, camera, 0, 1); + camera.easeTo({ center: [100, 0], zoom: 3.2, bearing: 90, duration: 1000 }); + }); + t.end(); }); @@ -1530,6 +1550,14 @@ test('camera', (t) => { camera.flyTo({ center: [-122.3998631, 37.7884307], maxDuration: 100 }); }); + t.test('flys instantly when prefers-reduce-motion:reduce is set', (t) => { + const camera = createCamera(); + const stub = t.stub(browser, 'prefersReducedMotion'); + stub.get(() => true); + assertTransitionTime(t, camera, 0, 1); + camera.flyTo({ center: [100, 0], bearing: 90, animate: true }); + }); + t.end(); }); From dc165f0bde6e165d834632167b6aeb7220f6a870 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Thu, 18 Jul 2019 16:55:08 -0700 Subject: [PATCH 07/11] make tests more robust to CI timing issues --- test/unit/ui/camera.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/ui/camera.test.js b/test/unit/ui/camera.test.js index a83c9559f6d..6e6d91b9d84 100644 --- a/test/unit/ui/camera.test.js +++ b/test/unit/ui/camera.test.js @@ -36,7 +36,7 @@ test('camera', (t) => { .on('moveend', () => { const endTime = new Date(); const timeDiff = endTime - startTime; - test.ok(timeDiff >= min && timeDiff < max, 'Camera transition time exceeded specified range'); + test.ok(timeDiff >= min && timeDiff < max, `Camera transition time exceeded expected range( [${min},${max}) ) :${timeDiff}`); test.end(); }); } @@ -885,7 +885,7 @@ test('camera', (t) => { const camera = createCamera(); const stub = t.stub(browser, 'prefersReducedMotion'); stub.get(() => true); - assertTransitionTime(t, camera, 0, 1); + assertTransitionTime(t, camera, 0, 10); camera.easeTo({ center: [100, 0], zoom: 3.2, bearing: 90, duration: 1000 }); }); @@ -1554,7 +1554,7 @@ test('camera', (t) => { const camera = createCamera(); const stub = t.stub(browser, 'prefersReducedMotion'); stub.get(() => true); - assertTransitionTime(t, camera, 0, 1); + assertTransitionTime(t, camera, 0, 10); camera.flyTo({ center: [100, 0], bearing: 90, animate: true }); }); From 8b96dc4937a221941f382c9dee70905ecb10f607 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Thu, 18 Jul 2019 17:23:25 -0700 Subject: [PATCH 08/11] fix typo --- src/ui/camera.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/camera.js b/src/ui/camera.js index 2b4dc74778c..b866d500224 100644 --- a/src/ui/camera.js +++ b/src/ui/camera.js @@ -858,7 +858,7 @@ class Camera extends Evented { * @see [Fly to a location based on scroll position](https://www.mapbox.com/mapbox-gl-js/example/scroll-fly-to/) */ flyTo(options: Object, eventData?: Object) { - // Fall through to jumpTo is user has set prefers-reduced-motion + // Fall through to jumpTo if user has set prefers-reduced-motion if (browser.prefersReducedMotion) { const coercedOptions = (pick(options, ['center', 'zoom', 'bearing', 'pitch', 'around']): CameraOptions); return this.jumpTo(coercedOptions, eventData); From e4f40a9051e2baa036cbe4260017daad4488faa2 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Thu, 18 Jul 2019 17:50:48 -0700 Subject: [PATCH 09/11] add js docs comments --- src/ui/camera.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ui/camera.js b/src/ui/camera.js index b866d500224..77f441b6e2f 100644 --- a/src/ui/camera.js +++ b/src/ui/camera.js @@ -653,6 +653,9 @@ class Camera extends Evented { * Changes any combination of center, zoom, bearing, and pitch, with an animated transition * between old and new values. The map will retain its current values for any * details not specified in `options`. + * + * Note: The transition will happen instantly if the user has enabled + * the `reduced motion` accesibility feature enabled in their operating system. * * @memberof Map# * @param options Options describing the destination and animation of the transition. @@ -806,6 +809,9 @@ class Camera extends Evented { * Changes any combination of center, zoom, bearing, and pitch, animating the transition along a curve that * evokes flight. The animation seamlessly incorporates zooming and panning to help * the user maintain her bearings even after traversing a great distance. + * + * Note: The animation will be skipped, and this will behave equivalently to `jumpTo` + * if the user has the `reduced motion` accesibility feature enabled in their operating system. * * @memberof Map# * @param {Object} options Options describing the destination and animation of the transition. From 602b17aeb5c3976bcfb1c03453520096ffd5abee Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Thu, 18 Jul 2019 18:09:21 -0700 Subject: [PATCH 10/11] trim trailing whitespace --- src/ui/camera.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ui/camera.js b/src/ui/camera.js index 77f441b6e2f..57968fa1b5d 100644 --- a/src/ui/camera.js +++ b/src/ui/camera.js @@ -653,8 +653,8 @@ class Camera extends Evented { * Changes any combination of center, zoom, bearing, and pitch, with an animated transition * between old and new values. The map will retain its current values for any * details not specified in `options`. - * - * Note: The transition will happen instantly if the user has enabled + * + * Note: The transition will happen instantly if the user has enabled * the `reduced motion` accesibility feature enabled in their operating system. * * @memberof Map# @@ -809,7 +809,7 @@ class Camera extends Evented { * Changes any combination of center, zoom, bearing, and pitch, animating the transition along a curve that * evokes flight. The animation seamlessly incorporates zooming and panning to help * the user maintain her bearings even after traversing a great distance. - * + * * Note: The animation will be skipped, and this will behave equivalently to `jumpTo` * if the user has the `reduced motion` accesibility feature enabled in their operating system. * From 6f849512462ff46caa185bb2fc657256c05ee456 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Fri, 19 Jul 2019 14:42:16 -0700 Subject: [PATCH 11/11] fix yet another typo --- test/unit/ui/camera.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/ui/camera.test.js b/test/unit/ui/camera.test.js index 6e6d91b9d84..3fd18fe85f2 100644 --- a/test/unit/ui/camera.test.js +++ b/test/unit/ui/camera.test.js @@ -881,7 +881,7 @@ test('camera', (t) => { }, 0); }); - t.test('duration is 0 when prefers-reduced-motions: reduce is set', (t) => { + t.test('duration is 0 when prefers-reduced-motion: reduce is set', (t) => { const camera = createCamera(); const stub = t.stub(browser, 'prefersReducedMotion'); stub.get(() => true);