Giter VIP home page Giter VIP logo

leaflet.loading's Introduction

Leaflet.loading

Leaflet.loading is a simple loading control for Leaflet. An unobtrusive loading indicator is added below the zoom control if one exists. The indicator is visible when tiles are loading or when other data is loading, as indicated by firing custom events on a map. The indicator can be an image, or a spin.js spinner (image-less).

Usage

Leaflet.loading is only tested on Leaflet version 0.6 or greater. It will almost certainly not work with older versions of Leaflet. Of course we intend to support Leaflet 1.0, and we have tested against the latest release (beta 2). Please create an issue if you find that any part of this project is not compatible with Leaflet 1.0.

Include Control.Loading.js and Control.Loading.css, then create a map with loadingControl: true in its options.

By default, Leaflet.loading includes a base64-encoded animagted loading image in Control.Loading.css. You can customize this by changing background-image for the selector .leaflet-control-loading. The simplest case would be adding a 16 x 16 loading gif in .leaflet-control-loading.

You can also set spinjs: true in the options, and load spin.js to use that instead of an image. A spin.js options object can be passed as the spin key when initializing the control.

Whichever method you use, make sure you only use one.

Once the above is complete you will have a loading indicator that only appears when tiles are loading.

If you want to show the loading indicator while other AJAX requests or something else is occurring, fire the dataloading event on your map when you begin loading and dataload when you are finished loading. Please note that there is an issue with the way this control tracks these events and that this will be re-worked in a future version.

Options

  • position: (string) Where you want the control to show up on the map (standard Leaflet control option). Optional, defaults to topleft

  • separate: (boolean) Whether the control should be separate from the zoom control or not, defaults to false.

  • zoomControl: (L.Control.Zoom) The zoom control that the control should be added to. This is only necessary when adding a loading control to a zoom control that you added manually and do not want a separate loading control.

  • delayIndicator: (float) The number of milliseconds to wait before showing the loading indicator. Defaults to null (no delay).

  • spinjs: (boolean) Enable the use of spin.js. Optional, defaults to false

  • spin: (object) A spin.js options object. Optional, defaults to

    {
        lines: 7,
        length: 3,
        width: 3,
        radius: 5,
        rotate: 13,
        top: "83%"
    }
    

Usage with react-leaflet

  1. npm install leaflet-loading.
  2. Add import "leaflet-loading"; to the file containing MapContainer.
  3. Add @import "~leaflet-loading/src/Control.Loading.css" to index.scss.
  4. Add loadingControl={true} to <MapContainer> props.

If you're using TypeScript you will get an error:

Property 'loadingControl' does not exist on type 'IntrinsicAttributes & MapContainerProps'.  TS2322

Until react-leaflet#847 is fixed you can work around with @ts-ignore, eg:

<MapContainer
  // @ts-ignore
  loadingControl={true}
>

Demos

See Leaflet.loading in action (zoom or pan to make tiles load):

License

Leaflet.loading is free software, and may be redistributed under the MIT License.

leaflet.loading's People

Contributors

althaus avatar davetapley avatar dmolineus avatar ebrelsford avatar kcwu avatar kermit-the-frog avatar magrossi avatar nickiaconis avatar robbiet480 avatar yohanboniface avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

leaflet.loading's Issues

Usage with zoomcontrol in non-default position

Hey, I like the idea of putting the loading indicator in the zoom control. The issue that I am having is that because I want my zoom control positioned in the top right corner, I have to set zoomControl=false; in the map initialization, and then add the control later.

i.e.

var map = L.map('map', {
  'zoomControl': false,
});

map.addControl(L.control.zoom({position: 'topright'}));
map.addControl(new L.Control.Loading());

This breaks your code since you are checking map.zoomControl, which doesn't reflect whether the zoomControl has been added or not, just if it was initialized.

Also, your code requires the zoomControl to be added to the map prior to the Loading control... this might be something to note in the readme. Or is there some way you could have the Loading control hook in to leaflet so it can tell when a zoom control is added? I don't know much about the internal workings of leaflet.

Will this work with google layer as well?

I am using angular-leaflet and have two layers for now. One is using google and other with OpenStreetMap. This works well with OpenStreetMap but not with google. Is there a way to make it work with google layer as well? I noticed tile 'loading' event never gets triggered for google so _handleLoading never gets executed. Is there a work around?
Thanks in advance!

