Skip to content

Commit

Permalink
Throttle ResizeObserver's call to map.resize() (maplibre#2986)
Browse files Browse the repository at this point in the history
* Throttle ResizeObserver calls

* Update changelog

* Typing fix

* Typing fix

---------

Co-authored-by: neodescis <[email protected]>
  • Loading branch information
neodescis and neodescis authored Aug 14, 2023
1 parent f460886 commit ff251c7
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 16 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@

### 🐞 Bug fixes

- Correct declared return type of `Map.getLayer()` and `Style.getLayer()` to be `StyleLayer | undefined` to match the documentation.
- Correct type of `Map.addLayer()` and `Style.addLayer()` to allow adding a layer with an embedded source, matching the documentation.
- Correct declared return type of `Map.getLayer()` and `Style.getLayer()` to be `StyleLayer | undefined` to match the documentation ([#2969](https://github.com/maplibre/maplibre-gl-js/pull/2969))
- Correct type of `Map.addLayer()` and `Style.addLayer()` to allow adding a layer with an embedded source, matching the documentation ([#2966](https://github.com/maplibre/maplibre-gl-js/pull/2966))
- Throttle map resizes from ResizeObserver to reduce flicker ([#2986](https://github.com/maplibre/maplibre-gl-js/pull/2986))
- _...Add new stuff here..._

## 3.3.0
Expand Down
23 changes: 16 additions & 7 deletions src/ui/map.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -861,21 +861,30 @@ describe('Map', () => {

const map = createMap();

const spyA = jest.spyOn(map, '_update');
const spyB = jest.spyOn(map, 'resize');
const updateSpy = jest.spyOn(map, '_update');
const resizeSpy = jest.spyOn(map, 'resize');

// The initial "observe" event fired by ResizeObserver should be captured/muted
// in the map constructor

observerCallback();
expect(spyA).not.toHaveBeenCalled();
expect(spyB).not.toHaveBeenCalled();
expect(updateSpy).not.toHaveBeenCalled();
expect(resizeSpy).not.toHaveBeenCalled();

// Following "observe" events should fire a resize / _update
// The next "observe" event should fire a resize / _update

observerCallback();
expect(updateSpy).toHaveBeenCalled();
expect(resizeSpy).toHaveBeenCalledTimes(1);

// Additional "observe" events should be throttled
observerCallback();
observerCallback();
observerCallback();
observerCallback();
expect(spyA).toHaveBeenCalled();
expect(spyB).toHaveBeenCalled();
expect(resizeSpy).toHaveBeenCalledTimes(1);
await new Promise((resolve) => { setTimeout(resolve, 100); });
expect(resizeSpy).toHaveBeenCalledTimes(2);
});

test('width and height correctly rounded', () => {
Expand Down
11 changes: 7 additions & 4 deletions src/ui/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {RGBAImage} from '../util/image';
import {Event, ErrorEvent, Listener} from '../util/evented';
import {MapEventType, MapLayerEventType, MapMouseEvent, MapSourceDataEvent, MapStyleDataEvent} from './events';
import {TaskQueue} from '../util/task_queue';
import {throttle} from '../util/throttle';
import {webpSupported} from '../util/webp_supported';
import {PerformanceMarkers, PerformanceUtils} from '../util/performance';
import {Source, SourceClass} from '../source/source';
Expand Down Expand Up @@ -638,15 +639,17 @@ export class Map extends Camera {
if (typeof window !== 'undefined') {
addEventListener('online', this._onWindowOnline, false);
let initialResizeEventCaptured = false;
const throttledResizeCallback = throttle((entries: ResizeObserverEntry[]) => {
if (this._trackResize && !this._removed) {
this.resize(entries)._update();
}
}, 50);
this._resizeObserver = new ResizeObserver((entries) => {
if (!initialResizeEventCaptured) {
initialResizeEventCaptured = true;
return;
}

if (this._trackResize) {
this.resize(entries)._update();
}
throttledResizeCallback(entries);
});
this._resizeObserver.observe(this._container);
}
Expand Down
10 changes: 7 additions & 3 deletions src/util/throttle.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
/**
* Throttle the given function to run at most every `period` milliseconds.
*/
export function throttle(fn: () => void, time: number): () => ReturnType<typeof setTimeout> {
export function throttle<T extends (...args: any) => void>(fn: T, time: number): (...args: Parameters<T>) => ReturnType<typeof setTimeout> {
let pending = false;
let timerId: ReturnType<typeof setTimeout> = null;
let lastCallContext = null;
let lastCallArgs: Parameters<T>;

const later = () => {
timerId = null;
if (pending) {
fn();
fn.apply(lastCallContext, lastCallArgs);
timerId = setTimeout(later, time);
pending = false;
}
};

return () => {
return (...args: Parameters<T>) => {
pending = true;
lastCallContext = this;
lastCallArgs = args;
if (!timerId) {
later();
}
Expand Down

0 comments on commit ff251c7

Please sign in to comment.