Skip to content

Commit

Permalink
HARP-14215, align animation with other leaflet layers
Browse files Browse the repository at this point in the history
This pull-request removes zoom/move animation that relies on requestAnimationFrame and
runs in parallel to built-in Leaflet CSS animations. Attempt to emulate built-in CSS animation via
interpolation between start/finish zoom levels and map centers doesn't provide a good synchronization
between HarpGL layer and all other Leaflet layers. Instead doing that, now we rely on CSS transformations
also in the HarpGL layer.

Apart from that, the following changes were applied:
- Update harp.gl version to 0.22.0 and three.js version to 0.124.0
- Remove unused dependencies from package.json
- Fix z-index in helloworld.html example in order to show panel on top of leaflet panes

Signed-off-by: Andrii Heonia <[email protected]>
Signed-off-by: Daniele Bacarella <[email protected]>
  • Loading branch information
Andrii Heonia authored and dbacarel committed Mar 12, 2021
1 parent a81626c commit 9b06c92
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 279 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# @here/harp-leaflet [![Build Status](https://travis-ci.com/heremaps/harp-leaflet.svg?branch=master)](https://travis-ci.com/heremaps/harp-leaflet)

__Note:__ this plugin is __DEPRECATED__ and it won't be maintained anymore.

## Overview

### A Leaflet plugin that adds harp.gl layer
Expand Down
1 change: 1 addition & 0 deletions examples/helloworld.html
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
}

.harp-gl-controls_main {
z-index: 800;
position: absolute;
right: 5px;
top: 0;
Expand Down
25 changes: 10 additions & 15 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@here/harp-leaflet",
"version": "0.2.5",
"version": "0.2.6",
"description": "Leaflet plugin for harp.gl maps",
"author": {
"name": "HERE Europe B.V.",
Expand Down Expand Up @@ -33,12 +33,12 @@
"yarn": ">=1.11.1"
},
"devDependencies": {
"@here/harp-geoutils": "^0.21.0",
"@here/harp-map-theme": "^0.21.1",
"@here/harp-mapview": "^0.21.1",
"@here/harp-omv-datasource": "^0.21.1",
"@here/harp-test-utils": "^0.21.0",
"@here/harp.gl": "^0.21.1",
"@here/harp-geoutils": "^0.22.0",
"@here/harp-map-theme": "^0.22.0",
"@here/harp-mapview": "^0.22.0",
"@here/harp-omv-datasource": "^0.22.0",
"@here/harp-test-utils": "^0.22.0",
"@here/harp.gl": "^0.22.0",
"@types/chai": "^4.2.5",
"@types/leaflet": "^1.5.1",
"@types/mocha": "^7.0.2",
Expand All @@ -59,7 +59,7 @@
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-typescript2": "^0.27.1",
"sinon": "^9.0.2",
"three": "^0.120.1",
"three": "^0.124.0",
"ts-loader": "^7.0.5",
"tslint": "^6.1.2",
"tslint-config-prettier": "^1.18.0",
Expand All @@ -68,14 +68,9 @@
"webpack-cli": "^3.3.6",
"webpack-dev-server": "^3.9.0"
},
"dependencies": {
"bezier-easing": "^2.1.0",
"bl": "4.0.2",
"semver": "7.3.2"
},
"peerDependencies": {
"@here/harp-geoutils": "^0.21.0",
"@here/harp-mapview": "^0.21.1",
"@here/harp-geoutils": "^0.22.0",
"@here/harp-mapview": "^0.22.0",
"leaflet": "^1.7.1"
}
}
146 changes: 42 additions & 104 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,106 +5,33 @@
*/
import { GeoCoordinates, GeoCoordinatesLike } from "@here/harp-geoutils";
import { MapView, MapViewEventNames, MapViewOptions, MapViewUtils } from "@here/harp-mapview";
import bezier from "bezier-easing";
import { DomUtil, LatLng, Layer, LayerOptions, Map } from "leaflet";
import { DomUtil, LatLng, Layer, LayerOptions, LeafletEvent, Map, Point } from "leaflet";
import "./draggable-patch";

type HarpLeafletOptions = Omit<LayerOptions & MapViewOptions, "canvas">;

const GEO_COORD = new GeoCoordinates(0, 0);

const easing = bezier(0, 0, 0.5, 1);

// Smooth zoom block
interface ISmoothZoom {
compute: (zoom: number, center: LatLng) => { zoom: number; center: LatLng };
setZoomAndTimestamp: (zoom: number, center: LatLng, timestamp: number) => void;
}

function lerp(v0: number, v1: number, t: number): number {
return v0 * (1 - t) + v1 * t;
}

function createSmoothZoom(delay: number): ISmoothZoom {
let lastZoom: number | null = null;
let lastCenter: LatLng | null = null;
let startZoomTimestamp: number | null = null;

return {
compute: (zoom: number, center: LatLng) => {
if (lastZoom === null) {
lastZoom = zoom;
} else if (lastZoom !== zoom && lastCenter !== null) {
if (startZoomTimestamp === null) {
startZoomTimestamp = performance.now();
}

const diff = performance.now() - startZoomTimestamp!;
const progress = 1 - easing(Math.max(Math.min(diff / delay, 1), 0));

const currentZoom = lerp(lastZoom, zoom, progress);
const lat = lerp(lastCenter.lat, center.lat, progress);
const lng = lerp(lastCenter.lng, center.lng, progress);

return { zoom: currentZoom, center: ({ lat, lng } as any) as LatLng };
}

return { zoom, center };
},
setZoomAndTimestamp: (zoom: number, center: LatLng, timestamp: number) => {
lastZoom = zoom;
lastCenter = center;
startZoomTimestamp = timestamp;
},
};
}

