Safari's use of threeJS and overlays

My website utilizes threeJS for rendering to a canvas with a white clear color set (renderer.setClearColor(0xffffff)).

There is also a div overlay displayed on top of the canvas for a drawer, which animates onto the screen.

The issue arises when this setup is executed on Safari, causing the overlay to render in an unusual manner.

https://i.sstatic.net/eAlUgUTv.gif

It's evident that the overlay effect appears differently on the canvas connected to threeJS compared to the canvas below where the result is copied into.

This discrepancy might be due to how Safari handles webGL connections, but is there any workaround available?

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title></title>
        <style>
            body { margin: 0; }
            canvas { display: block; }
        </style>
        <script type="importmap">
            {
            "imports": {
                "three": "https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="384c504a5d5d780816090e001608">[email protected]</a>/build/three.module.js"
            }
            }
        </script>
    </head>
    <body>
        <style>
            #overlay {
                position: fixed;
                left: 0px;
                top: 0px;
                width: 100vw;
                height: 100vh;
                background-color: rgba(0,0,0,0.2);
                pointer-events: none;
                opacity: 0;
                animation: fadein 0.3s ease-in-out forwards;
            }
            @keyframes fadein {
                0% {
                    opacity: 0;
                }
                100% {
                    opacity: 1;
                }
            }
        </style>
        <div id="container">

        </div>
        <canvas id="static"></canvas>
        <button id="button">test</button>
        <script>
            document.querySelector("#button").addEventListener("click", () => {
                if(document.querySelector("#overlay")) {
                    document.body.removeChild(document.querySelector("#overlay"))
                    return;
                }
                const div = document.createElement("div");
                div.id = "overlay";
                document.body.appendChild(div);
            })
        </script>
        <script type="module">
            import * as THREE from 'three';

            let camera, scene, renderer;
            let mesh;

            init();

            function init() {

                camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 100 );
                camera.position.z = 2;

                scene = new THREE.Scene();


                const geometry = new THREE.BoxGeometry();
                const material = new THREE.MeshBasicMaterial( { color: 0xff00ff } );

                mesh = new THREE.Mesh( geometry, material );
                scene.add( mesh );

                renderer = new THREE.WebGLRenderer( { antialias: true } );
                renderer.setClearColor(0xffffff)
                renderer.setPixelRatio( window.devicePixelRatio );
                renderer.setSize( 300, 300 );
                animate()
                //renderer.setAnimationLoop( animate );
                document.querySelector("#container").appendChild( renderer.domElement );

            }

            function animate() {

                mesh.rotation.x += 0.005;
                mesh.rotation.y += 0.01;

                renderer.render( scene, camera );

                document.querySelector("#static").getContext("2d").drawImage(renderer.domElement,0,0)

            }
        </script>
    </body>
</html>

Answer №1

The phenomenon of the "growing" effect can be attributed to various factors. The primary reason for this visual change is the addition and removal of the overlay element triggered by clicking a button. As a result, the browser adds the element, renders it simultaneously, and applies the animation to it.

In this case, instead of completely removing and reinserting the element, a more efficient solution is proposed below. This approach avoids unnecessary steps by simply toggling the opacity property. Additionally, the animation has been replaced with a transition for smoother visual effects. The transition eliminates concerns about iteration counts, starting/stopping the animation, etc.

Key modifications made include:

  • Substituting the animation with a transition
  • Including a persistent
    <div id="overlay"></div>
    element
  • Adjusting the body of the click event handler

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title></title>
        <style>
            body { margin: 0; }
            canvas { display: block; }
        </style>
        <script type="importmap">
            {
            "imports": {
                "three": "https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="16627e64737356263827202e3826">[email protected]</a>/build/three.module.js"
            }
            }
        </script>
    </head>
    <body>
        <style>
            #overlay {
                position: fixed;
                left: 0px;
                top: 0px;
                width: 100vw;
                height: 100vh;
                background-color: rgba(0,0,0,0.2);
                pointer-events: none;
                opacity: 0;
                /* REPLACED THE ANIMATION WITH A TRANSITION */
                transition: opacity 0.3s ease-in-out 0s;
                /* REPLACED THE ANIMATION WITH A TRANSITION */
            }
        </style>
        <div id="container">

        </div>
        <canvas id="static"></canvas>
        <button id="button">test</button>
        <!-- NEW OVERLAY ELEMENT -->
        <div id="overlay"></div>
        <!-- NEW OVERLAY ELEMENT -->
        <script>
            // CHANGED SCRIPT
            const overlayEl = document.querySelector('#overlay');
            document.querySelector("#button").addEventListener("click", () => {
                const opacity = Number(overlayEl.style.opacity);
                overlayEl.style.opacity = opacity === 1 ? 0 : 1;
            })
            // CHANGED SCRIPT
        </script>
        <script type="module">
            import * as THREE from 'three';

            let camera, scene, renderer;
            let mesh;

            init();

            function init() {

                camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 100 );
                camera.position.z = 2;

                scene = new THREE.Scene();


                const geometry = new THREE.BoxGeometry();
                const material = new THREE.MeshBasicMaterial( { color: 0xff00ff } );

                mesh = new THREE.Mesh( geometry, material );
                scene.add( mesh );

                renderer = new THREE.WebGLRenderer( { antialias: true } );
                renderer.setClearColor(0xffffff)
                renderer.setPixelRatio( window.devicePixelRatio );
                renderer.setSize( 300, 300 );
                animate()
                //renderer.setAnimationLoop( animate );
                document.querySelector("#container").appendChild( renderer.domElement );

            }

            function animate() {

                mesh.rotation.x += 0.005;
                mesh.rotation.y += 0.01;

                renderer.render( scene, camera );

                document.querySelector("#static").getContext("2d").drawImage(renderer.domElement,0,0)

            }
        </script>
    </body>
