How to add tiles nested in leaflet map to canvas with latest version of react-leaflet?

1.2k Views Asked by At

I'm trying to add map on the canvas and put tiles there with react-leaflet library v3.X. At the moment tiles are displaying as <img> in HTML. In latest versions of react-leaflet there isn't Map object there is MapContainer object only. So I can't add property preferCanvas=true to MapContainer. It is noteworthy that in old versions of react-leaflet there was Map object with preferCanvas option but then it was removed. I think another way to add map to canvas exists. Please, help.

import React, { useState, useEffect, useRef } from "react";
import { MapContainer as LeafletMap, TileLayer } from "react-leaflet";

// some code skipped
return (
<LeafletMap
  preferCanvas={true} // не работает
  center={[51.65, 103.72]}
  zoom={14}
  maxZoom={16}
  attributionControl={true}
  zoomControl={true}
  doubleClickZoom={false}
  scrollWheelZoom={true}
  dragging={true}
  animate={true}
  easeLinearity={0.35}
  whenReady={(map) => {
    setMapReady(true);
  }}
>
  <TileLayer
    url="https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}"
    id="mapbox/outdoors-v11"
  />
</LeafletMap>
  );
}
1

There are 1 best solutions below

3
On BEST ANSWER

preferCanvas was never intended to render tilelayer tiles as canvas elements. From the docs:

Whether Paths should be rendered on a Canvas renderer. By default, all Paths are rendered in a SVG renderer.

What you want to do is not built into leaflet, but is not too uncommon. Rather than build it totally from scratch, it looks like someone already built L.TileLayer.Canvas for vanilla leaflet. So all we need to do is bring it into react-leaflet. You can follow these docs on the react-leaflet website to create a custom react-leaflet component that is built from L.TileLayer.Canvas:

import { createLayerComponent } from "@react-leaflet/core";
import L from "leaflet";
import "tilelayer-canvas";

const createLayer = (props, context) => {
  const instance = L.tileLayer.canvas(props.url, { ...props });

  return { instance, context };
};

const updateLayer = (instance, props, prevProps) => {
  // you may not need all of these
  //or you may need others I didn't include here:
  if (prevProps.url !== props.url) {
    if (instance.setUrl) instance.setUrl(props.url);
  }
  if (prevProps.opacity !== props.opacity) {
    if (instance.setOpacity) instance.setOpacity(props.opacity);
  }
  if (prevProps.zIndex !== props.zIndex) {
    if (instance.setZIndex) instance.setZIndex(props.zIndex);
  }
};

const TilelayerCanvas = createLayerComponent(createLayer, updateLayer);

export default TilelayerCanvas;

Now in your map:

import TilelayerCanvas from "./TilelayerCanvas";

const Map = (props) => {
  return (
    <MapContainer ... >
      <TilelayerCanvas url="the_url" attribution="some_attribution" />
    </MapContainer>
  );
};

export default Map;

Working codesandbox

If you check the devtools, you'll see that tiles are rendered as canvas elements instead of img elements.