support LayerGroup

Context: using a L.control.layers instance and a LayerGroup.

Use case:

var map = L.Map( 'map', { ... , layers : [ some_tilelayer ], loadingControl : true } );
var control = L.control.layers( { a : some_tilelayer, b : some_layergroup } );
control.addTo( map );

when the user clicks on the layer control, and switches to b, the loading indicator appears (correct) but stays forever (bug).

The issue seems to be caused by:

map.on({
  baselayerchange: this._handleLoading,

so that it is expected that the baselayer some_layergroup also fires "load" which never happens.

Tentative fixes

I tried this modification:

_handleLoading: function(e) {
  if (!(e.layer  &&  e.layer instanceof L.LayerGroup))  // <<< inserted this line
    this.addLoader(this.getEventId(e));
},

...but this revives issue 27.

I also tried to protect the layer.on(...) calls, but to no avail.

Current solution

Use a FeatureGroup that is extended to support the "loading" and "load" events: Leaflet.FeatureGroup.LoadEvents.

Remark

I honestly don't know whether this issue is fixable within Leaflet.loading, especially from a design point of view, or whether the issue must be fixed outside of it, e.g. with the Leaflet.FeatureGroup.LoadEvents plugin.

Wrong event counting if IDs equal

This control works based on an internal map that stores an event ID according to this code:

            getEventId: function(e) {
                if (e.id) {
                    return e.id;
                }
                else if (e.layer) {
                    return e.layer._leaflet_id;
                }
                return e.target._leaflet_id;
            },

This is quite problematic I think. The problem is, when I fire multiple dataloading events on the map instance, then these are registered as a single event only since the ID is identical for all of them (it is the map id) and therefore the spinner disappears once the first dataload event comes in, even though it should have been waiting for the others if it had counted correctly.

Why is this based on IDs anyway? It could be a simple counter, couldn't it? To not break the API and remove all the "loader" management functions (like addLoader and removeLoader), the plugin could store counts for each loader id instead of just true. Then this would support the case above and be fully backwards compatible.

Endless loading with L.TileLayer.Canvas(),

If you replace the layer openStreetMapHumanitarian from twobaselayers.html to L.TileLayer.Canvas(),
openStreetMapHumanitarian = new L.TileLayer.Canvas(),
and when you switch on this layer from layer control, loading indicator does not stop

Showing indicator for all layers added to map

I'm looking for a solution to show loading indicator for all types of maps including ImageOverlays or tiled layers over base maps.

Is there any trick to enable this plugin for all kinds of layers?

spinner.js

Hey,
wow to style the spinner when using spinjs: true, ?

Show the loader when the map is initialised.

Hi,
I think that the loader must be displayed by default. Later when the layer is loaded, the event load will trigger and the event handler will hide the loader.

In fact the event loading is triggred before the method _addLayerListeners is called. so the plugin dosn't handle the first time the map is loading.

I suggest the following fix.

onAdd: function(map) {
    ...

    // Create the loading indicator
    var classes = 'leaflet-control-loading';

    ...
}

becomes

onAdd: function(map) {
    ...

    // Create the loading indicator
    var classes = 'leaflet-control-loading is-loading';

    ...
}

This way the loader appears when the map is loading for the first time.

Base layer change

When the user change the base layer (TileLayer) through the layer control, the loader does not appear for the first time the layer is loading. then when the tile layer loaded all visible tiles everything works fine.

I have added baselayerchange support in the method _addMapListeners().

_addMapListeners: function(map) {
    // Add listeners to the map for (custom) dataloading and dataload
    // events, eg, for AJAX calls that affect the map but will not be
    // reflected in the above layer events.
    map.on({
        baselayerchange: this._handleLoading,
        dataloading: this._handleLoading,
        dataload: this._handleLoad,
        layerremove: this._handleLoad
    }, this);
}
_removeMapListeners: function(map) {
    map.off({
        baselayerchange: this._handleLoading,
        dataloading: this._handleLoading,
        dataload: this._handleLoad,
        layerremove: this._handleLoad
    }, this);
}

This make the loader appears when the base layer is changed through the layer control, but I'm not sure it's the right way to fix this problem.

put custom spinner location

First, thanks for a perfect plugin.

I would like to put loading spinner (in my case spinjs) to custom position. E.g. make it really big in the center of the map.

Is it somehow possible through {separate: true} options and custom css? I cannot find a way:(

The only idea what I have is to change code:

  • where the spinjs is attached to DOM, to let _indicator undefined (and store _map on map).

  • functions _showIndicator and hideIndicator like:

          _showIndicator: function() {                                       
              if (this._spinner && !this._indicator) {                       
                  this._spinner.stop();                                      
                  this._spinner = new Spinner(this.options.spin).spin(this._map._container);
                  return;                                                    
              }
              ...
    

Is it reasonable solution or am I something overlooking missing?

Container border on touch

Thanks for a great plugin!

I found a small problem.
When using this plugin on a touch device the border of the _container is visible as a dot when the loading indicator is hidden.

I added display none/block to the container when hiding/showing the indicator which solved the problem, but i belive this needs to be fixed in the upcomming versions.
Also fiering hide/show events would be nice.

Thanks again

A simple example

It would be nice to have a simple example showing how to use the loading control.

React Leaflet 2

Is this component compatible with React Leaflet 2 or only React Leaflet 3?

Let user manually show/hide spinner

I have use case where I want to show spinner only during loading of application data on the map. I am not concerned with progress of tiles and other layers loading, since data on those layers is not crucial to the user.

I achieved this with a bit of hacking, using "private" methods _showIndicator and _hideIndicator and unregistering event listeners immediately after control is added to the map:

    // Initialization logic
    this._loadingControl = L.Control.loading({ separate: true, delayIndicator: 300 });
    this._map.addControl(this._loadingControl);
    // remove layer and basemap event listeners, we want only our logic to show spinner.
    this._loadingControl.onRemove(this._map); 
    private async loadMyData(filter) {
        this._loadingControl._showIndicator();

        try {
            // Data loading logic here
        } finally {
            this._loadingControl._hideIndicator();
        }
    }

It would be nice to have official support for this use case.

no method 'eachLayer'

When I add the loading control to my map I am getting:

Uncaught TypeError: Object [object Object] has no method 'eachLayer'

var map = new L.Map('full-map', {
scrollWheelZoom: false,
minZoom: 2,
attributionControl: false,
loadingControl: true
});

Not working with Angular.js?

This is a cool addon for Leaflet!

After playing around with it using Angular.js, I could only get it to work with spinner.js using:

      var loadingControl = L.Control.loading({
          spinjs: true
      });
      map.addControl(loadingControl);

But I much prefer the image you use, and not spinner.js. I am wondering if something in Angular is tripping up the normal initialization of this control?

If you have any ideas I would be happy to poke around and create a pull request if I can get it fixed.

Loading indicator stays on for GeoJSON data

Neat plugin. Seems to be more complex to do this than you'd expect.

I have a basemap that's geojson data with many objects. There's also another (non-geojson) baselayer. The loading indicator is fine if the base-layer starts as the geojson, but when switching back to the geojson baselayer, the loading indicator appears and never disappears.

The problem seems to be that inside _handleBaseLayerChange the plugin runs addLoader for each child layer (as GeoJSON loads as a group). But they are then never triggered. Don't know why.

I worked around this by simply disabling it for LayerGroups:

                    if(!e.layer instanceof L.LayerGroup) {
                        e.layer.eachLayer(function (layer) {
                            that._handleBaseLayerChange({layer: layer});
                        });
                    }

but it's a weak solution

Can we set a time (half a second for example) before starting the loader?

Hello,
this tool is really great, thanks! I have one simple question:
Is there a way to ask the loader to wait before it is displayed?
Because in my case, even when all tiles are already in the cache, the loader appears for a millisecond before disappearing when zooming or paning. It would be great that when the tiles appear almost instantaneously, the loader does not show up.
Maybe a simple wait() or sleep() somewhere could do the trick?

Thanks a lot

Not IE8 compatible

Minor issue: this library is not IE8 compatible because of it's use of console statements. IE8 browsers do not instantiate the console object until Developer Tools are opened. This can cause the map to break

Add default loading.gif

It would be nice if a default loading gif is provided. To support spin.js as well, you could use the :empty selector:

.leaflet-control-loading:empty {
    background-image: url('loading.gif');
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.