Blend Modes

This example uses OpenLayers v4.0.1. The latest is v.

#### Blend Modes

Canvas compositing / blending mode Red circle affected Green circle affected Blue circle affected

Shows how to change the canvas compositing / blending mode in post- and precompose eventhandlers.

This example shows how to change the canvas compositing / blending mode in post- and precompose event handlers. The Canvas 2D API provides the property `globalCompositeOperation` with which one can influence which composition operation will be used when drawing on the canvas. The various options are well described on the MDN documentation page.

In this example three circles on the corners of an equilateral triangle are drawn with red, green or blue styles respectively. By setting the `globalCompositeOperation` you can change how these colors turn out when they are combined on the map.

You can select an operation in the select-field and you can also control which layers will be affected by the chosen operation through the layer checkboxes.

Related API documentation:

- ol.Feature
,
- ol.Map
,
- ol.View
,
- ol.geom.Point
,
- ol.layer.Vector
,
- ol.source.Vector
,
- ol.style.Circle
,
- ol.style.Fill
,
- ol.style.Stroke
,
- ol.style.Style

Copy Edit
Blend Modes Shows how to change the canvas compositing / blending mode in post- and precompose eventhandlers. // Create separate layers for red, green an blue circles. // // Every layer has one feature that is styled with a circle, together the // features form the corners of an equilateral triangle and their styles overlap var redLayer = new ol.layer.Vector({ source: new ol.source.Vector({ features: [new ol.Feature(new ol.geom.Point([0, 0]))] }), style: new ol.style.Style({ image: new ol.style.Circle({ fill: new ol.style.Fill({ color: 'rgba(255,0,0,0.8)' }), stroke: new ol.style.Stroke({ color: 'rgb(255,0,0)', width: 15 }), radius: 120 }) }) }); var greenLayer = new ol.layer.Vector({ source: new ol.source.Vector({ // 433.013 is roughly 250 * Math.sqrt(3) features: [new ol.Feature(new ol.geom.Point([250, 433.013]))] }), style: new ol.style.Style({ image: new ol.style.Circle({ fill: new ol.style.Fill({ color: 'rgba(0,255,0,0.8)' }), stroke: new ol.style.Stroke({ color: 'rgb(0,255,0)', width: 15 }), radius: 120 }) }) }); var blueLayer = new ol.layer.Vector({ source: new ol.source.Vector({ features: [new ol.Feature(new ol.geom.Point([500, 0]))] }), style: new ol.style.Style({ image: new ol.style.Circle({ fill: new ol.style.Fill({ color: 'rgba(0,0,255,0.8)' }), stroke: new ol.style.Stroke({ color: 'rgb(0,0,255)', width: 15 }), radius: 120 }) }) }); // Create the map, the view is centered on the triangle. Zooming and panning is // restricted to a sane area var map = new ol.Map({ layers: [ redLayer, greenLayer, blueLayer ], target: 'map', view: new ol.View({ center: [250, 220], extent: [0, 0, 500, 500], resolution: 4, minResolution: 2, maxResolution: 32 }) }); // Get the form elements and bind the listeners var select = document.getElementById('blend-mode'); var affectRed = document.getElementById('affect-red'); var affectGreen = document.getElementById('affect-green'); var affectBlue = document.getElementById('affect-blue'); /** * This method sets the globalCompositeOperation to the value of the select * field and it is bound to the precompose event of the layers. * * @param {ol.render.Event} evt The render event. */ var setBlendModeFromSelect = function(evt) { evt.context.globalCompositeOperation = select.value; }; /** * This method resets the globalCompositeOperation to the default value of * 'source-over' and it is bound to the postcompose event of the layers. * * @param {ol.render.Event} evt The render event. */ var resetBlendModeFromSelect = function(evt) { evt.context.globalCompositeOperation = 'source-over'; }; /** * Bind the pre- and postcompose handlers to the passed layer. * * @param {ol.layer.Vector} layer The layer to bind the handlers to. */ var bindLayerListeners = function(layer) { layer.on('precompose', setBlendModeFromSelect); layer.on('postcompose', resetBlendModeFromSelect); }; /** * Unind the pre- and postcompose handlers to the passed layers. * * @param {ol.layer.Vector} layer The layer to unbind the handlers from. */ var unbindLayerListeners = function(layer) { layer.un('precompose', setBlendModeFromSelect); layer.un('postcompose', resetBlendModeFromSelect); }; /** * Handler for the click event of the 'affect-XXX' checkboxes. * * @this {HTMLInputElement} */ var affectLayerClicked = function() { var layer; if (this.id == 'affect-red') { layer = redLayer; } else if (this.id == 'affect-green') { layer = greenLayer; } else { layer = blueLayer; } if (this.checked) { bindLayerListeners(layer); } else { unbindLayerListeners(layer); } map.render(); }; // Rerender map when blend mode changes select.addEventListener('change', function() { map.render(); }); // Unbind / bind listeners depending on the checked state when the checkboxes // are clicked affectRed.addEventListener('click', affectLayerClicked); affectGreen.addEventListener('click', affectLayerClicked); affectBlue.addEventListener('click', affectLayerClicked); // Initially bind listeners bindLayerListeners(redLayer); bindLayerListeners(greenLayer); bindLayerListeners(blueLayer); .map{ background-repeat: repeat; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAApSURBVBiVY7x///5/BjSgqKjIiC7GhC6ACwygQgxHMzAwMGDz4FDwDAD5/wevjSk4mwAAAABJRU5ErkJggg==); } <div id="map" class="map"></div> <form class="form-horizontal"> <label> <select id="blend-mode" class="form-control"> <option value="source-over">source-over (default)</option> <option>source-in</option> <option>source-out</option> <option>source-atop</option> <option>destination-over</option> <option>destination-in</option> <option>destination-out</option> <option>destination-atop</option> <option>lighter</option> <option>copy</option> <option>xor</option> <option>multiply</option> <option>screen</option> <option>overlay</option> <option>darken</option> <option>lighten</option> <option>color-dodge</option> <option>color-burn</option> <option>hard-light</option> <option>soft-light</option> <option selected>difference</option> <option>exclusion</option> <option>hue</option> <option>saturation</option> <option>color</option> <option>luminosity</option> </select> Canvas compositing / blending mode </label> <label> <input type="checkbox" id="affect-red" checked> Red circle affected </label> <label> <input type="checkbox" id="affect-green" checked> Green circle affected </label> <label> <input type="checkbox" id="affect-blue" checked> Blue circle affected </label> </form>

    <!DOCTYPE html>
    <html>
      <head>
        <title>Blend Modes</title>
        <link rel="stylesheet" href="https://openlayers.org/en/v4.0.1/css/ol.css" type="text/css">
        <!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
        <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL"></script>
        <script src="https://openlayers.org/en/v4.0.1/build/ol.js"></script>
        <style>
          .map{
            background-repeat: repeat;
            background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAApSURBVBiVY7x///5/BjSgqKjIiC7GhC6ACwygQgxHMzAwMGDz4FDwDAD5/wevjSk4mwAAAABJRU5ErkJggg==);
          }
        </style>
      </head>
      <body>
        <div id="map" class="map"></div>
        <form class="form-horizontal">
          <label>
            <select id="blend-mode" class="form-control">
              <option value="source-over">source-over (default)</option>
              <option>source-in</option>
              <option>source-out</option>
              <option>source-atop</option>
              <option>destination-over</option>
              <option>destination-in</option>
              <option>destination-out</option>
              <option>destination-atop</option>
              <option>lighter</option>
              <option>copy</option>
              <option>xor</option>
              <option>multiply</option>
              <option>screen</option>
              <option>overlay</option>
              <option>darken</option>
              <option>lighten</option>
              <option>color-dodge</option>
              <option>color-burn</option>
              <option>hard-light</option>
              <option>soft-light</option>
              <option selected>difference</option>
              <option>exclusion</option>
              <option>hue</option>
              <option>saturation</option>
              <option>color</option>
              <option>luminosity</option>
            </select>
            Canvas compositing / blending mode
          </label>
          <label>
            <input type="checkbox" id="affect-red" checked>
            Red circle affected
          </label>
          <label>
            <input type="checkbox" id="affect-green" checked>
            Green circle affected
          </label>
          <label>
            <input type="checkbox" id="affect-blue" checked>
            Blue circle affected
          </label>
        </form>
        <script>
          // Create separate layers for red, green an blue circles.
          //
          // Every layer has one feature that is styled with a circle, together the
          // features form the corners of an equilateral triangle and their styles overlap
          var redLayer = new ol.layer.Vector({
            source: new ol.source.Vector({
              features: [new ol.Feature(new ol.geom.Point([0, 0]))]
            }),
            style: new ol.style.Style({
              image: new ol.style.Circle({
                fill: new ol.style.Fill({
                  color: 'rgba(255,0,0,0.8)'
                }),
                stroke: new ol.style.Stroke({
                  color: 'rgb(255,0,0)',
                  width: 15
                }),
                radius: 120
              })
            })
          });
          var greenLayer = new ol.layer.Vector({
            source: new ol.source.Vector({
              // 433.013 is roughly 250 * Math.sqrt(3)
              features: [new ol.Feature(new ol.geom.Point([250, 433.013]))]
            }),
            style: new ol.style.Style({
              image: new ol.style.Circle({
                fill: new ol.style.Fill({
                  color: 'rgba(0,255,0,0.8)'
                }),
                stroke: new ol.style.Stroke({
                  color: 'rgb(0,255,0)',
                  width: 15
                }),
                radius: 120
              })
            })
          });
          var blueLayer = new ol.layer.Vector({
            source: new ol.source.Vector({
              features: [new ol.Feature(new ol.geom.Point([500, 0]))]
            }),
            style: new ol.style.Style({
              image: new ol.style.Circle({
                fill: new ol.style.Fill({
                  color: 'rgba(0,0,255,0.8)'
                }),
                stroke: new ol.style.Stroke({
                  color: 'rgb(0,0,255)',
                  width: 15
                }),
                radius: 120
              })
            })
          });

          // Create the map, the view is centered on the triangle. Zooming and panning is
          // restricted to a sane area
          var map = new ol.Map({
            layers: [
              redLayer,
              greenLayer,
              blueLayer
            ],
            target: 'map',
            view: new ol.View({
              center: [250, 220],
              extent: [0, 0, 500, 500],
              resolution: 4,
              minResolution: 2,
              maxResolution: 32
            })
          });

          // Get the form elements and bind the listeners
          var select = document.getElementById('blend-mode');
          var affectRed = document.getElementById('affect-red');
          var affectGreen = document.getElementById('affect-green');
          var affectBlue = document.getElementById('affect-blue');

          /**
           * This method sets the globalCompositeOperation to the value of the select
           * field and it is bound to the precompose event of the layers.
           *
           * @param {ol.render.Event} evt The render event.
           */
          var setBlendModeFromSelect = function(evt) {
            evt.context.globalCompositeOperation = select.value;
          };

          /**
           * This method resets the globalCompositeOperation to the default value of
           * 'source-over' and it is bound to the postcompose event of the layers.
           *
           * @param {ol.render.Event} evt The render event.
           */
          var resetBlendModeFromSelect = function(evt) {
            evt.context.globalCompositeOperation = 'source-over';
          };

          /**
           * Bind the pre- and postcompose handlers to the passed layer.
           *
           * @param {ol.layer.Vector} layer The layer to bind the handlers to.
           */
          var bindLayerListeners = function(layer) {
            layer.on('precompose', setBlendModeFromSelect);
            layer.on('postcompose', resetBlendModeFromSelect);
          };

          /**
           * Unind the pre- and postcompose handlers to the passed layers.
           *
           * @param {ol.layer.Vector} layer The layer to unbind the handlers from.
           */
          var unbindLayerListeners = function(layer) {
            layer.un('precompose', setBlendModeFromSelect);
            layer.un('postcompose', resetBlendModeFromSelect);
          };

          /**
           * Handler for the click event of the 'affect-XXX' checkboxes.
           *
           * @this {HTMLInputElement}
           */
          var affectLayerClicked = function() {
            var layer;
            if (this.id == 'affect-red') {
              layer = redLayer;
            } else if (this.id == 'affect-green') {
              layer = greenLayer;
            } else {
              layer = blueLayer;
            }
            if (this.checked) {
              bindLayerListeners(layer);
            } else {
              unbindLayerListeners(layer);
            }
            map.render();
          };

          // Rerender map when blend mode changes
          select.addEventListener('change', function() {
            map.render();
          });

          // Unbind / bind listeners depending on the checked state when the checkboxes
          // are clicked
          affectRed.addEventListener('click', affectLayerClicked);
          affectGreen.addEventListener('click', affectLayerClicked);
          affectBlue.addEventListener('click', affectLayerClicked);

          // Initially bind listeners
          bindLayerListeners(redLayer);
          bindLayerListeners(greenLayer);
          bindLayerListeners(blueLayer);
        </script>
      </body>
    </html>