Edit

Layer Swipe (WebGL)

swipe2 webgl18

Cropping a WebGL tile layer

The prerender and postrender events on a WebGL tile layer can be used to manipulate the WebGL context before and after rendering. In this case, the gl.scissor() method is called to clip the top layer based on the position of a slider.

Note: This example is minimalist and works as there is only one WebGL layer rendered. That might works as well if the layers are drawn to different contexts. This does not apply to to several layers drawn to a single context, such as set with the 'className' property to 'canvas3d' on multiple layers. In such case, it's would be wise adapt your code using 'gl.clear' and other tweaks to the prerender/postrender event.

main.js
import Map from 'ol/Map.js';
import View from 'ol/View.js';
import TileLayer from 'ol/layer/WebGLTile.js';
import {getRenderPixel} from 'ol/render.js';
import ImageTile from 'ol/source/ImageTile.js';
import OSM from 'ol/source/OSM.js';

const osm = new TileLayer({
  source: new OSM({wrapX: true}),
});

const key = 'Get your own API key at https://www.maptiler.com/cloud/';

const imagery = new TileLayer({
  source: new ImageTile({
    url: 'https://api.maptiler.com/maps/satellite/{z}/{x}/{y}.jpg?key=' + key,
    attributions:
      '<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
      '<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>',
    maxZoom: 20,
  }),
});

const map = new Map({
  layers: [osm, imagery],
  target: 'map',
  view: new View({
    center: [0, 0],
    zoom: 2,
  }),
});

const swipe = document.getElementById('swipe');

imagery.on('prerender', function (event) {
  const gl = event.context;
  gl.enable(gl.SCISSOR_TEST);

  const mapSize = map.getSize(); // [width, height] in CSS pixels

  // get render coordinates and dimensions given CSS coordinates
  const bottomLeft = getRenderPixel(event, [0, mapSize[1]]);
  const topRight = getRenderPixel(event, [mapSize[0], 0]);

  const width = Math.round(
    (topRight[0] - bottomLeft[0]) * (Number(swipe.value) / 100),
  );
  const height = topRight[1] - bottomLeft[1];

  gl.scissor(bottomLeft[0], bottomLeft[1], width, height);
});

imagery.on('postrender', function (event) {
  const gl = event.context;
  gl.disable(gl.SCISSOR_TEST);
});

swipe.addEventListener('input', function () {
  map.render();
});
index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Layer Swipe (WebGL)</title>
    <link rel="stylesheet" href="node_modules/ol/ol.css">
    <style>
      .map {
        width: 100%;
        height: 400px;
      }
    </style>
  </head>
  <body>
    <div id="map" class="map"></div>
    <input id="swipe" type="range" style="width: 100%">

    <script type="module" src="main.js"></script>
  </body>
</html>
package.json
{
  "name": "webgl-layer-swipe",
  "dependencies": {
    "ol": "10.5.0"
  },
  "devDependencies": {
    "vite": "^3.2.3"
  },
  "scripts": {
    "start": "vite",
    "build": "vite build"
  }
}