I have made this codepen where I manipulate the context of the basemap of a OpenLayers map on postcompose
. The problem I have is translating this working demo to a Vue SPA which uses OpenLayers 6 where I would like to link the dark mode map effect to a toggle. The postcompose
event is not fired the same way for OL6 as it did for OL3 so I tried hooking onto prerender
.
import TileLayer from 'ol/layer/Tile';
...
osm: new TileLayer({source: new OSM()})
In OpenLayers 3 the following code gets me a permanently dark mode map :
OSM_LAYER.on('postcompose', function (evt) {
evt.context.globalCompositeOperation = 'color';
evt.context.fillStyle = 'rgba(0,0,0,' 1.0 ')';
evt.context.fillRect(0, 0, evt.context.canvas.width, evt.context.canvas.height);
evt.context.globalCompositeOperation = 'overlay';
evt.context.fillStyle = 'rgb(' [200,200,200].toString() ')';
evt.context.fillRect(0, 0, evt.context.canvas.width, evt.context.canvas.height);
evt.context.globalCompositeOperation = 'source-over';
document.querySelector('canvas').style.filter="invert(99%)";
});
Currently I am trying to use getVectorContext
as per this answer but I am not understanding something since my code does not give me the correct dark mode :
OSM_LAYER.on('prerender', function (evt) {
var ctx = getVectorContext(evt).context_
ctx.globalCompositeOperation = ‘color’;
ctx.fillStyle = ‘rgba(0,0,0,’ 1.0 ‘)’;
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.globalCompositeOperation = ‘overlay’;
ctx.fillStyle = ‘rgb(‘ [200,200,200].toString() ‘)’;
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.globalCompositeOperation = ‘source-over’;
document.querySelector('canvas').style.filter="invert(99%)";
});
EDIT : Thanks to Mr. Mike's answer I am able to replicate the look of the OL3 dark mode. The problem now is that I actually use this basemap to create an animation where I update the time parameters of all layers in a for loop and then extract the map canvas using :
getMapCanvas () {
var mapCanvas = document.createElement('canvas');
var divElement = document.querySelector(".map");
mapCanvas.width = divElement.offsetWidth;//size[0];
mapCanvas.height = divElement.offsetHeight;//size[1];
var mapContext = mapCanvas.getContext('2d');
Array.prototype.forEach.call(
document.querySelectorAll('.ol-layer canvas'),
function (canvas) {
if (canvas.width > 0) {
const opacity = canvas.parentNode.style.opacity;
mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity);
const transform = canvas.style.transform;
const matrix = transform
.match(/^matrix\(([^\(]*)\)$/)[1] //eslint-disable-line
.split(',')
.map(Number);
CanvasRenderingContext2D.prototype.setTransform.apply(mapContext,matrix);
mapContext.drawImage(canvas, 0, 0);
}
}
);
return mapCanvas;
},
which is called only after a rendercomplete
promise is resolved. Using Mr. Mike's method I get a weird white hue instead of the dark mode OSM and I think it is because of how I am exporting the canvas in the getMapCanvas
function. Can someone help me out debug this?
CodePudding user response:
Style filters only affect how the canvas appears on screen. To invert what is drawn needs a difference
globalCompositeOperation with white fill.
this.map.getLayers().getArray()[0].on(['postrender'], (evt) => {
if ( this.darkOSM !== null && this.darkOSM === false ) {
evt.context.globalCompositeOperation = 'color';
evt.context.fillStyle = 'rgba(0,0,0,' 1.0 ')';
evt.context.fillRect(0, 0, evt.context.canvas.width, evt.context.canvas.height);
evt.context.globalCompositeOperation = 'overlay';
evt.context.fillStyle = 'rgb(' [200,200,200].toString() ')';
evt.context.fillRect(0, 0, evt.context.canvas.width, evt.context.canvas.height);
evt.context.globalCompositeOperation = 'difference';
evt.context.fillStyle = 'rgba(255,255,255,' 0.9 ')';
evt.context.fillRect(0, 0, evt.context.canvas.width, evt.context.canvas.height);
evt.context.globalCompositeOperation = 'source-over';
}
});
To use on
and un
you would need:
this.myFunction = (evt) => {
if ( this.darkOSM !== null && this.darkOSM === false ) {
evt.context.globalCompositeOperation = 'color';
evt.context.fillStyle = 'rgba(0,0,0,' 1.0 ')';
evt.context.fillRect(0, 0, evt.context.canvas.width, evt.context.canvas.height);
evt.context.globalCompositeOperation = 'overlay';
evt.context.fillStyle = 'rgb(' [200,200,200].toString() ')';
evt.context.fillRect(0, 0, evt.context.canvas.width, evt.context.canvas.height);
evt.context.globalCompositeOperation = 'difference';
evt.context.fillStyle = 'rgba(255,255,255,' 0.9 ')';
evt.context.fillRect(0, 0, evt.context.canvas.width, evt.context.canvas.height);
evt.context.globalCompositeOperation = 'source-over';
}
};
this.map.getLayers().getArray()[0].on(['postrender'], this.myFunction);
this.map.getLayers().getArray()[0].un(['postrender'], this.myFunction);
If you save a key from on
it must be used with unByKey
https://openlayers.org/en/latest/apidoc/module-ol_Observable.html#.unByKey