Skip to content

Maplibre Opacity Slider

Use Maplibre adjust opacity of the map

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Adjust a layer's opacity</title>
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
    <link rel="stylesheet" href="https://unpkg.com/maplibre-gl@3.6.0/dist/maplibre-gl.css" />
    <script
      src="https://unpkg.com/maplibre-gl@3.6.0/dist/maplibre-gl.js"
      integrity="sha384-EYXW8ZhnlWPofZd2XtrFMKIJZXd8x1vTls280ww621jL26+4304H6YXxvU6RcKYd"
      crossorigin="anonymous"
    ></script>
    <style>
      body {
        margin: 0;
        padding: 0;
        overflow: hidden;
      }

      body * {
        -webkit-touch-callout: none;
        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
      }

      .map {
        position: absolute;
        top: 0;
        bottom: 0;
        width: 100%;
      }

      .map-overlay {
        font: bold 12px/20px 'Helvetica Neue', Arial, Helvetica, sans-serif;
        position: absolute;
        width: 25%;
        top: 0;
        left: 0;
        padding: 10px;
      }

      .map-overlay .map-overlay-inner {
        background-color: #fff;
        box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
        border-radius: 3px;
        padding: 10px;
        margin-bottom: 10px;
      }

      .map-overlay label {
        display: block;
        margin: 0 0 10px;
      }

      .map-overlay input {
        background-color: transparent;
        display: inline-block;
        width: 100%;
        position: relative;
        margin: 0;
        cursor: ew-resize;
      }
    </style>
  </head>

  <body>
    <div id="raster" class="map"></div>
    <div id="vector" class="map"></div>

    <div class="map-overlay top">
      <div class="map-overlay-inner">
        <label>Layer opacity: <span id="slider-value">100%</span></label>
        <input id="slider" type="range" min="0" max="100" step="0" value="100" />
      </div>
    </div>

    <script>
      // vector layers
      const styleUrl =
        'https://basemaps.linz.govt.nz/v1/tiles/topographic/WebMercatorQuad/style/topographic.json?api=d01hep5551e30kxb7w85hck49tp';

      // Raster layers
      const url =
        'https://basemaps.linz.govt.nz/v1/tiles/aerial/WebMercatorQuad/{z}/{x}/{y}.webp?api=d01hep5551e30kxb7w85hck49tp';

      const startPos = [173, -40.5];
      const startZoom = 6;

      var raster = new maplibregl.Map({
        container: 'raster', // Container ID
        style: {
          version: 8,
          sources: {
            'raster-tiles': {
              type: 'raster',
              tiles: [url],
              tileSize: 256,
            },
          },
          layers: [
            {
              id: 'LINZ Raster Basemaps',
              type: 'raster',
              source: 'raster-tiles',
            },
          ],
        },
        center: startPos,
        zoom: startZoom,
      });

      var vector = new maplibregl.Map({
        container: 'vector', // Container ID
        style: styleUrl,
        center: startPos,
        zoom: startZoom,
      });

      var slider = document.getElementById('slider');
      var sliderValue = document.getElementById('slider-value');

      vector.on('load', function () {
        slider.addEventListener('input', function (e) {
          var vectorMap = document.querySelector('#vector');
          vectorMap.style.opacity = parseInt(e.target.value, 10) / 100;
          sliderValue.textContent = e.target.value + '%';
        });
      });

      // Copyright (c) 2016, Mapbox - ISC License
      // https://github.com/mapbox/mapbox-gl-sync-move
      function moveToMapPosition(master, clones) {
        let center = master.getCenter();
        let zoom = master.getZoom();
        let bearing = master.getBearing();
        let pitch = master.getPitch();

        clones.forEach(function (clone) {
          clone.jumpTo({
            center: center,
            zoom: zoom,
            bearing: bearing,
            pitch: pitch,
          });
        });
      }

      // Sync movements of two maps.
      //
      // All interactions that result in movement end up firing
      // a "move" event. The trick here, though, is to
      // ensure that movements don't cycle from one map
      // to the other and back again, because such a cycle
      // - could cause an infinite loop
      // - prematurely halts prolonged movements like
      //   double-click zooming, box-zooming, and flying
      function syncMaps() {
        let maps;
        let argLen = arguments.length;
        if (argLen === 1) {
          maps = arguments[0];
        } else {
          maps = [];
          for (let i = 0; i < argLen; i++) {
            maps.push(arguments[i]);
          }
        }

        // Create all the movement functions, because if they're created every time
        // they wouldn't be the same and couldn't be removed.
        let fns = [];
        maps.forEach(function (map, index) {
          fns[index] = sync.bind(
            null,
            map,
            maps.filter(function (o, i) {
              return i !== index;
            }),
          );
        });

        function on() {
          maps.forEach(function (map, index) {
            map.on('move', fns[index]);
          });
        }

        function off() {
          maps.forEach(function (map, index) {
            map.off('move', fns[index]);
          });
        }

        // When one map moves, we turn off the movement listeners
        // on all the maps, move it, then turn the listeners on again
        function sync(master, clones) {
          off();
          moveToMapPosition(master, clones);
          on();
        }

        on();
        return function () {
          off();
          fns = [];
          maps = [];
        };
      }

      syncMaps(raster, vector);
    </script>
  </body>
</html>