export class HarpGL extends Layer {
private m_glContainer!: HTMLElement;
private m_mapView!: MapView;
private m_smoothZoom!: ISmoothZoom;
private m_isZooming: boolean = false;

constructor(private m_options: HarpLeafletOptions) {
super((m_options as any) as LayerOptions);
}

initialize() {
this.update();

this.m_smoothZoom = createSmoothZoom(200); // 1/4 sec
this.resetTransform = this.resetTransform.bind(this);
}

getEvents() {
return {
movestart: () => {
this.update();
},
moveend: () => {
this.update();
},
move: () => {
this.update();
},
zoomstart: () => {
this.m_mapView.addEventListener(MapViewEventNames.AfterRender, this.onAfterRender);
this.m_isZooming = true;
this.m_mapView.beginAnimation();
},
zoomend: () => {
this.m_mapView.removeEventListener(
MapViewEventNames.AfterRender,
this.onAfterRender
);
this.m_mapView.endAnimation();
this.m_isZooming = false;
this.update();
},
zoom: () => {
this.update();
},
zoomanim: (e: L.LeafletEvent) => {
this.setNewZoomTarget(e as L.ZoomAnimEvent);
move: this.resetTransform,
zoom: this.resetTransform,
zoomanim: (event: L.LeafletEvent) => {
const evt = event as L.ZoomAnimEvent;
this.setTransform(evt.zoom, evt.center);
},
};
}
Expand All @@ -114,7 +41,7 @@ export class HarpGL extends Layer {
this.initContainer();
}

this.getPane("mapPane")!.parentNode!.appendChild(this.m_glContainer);
this.getPane("mapPane")!.appendChild(this.m_glContainer);

if (this.m_mapView === undefined) {
this.initMapView();
Expand All @@ -123,16 +50,14 @@ export class HarpGL extends Layer {
// ...

this.onResize();
this._map.on("resize", this.onResize);
map.on("resize", this.onResize);

this.update();
return this;
}

onRemove(map: Map): this {
map.off("resize", this.onResize);
if (this.m_mapView !== undefined) {
this.m_mapView.removeEventListener(MapViewEventNames.AfterRender, this.onAfterRender);
this.m_mapView.dispose();
this.m_mapView = undefined!;
}
Expand All @@ -150,22 +75,46 @@ export class HarpGL extends Layer {
this.m_glContainer.style.width = size.x + "px";
this.m_glContainer.style.height = size.y + "px";
this.m_mapView.resize(size.x, size.y);
};

private onAfterRender = () => {
if (!this.m_isZooming) {
return;
}
this.resetTransform();
};

private resetTransform() {
this.update();
};
this.setTransform(this._map.getZoom(), this._map.getCenter());
}

private setTransform(toZoom: number, toCenter: LatLng) {
const map = this._map;
const fromZoom = map.getZoom();
const scale = map.getZoomScale(toZoom, fromZoom);
const mapPanePos = DomUtil.getPosition(this.getPane("mapPane")!);

const origin = map.getPixelOrigin().subtract(mapPanePos);

const newOrigin = map.project(toCenter, toZoom);
newOrigin.x -= this.m_glContainer.clientWidth / 2 || 0;
newOrigin.y -= this.m_glContainer.clientHeight / 2 || 0;
newOrigin.x = newOrigin.x + mapPanePos.x;
newOrigin.y = newOrigin.y + mapPanePos.y;

const translate = new Point(
Math.round(origin.x * scale - newOrigin.x),
Math.round(origin.y * scale - newOrigin.y)
);
DomUtil.setTransform(this.m_glContainer, translate, scale);
}

private initContainer() {
const container = (this.m_glContainer = DomUtil.create("div", "leaflet-harpgl-layer"));
const container = this._map.createPane("harpgl");

container.style.zIndex = "190"; // put it under tilePane
const size = this._map.getSize();
container.style.width = size.x + "px";
container.style.height = size.y + "px";
DomUtil.addClass(container, "leaflet-zoom-animated");

this.m_glContainer = container;
}

private initMapView() {
Expand All @@ -182,21 +131,14 @@ export class HarpGL extends Layer {
canvas,
...this.m_options,
});

this.m_mapView.addEventListener(MapViewEventNames.AfterRender, this.onAfterRender);
}

private update() {
if (!this._map) {
return;
}
let zoom = this._map.getZoom();
let center = this._map.getCenter();
if (this._map.options.zoomAnimation !== false && this.m_isZooming) {
const r = this.m_smoothZoom.compute(zoom, center);
zoom = r.zoom;
center = r.center;
}
const zoom = this._map.getZoom();
const center = this._map.getCenter();

const cameraDistance = MapViewUtils.calculateDistanceToGroundFromZoomLevel(
this.m_mapView,
Expand All @@ -213,10 +155,6 @@ export class HarpGL extends Layer {
}
}

private setNewZoomTarget(e: L.ZoomAnimEvent) {
this.m_smoothZoom.setZoomAndTimestamp(e.zoom, e.center, performance.now());
}

get mapView() {
return this.m_mapView;
}
Expand Down
Loading

0 comments on commit 9b06c92

Please sign in to comment.