</html>

Upon reviewing the example using Chrome 128.0.6613.120 on MacOS, there seems to be no visible rendering on the second canvas. This observation is mentioned for reference purposes, even though it deviates from the original issue raised.

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

Display the internal array of meteor in the template

Currently, I am working with Meteor and am facing a challenge in accessing values stored within a field that operates as an internal array. After executing the query (with projection), I receive a single record structured like this: { "comments" : [ { "u ...

The click function in the <a> tag is malfunctioning

I am currently working on a dynamic menu system that pulls its items from a database. I need to determine the ID of the clicked menu item within a foreach loop in the following code: public function gen_menu($menuItems, $pId = 0) { $menu = &apos ...

Focusing on specific parents to target the same children using CSS and jQuery selectors

Is there a more efficient way to target specific children of select parents in CSS without excessive repetition and lengthening the selector? For instance, if I want to style the first-child paragraph within certain divs. The current CSS method: .funny ...

Import the JSON data into the designated storage unit

$(".btn-primary").click(function(){ runSuccessFunction(jsonObj); }); If I need to update the JSON data in the JSFiddle Code example, how can I reload only the container by clicking a button? I want to run the code every time I make a change to the J ...

Ensuring proper validation of sinon stub parameters in TypeScript

One of the unit tests in my code is responsible for checking the function arguments. it('Should retrieve product from the database', () => { stub(ProductModel, 'findById').returns({ lean: stub().returns({ total: 12 }), }); ...

Avoid changing the styling of the parent element when hovering over it

I am working on a comment box that allows for nested comments and is structured in the following way: <article class="comment"> Level 1 comment <article class="comment"> Level 2 comment </article> <article class="comment"& ...

Is there a way to eliminate get variables and filename from a URL using JavaScript or jQuery?

I've been researching this issue, but unfortunately, I haven't been able to find a definitive solution for my specific needs. Let's say I have a URL like... How can I extract this URL and remove the "index.php?search=my+search" part so that ...

Decoding JSON objects using JavaScript/jQuery

I am working with a Json object that looks like this. { "firstName":"John", "lastName":"Doe" "123":"456" } My goal is to access the keys and values in order to populate fields. However, for some reason it is not working as expected even though I have ch ...

Issue encountered when attempting to sign up a user with passport.js

I encounter a "Bad Request" message when attempting to create a new user using Postman with the following content: { "username": "username", "email": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="07626a666e6b47626a66 ...

Tips for resolving vertical alignment styling for images of varying sizes within Vue.js transition-group?

I have created an image slider example, but I'm facing a styling issue when using images of different dimensions. The element leaving the array is positioned absolutely for smooth transitions, but this causes the image to move up. I am trying to vert ...

Challenges with Knockout.js Virtual Elements in Different Environments

I am facing a peculiar issue where a virtual knockout template fails to bind correctly when accessed remotely, yet functions perfectly when viewed locally. You can find the problematic page here: Here is the template I am using: <ul> <!-- k ...

What is the best way to add an additional argument to a Django URL?

In my views.py for page1.html: def spwords(request, spray_id) if request.method == 'POST': value= request.POST["rangeVal"] #### the other argument that I want to pass to views.py of the second page html ... form = forms. ...

Preventing Cross-Site Scripting (XSS) vulnerabilities during the

When my site stores user-generated HTML to be rendered on a web page, what are the best practices to avoid XSS attacks? Is simply stripping <script> and <iframe> tags sufficient protection? Will this safeguard work across all browsers? I have h ...

Issue with Vuex not functioning properly in Nuxt.js

I'm facing an issue with setting the state in Vuex on my Nuxt.js App. It seems to not be working correctly. So, here is how I am trying to set the state using the fetch method: fetch({app, store, route}) { app.$axios.$get(`apps/${route.params ...

How to set a custom height for Mat-form-field in Angular 8 using pixel values

Is there a way to adjust the height of mat-form-field when using appearance="outline" to a specific pixel measurement, such as 40px (or any other value required by the UX team in the future)? I need to decrease the size of the mat-form-field. How can this ...

alter URL parameters dynamically during API invocation

Is there a way to call an API multiple times in one request? I need the ability to dynamically adjust the date parameters for each call so that I can retrieve data for different days simultaneously. While conducting research, I came across the following c ...

What is causing the col-auto with the red border to exceed its available height?

For this code, I've utilized version 5 of Bootstrap. In full screen width, the col-auto with the red border seems to be taking up more space than its content. The yellow border marks the available content space inside. I'm curious about what migh ...

integrating a ui-bootstrap modal in a sidebar using angularjs

I recently utilized the modal example from https://angular-ui.github.io/bootstrap/ and everything worked perfectly. By using the code snippet below, I was able to successfully open the modal as shown in the example: $scope.open = function (size) { v ...

Carry out numerous commitments across a range of elements

I have a list of objectIds and I want to perform operations on different collections based on each Id. I prefer executing the operations sequentially. var removeOperation = function(objectified){ return Comps.findOne({reviews : objectified}).populate([ ...

What is the process for altering the background using buttons in three.js?

As a newcomer to three.js, I'm looking for guidance on how to change the background by clicking a button. It seems like using "switch" and "break" might not be the right approach in this scenario. Would utilizing .loadTexture be a better alternative? ...