An overlay class for Leaflet, a JS library for interactive maps. Allows drawing overlay using Pixi.js, a JavaScript library for drawing using WebGL that seamlessly falls back to HTML5's canvas if needed. Thanks to Leaflet.D3SvgOverlay for inspiration.
- No limitations to polylines, circles or geoJSON. Draw whatever you want with Pixi.js
- No need to reproject your geometries on zoom, this is done using scaling
- Zoom animation where Leaflet supports it
Supports leaflet@0.7, 1 and pixi.js@4, 5, 6, 7
A very basic demo (pixi.js@7) .
Largest US cities (pixi.js@7, 1000 animated markers).
French cities (pixi.js@7, 36700 animated markers).
Rotating markers with constant size during zoom
French presidential 2017 election results: first round and second round (36000 polygons).
French legislative 2017 election results: first round and second round (36000 polygons).
(graph-draw is used to display boundaries in the election demos when rendered in WebGL)
Leaflet.PixiOverlay is available as a npm package:
npm install leaflet-pixi-overlay
or can be included in a page with jsDelivr CDN.
Include Pixi.js and the PixiOverlay libraries:
<script src="pixi.min.js"></script>
<script src="L.PixiOverlay.min.js"></script>
Create a map:
const map = L.map(...);
Create an overlay:
const pixiOverlay = L.pixiOverlay((utils) => {
// your drawing code here
}, new PIXI.Container());
Add it to the map:
pixiOverlay.addTo(map);
const markerTexture = await PIXI.Assets.load('img/marker-icon.png');
const markerLatLng = [51.5, -0.09];
const marker = new PIXI.Sprite(markerTexture);
marker.anchor.set(0.5, 1);
const pixiContainer = new PIXI.Container();
pixiContainer.addChild(marker);
let firstDraw = true;
let prevZoom;
const pixiOverlay = L.pixiOverlay((utils) => {
const zoom = utils.getMap().getZoom();
const container = utils.getContainer();
const renderer = utils.getRenderer();
const project = utils.latLngToLayerPoint;
const scale = utils.getScale();
if (firstDraw) {
const markerCoords = project(markerLatLng);
marker.x = markerCoords.x;
marker.y = markerCoords.y;
}
if (firstDraw || prevZoom !== zoom) {
marker.scale.set(1 / scale);
}
firstDraw = false;
prevZoom = zoom;
renderer.render(container);
}, pixiContainer);
pixiOverlay.addTo(map);
const polygonLatLngs = [
[51.509, -0.08],
[51.503, -0.06],
[51.51, -0.047],
[51.509, -0.08]
];
let projectedPolygon;
const triangle = new PIXI.Graphics();
const pixiContainer = new PIXI.Container();
pixiContainer.addChild(triangle);
let firstDraw = true;
let prevZoom;
const pixiOverlay = L.pixiOverlay((utils) => {
const zoom = utils.getMap().getZoom();
const container = utils.getContainer();
const renderer = utils.getRenderer();
const project = utils.latLngToLayerPoint;
const scale = utils.getScale();
if (firstDraw) {
projectedPolygon = polygonLatLngs.map((coords) => project(coords));
}
if (firstDraw || prevZoom !== zoom) {
triangle.clear();
triangle.lineStyle(3 / scale, 0x3388ff, 1);
triangle.beginFill(0x3388ff, 0.2);
projectedPolygon.forEach((coords, index) => {
if (index == 0) triangle.moveTo(coords.x, coords.y);
else triangle.lineTo(coords.x, coords.y);
});
triangle.endFill();
}
firstDraw = false;
prevZoom = zoom;
renderer.render(container);
}, pixiContainer);
pixiOverlay.addTo(map);
L.pixiOverlay(<function> drawCallback, <PIXI.Container> container, <options> options?)
drawCallback
- callback to draw/update overlay contents.container
- a Pixi container (a subclass ofPIXI.Container
).options
- overlay options object.
Drawing callback function
drawCallback(utils, eventOrCustomData)
utils
- helper object. Contains methods to work with layers coordinate system and scaling.eventOrCustomData
- Contains either the Leaflet event that causes the redraw (moveend
event) or a plain object{type: 'add'}
when the pixi layer is added to the map or the argument of aredraw
call.
Overlay options object
available fields:
padding
- (number; defaults to0.1
) How much to extend the drawing area around the map view (relative to its size).forceCanvas
- (bool; defaults tofalse
) Force use of a 2d-canvas for rendering.doubleBuffering
- (bool; default tofalse
) Activate double buffering to prevent flickering when refreshing display on some devices (especially iOS devices). This field is ignored if rendering is done with 2d-canvas.resolution
- (number; defaults to2
on retina devices and1
elsewhere) Resolution of the renderer.projectionZoom
- (function(map): Number; defaults to function that returns the average ofmap.getMinZoom()
andmap.getMaxZoom()
if the latter is finite else it returnsmap.getMinZoom() + 8
) returns the projection zoom level. Customizing this option can help if you experience visual artifacts.pane
- (string; defaults to'overlayPane'
) The Leaflet pane where the overlay container is inserted.destroyInteractionManager
- (bool; defaults tofalse
) Destroy PIXI EventSystem. This is useful when you do not need to use PIXI interaction.autoPreventDefault
- (bool; defaults totrue
) Customize PIXI EventSystemautoPreventDefault
property. This option is ignored ifdestroyInteractionManager
istrue
. This should be set tofalse
in most situations to let all dom events flow from PIXI to leaflet but it is set by default for compatibility reason.preserveDrawingBuffer
- (bool; defaults tofalse
) Enables drawing buffer preservation, enable this if you need to call toDataUrl on the webgl context.clearBeforeRender
- (bool; defaults totrue
) This sets if the renderer will clear the canvas or not before the new render pass.shouldRedrawOnMove
- (function(e: moveEvent): Boolean; defaults tofunction () {return false;}
) Move events trigger a redraw when this function returnstrue
. For example usingfunction (e) {return e.flyTo || e.pinch;}
will trigger redraws duringflyTo
animation and pinch zooms.
Utils object
available methods:
latLngToLayerPoint(latLng, zoom?)
- (function) returnsL.Point
projected fromL.LatLng
in the coordinate system of the overlay.layerPointToLatLng(point, zoom?)
- (function) returnsL.LatLng
projected back fromL.Point
into the original CRS.getScale(zoom?)
- (function) return the current scale factor of the overlay or the scale factor associated to zoom value.getRenderer()
- (function) return the current PIXI renderer.getContainer()
- (function) return the PIXI container used in the overlay.getMap()
- (function) return the current map.
-
redraw(data)
- (function) trigger a refresh of the layer.data
is passed as second argument ofdrawCallback
function. This is useful for example when you modify something in thecontainer
or if you want to animate usingPIXI.ticker.Ticker
. -
destroy()
- (function) remove the layer from the map and destroy the underlying renderer. A destroyed layer is not usable anymore.
- Add support for
destroy
method
- Add support for pixi.js@7 (and remove deprecation warning for pixi.js@6)
- Improve default
projectionZoom
function
- Fix a pinch zoom regression introduced in 1.8.0
- Add support for redrawing the layer during flyTo animations and pinch zooms (This is disabled by default. See
shouldRedrawOnMove
option to enable it.) - Both pixi.js@5 and pixi.js-legacy@5 should be supported now
- Add basic support for pixi.js-legacy@5
- Add
preserveDrawingBuffer
andclearBeforeRender
options
- Bug fixes
- Add options for PIXI Interaction Manager
- Improved documentation
- Improved behavior when
doubleBuffering
is enabled - Remove event listeners on layer remove wih Leaflet 0.7.x
- Add second argument to
drawCallback
, improving control over redraw logic - No need to recompute container transform on
redraw
calls (performance improvement)
- Add
redraw
method
- Add
doubleBuffering
option to get rid of flickering on iOS devices
- Minor improvements
- Add support for leaflet@0.7.x (thanks to dzwiedzmin)
- Initial release
This code is provided under the MIT License (MIT).
Thanks to Stadia Maps for providing the Stamen Toner Lite map tiles in our project demos.