Implementing postcompose functionality from OpenLayers 3 into an OpenLayers 6 event within a Vue Single Page

Check out this codepen I created where I modify the basemap context of an OpenLayers map on the postcompose event. Now, I'm facing a challenge in translating this working demo to a Vue SPA using OpenLayers 6. I want to link the dark mode map effect to a toggle button. However, the postcompose event behaves differently in OL6 compared to OL3, so I attempted to use the prerender event instead.

import TileLayer from 'ol/layer/Tile';
...
osm: new TileLayer({source: new OSM()})

In OL3, the following code successfully gives me a constant 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'm attempting to utilize getVectorContext as suggested in this answer, but for some reason, my code isn't achieving the desired dark mode effect:

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?

Answer №1

Visual effects are applied to the canvas display with style filters. To reverse the drawing, a difference globalCompositeOperation is required along with a 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 effectively implement on and un, the following structure should be used:

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);

When using on, ensure to utilize the corresponding key with unByKey method. Reference:

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to create an input or form that is based on a select menu (dropdown list)?

I am currently developing a grading system and working on the form where users enter students' results. The form includes two dropdown lists (classroom, students) that are interdependent. My challenge lies in... When the user selects a classroom, the ...

Access-Control-Allow-Origin does not permit AngularJS Origin http://localhost:8080

I'm working on a web application using AngularJS. It's an admin interface that depends on a json-rpc API hosted on a different domain. When testing in my local environment, I encountered the error "Origin http://localhost:8080 is not allowed by ...

Make window.print() trigger the download of a PDF file

Recently, I added a new feature to my website allowing users to download the site's content by following these instructions: Printing with Javascript || Mozilla Dev. Essentially, the user can print a newly formatted site stored in a hidden iframe. Now ...

Modal closes automatically after being clicked twice on data-bs-dismiss

Having an odd issue with Bootstrap 5.0.2 In order to close the modal, I have to double-click the button (data-bs-dismiss) It's worth noting that I use multiple modals on the page, but they only open when needed. <div class="modal" id="open-rat ...

Access information from AJAX request beyond the success function

Currently, I am utilizing beautiful soup to extract information from a webpage. A user inputs their zip code upon arriving at the main page of my site, triggering an AJAX request to Django for data retrieval based on the input. The corresponding JavaScript ...

I am not sure how to properly execute this asynchronously

I have compiled a comprehensive list of everything related to this command I've been developing, and here is the error it keeps throwing at me. I tried testing if the image search feature was working properly, but instead, here's what I got: Synt ...

Developing dynamic progress indicators in Django - A guide

I'm in the process of figuring out how to create a real-time progress bar for updating. The idea is that the server will update the user periodically on the current progress. Fortunately, I am familiar with making an Ajax call using Django and jQuery ...

Showing the `ViewBag` data within the `@Html.DropDownListFor` method enclosed

I'm currently working with a DropDownListFor that is set up like this: <div class="form-horizontal" id=CurrencyDataBlock> @Html.DropDownListFor(model => model.Code, ViewBag.Currency as SelectList, "--Select Currency--", n ...

What is the best way to integrate a Ruby object into JavaScript?

I'm attempting to integrate Ruby into the JS.erb file, where I need access to the @user object and its relationships. Instead of converting to a JSON object, I prefer the ERB to precompile on the server-side. My goal is to iterate in the JS file rat ...

Creating dynamic animations in React/Redux that respond to user actions

As I near the completion of my first React+Redux application, I am eager to incorporate animations throughout the different sections of the project. However, I have found that the existing solutions do not quite meet my expectations. The ReactCSSTransition ...

What is the best way to store HTML in a variable as a string?

Imagine if I have a variable: let display_text = "Cats are pawsome!" I aim to show it as such: <div> <b>Cats</b> are pawsome! </div> To be clear, dynamically enclose the word "cats" whenever it shows up. ...

Guidelines on utilizing the information collected from social signups to seamlessly guide users towards finishing the registration process in [passport, node.js]

As a newcomer to the realm of node.js for a few months, I am still grappling with some concepts. Specifically, I need guidance on redirecting users to complete their registration using data acquired from social signups such as Facebook or Google. Current ...

What is the reason for React Native's absence of justify-self functionality?

I'm trying to figure out how to align an item on the right side of a row with other children aligned to the left. While I could use "position: absolute; right: 0" to achieve this, I'm curious if there's a more efficient way to do so. It seem ...

What is the best way to create a screen capture of a webpage using a URL?

Currently working on a Spring MVC website project, I have implemented a form requesting the user's website URL. Once entered, I aim to display a screenshot of the specified website for the user to view. Contemplating whether to generate the image on ...

Service has not been properly declared

I encountered an issue with AngularJS where I am struggling to import a Service from one module to another. In the Data module, I have a Service named MenuDataService that I need to utilize in the MenuApp module. However, when attempting to do so, I receiv ...

Spring framework experiencing issues with loading CSS and JavaScript files

I'm facing an issue with my Spring-MVC application where the CSS and JS files are not loading even though they are correctly called. Can anyone help me figure out what I'm doing wrong? Resources folder: Project --> webapp --> resources - ...

Exploring History Navigation with AngularJS: Leveraging $routeParams and $location

I have been diligently working on developing a question bank and have successfully coded most of the elements. However, I would like to enhance the user experience by implementing browser history for easy navigation through the questions. If you want to c ...

Issue with timestamp in the Instagram API call to retrieve media using AJAX (GET /media/search)

My API call is returning a 400 bad request error message: {"meta":{"error_type":"APIInvalidParametersError","code":400,"error_message":"invalid parameters-check the max\/min-timestamps, if you supplied them"}} Even though my request appears to be co ...

Unable to execute an Angular 2 application within Visual Studio 2015

I encountered an error while trying to set up an environment on VS 2015 with Angular2. Whenever I run the command "npm start," I receive the following error message. I attempted using "npm cache clean --force" before running "npm start," but the error pers ...

Deactivate PyV8's automatic garbage collection functionality

I am currently experiencing an issue that appears to be stemming from the interaction between Python and PyV8's garbage collection processes. To temporarily address this problem, I have disabled Python's garbage collection and implemented a worka ...