Move object with Easing – not working properly in Safari

Viewed 301

I created a custom cursor with JavaScript. – At the end of this question, I will add all the code, there is also a fiddle here: https://jsfiddle.net/8a9f2s0n/ …but what's really important is the following line of CSS: transition: all .1s ease-out;

Basically I'm deleting the actual cursor and then I'm chaining two circles to the mouse position, the second circle ("cursor2") has this CSS-property: transition: all .1s ease-out;, to make it move with some easing.

The Problem: I tested this code in all the big browsers and it's really smooth everywhere, except for Safari, in Safari the second, larger, circle move REALLY laggy.

What's going on?

var cursor = document.querySelector(".cursor");
var cursor2 = document.querySelector(".cursor_2");

var detectIfCursorStatic = undefined;

window.addEventListener('mousemove', function(e){
    // Chain "cursor" and "cursor2" to mouse-position. START
    cursor.style.top = e.y + "px";
    cursor.style.left = e.x + "px";
    
    cursor2.style.top = e.y + "px";
    cursor2.style.left = e.x + "px";
    // Chain "cursor" and "cursor2" to mouse-position. END
    
    cursor.style.display = "block";
    cursor2.style.display = "block";
    
    cursor2.style.transform = "translate(-50%, -50%) scale(1)";
    
    // Change cursor2, when mouse sits still. START
    clearTimeout(detectIfCursorStatic);
    detectIfCursorStatic = setTimeout(function(){
        
        cursor2.style.transform = "translate(-50%, -50%) scale(1.3)";
        setTimeout(function(){
            cursor2.style.transform = "translate(-50%, -50%) scale(0)";
        }, 200);
        
    }, 500);
    // Change cursor2, when mouse sits still. END
});

// Make it, so that when the cursor leaves the viewport, "cursor" and "cursor2" instantly disappear, but when entering the viewport, they blend in. START
document.addEventListener('mouseout', function(){
    cursor.style.opacity = "0";
    cursor2.style.opacity = "0";
    
    setTimeout(function(){
        cursor.style.transition = "opacity .5s linear"
    }, 500);
});

document.addEventListener('mouseover', function(){
    cursor.style.opacity = "1";
    cursor2.style.opacity = ".3";
    
    setTimeout(function(){
        cursor.style.transition = "opacity 0s linear"
    }, 500);
});
// … END

// Make it, so that, when the cursor is hovering over an object, with the class: "cursorInteraction", "cursor" and "cursor2" change colour. START
var cursorInteractionObjects = document.querySelectorAll(".cursorInteraction");

cursorInteractionObjects.forEach(function(element){
    element.addEventListener('mouseenter', function(){
        cursor.style.backgroundColor = "black";
        cursor2.style.backgroundColor = "black";
        cursor2.style.opacity = ".2";
    });
    
    element.addEventListener('mouseleave', function(){
        cursor.style.backgroundColor = "white";
        cursor2.style.backgroundColor = "white";
        cursor2.style.opacity = ".3";
    });
});
// … END

// Example: The custom-cursor is white, it is moved over an object, with the class: "cursorInteraction" and a background-color, of: white, the custom-cursor changes to black. – Inside the object, that the cursor is currently in, there is another object, a button, with: "background-color: black", on :hover, so the custom-cursor has to change again, in this case the custom-cursor will change in such a way, that it is no longer visible. – Such buttons, are assigned the following class: "cursorInteraction_Negative".
var cursorInteractionObjects_Negative = document.querySelectorAll(".cursorInteraction_Negative");

cursorInteractionObjects_Negative.forEach(function(element){
    element.addEventListener('mouseenter', function(){
        cursor.style.opacity = "0";
        cursor2.style.zIndex = "-1";
    });
    
    element.addEventListener('mouseleave', function(){
        cursor.style.opacity = "1";
        cursor2.style.zIndex = "0";
    });
});
// … END
@charset "UTF-8";
/* CSS Document */

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    list-style: none;
    cursor: none;
}

a {
    text-decoration: none;
    color: black;
}

a:hover {
    color: white;
}

body {
    background-color: black;
}

body, html {
    height: 100%; /* <- Firefox needs this. – This might not be necessary in the final website, because there will be objects spanning the whole viewport. */
}

.test_div {
    background-color: white;
    position: fixed;
    width: 400px;
    height: 200px;
    margin: 15px;
    
    display: flex;
    justify-content: center;
    align-items: center;
}

.test_button {
    border: 1px solid black;
    height: 50px;
    width: 150px;
    line-height: 50px;
    text-align: center;
    font-family: proxima-nova, sans-serif;
}

.test_button:hover {
    background-color: black;
}

/* Custom Cursor START */
.cursor {
    z-index: 10;
    width: .5rem;
    height: .5rem;
    border-radius: 50%;
    background-color: white;
    position: fixed;
    transform: translate(-50%, -50%);
    pointer-events: none;
    display: none;
}

.cursor_2 {
    z-index: 9;
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    background-color: white;
    opacity: .3;
    position: fixed;
    transform: translate(-50%, -50%);
    pointer-events: none;
    display: none;
    
    transition: all .1s ease-out;
}
/* Custom Cursor END */
<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Personal-Website Build-5</title>
        
        <link rel="stylesheet" href="style.css">
        
    </head>

    <body>
        <div class="test_div cursorInteraction">
            <a href="" class="test_button cursorInteraction_Negative"> Test Link </a>
        </div>
        
        <!-- Custom-Cursor START -->
        <div class="cursor"></div>
        <div class="cursor_2"></div>
        
        <script src="scripts/customCursor.js"></script>
        <!-- Custom-Cursor END -->
    </body>
</html>

1 Answers

It seems like in Safari every time the transition property is updated it resets the transition effect. This is causing the lag effect. To get around this you can throttle the code that updates cursor2 position using underscore throttle function or your preferred implementation.

Example is below:

var cursor = document.querySelector(".cursor");
var cursor2 = document.querySelector(".cursor_2");

var detectIfCursorStatic = undefined;

var updateCursor2 = _(function(e) {
   cursor2.style.top = e.y + "px";
   cursor2.style.left = e.x + "px";
}).throttle(50);


window.addEventListener('mousemove', function(e){
    // Chain "cursor" and "cursor2" to mouse-position. START
    cursor.style.top = e.y + "px";
    cursor.style.left = e.x + "px";
    
    updateCursor2(e);
    // Chain "cursor" and "cursor2" to mouse-position. END
    
    cursor.style.display = "block";
    cursor2.style.display = "block";
    
    cursor2.style.transform = "translate(-50%, -50%) scale(1)";
    
    // Change cursor2, when mouse sits still. START
    clearTimeout(detectIfCursorStatic);
    detectIfCursorStatic = setTimeout(function(){
        
        cursor2.style.transform = "translate(-50%, -50%) scale(1.3)";
        setTimeout(function(){
            cursor2.style.transform = "translate(-50%, -50%) scale(0)";
        }, 200);
        
    }, 500);
    // Change cursor2, when mouse sits still. END
});

// Make it, so that when the cursor leaves the viewport, "cursor" and "cursor2" instantly disappear, but when entering the viewport, they blend in. START
document.addEventListener('mouseout', function(){
    cursor.style.opacity = "0";
    cursor2.style.opacity = "0";
    
    setTimeout(function(){
        cursor.style.transition = "opacity .5s linear"
    }, 500);
});

document.addEventListener('mouseover', function(){
    cursor.style.opacity = "1";
    cursor2.style.opacity = ".3";
    
    setTimeout(function(){
        cursor.style.transition = "opacity 0s linear"
    }, 500);
});
// … END

// Make it, so that, when the cursor is hovering over an object, with the class: "cursorInteraction", "cursor" and "cursor2" change colour. START
var cursorInteractionObjects = document.querySelectorAll(".cursorInteraction");

cursorInteractionObjects.forEach(function(element){
    element.addEventListener('mouseenter', function(){
        cursor.style.backgroundColor = "black";
        cursor2.style.backgroundColor = "black";
        cursor2.style.opacity = ".2";
    });
    
    element.addEventListener('mouseleave', function(){
        cursor.style.backgroundColor = "white";
        cursor2.style.backgroundColor = "white";
        cursor2.style.opacity = ".3";
    });
});
// … END

// Example: The custom-cursor is white, it is moved over an object, with the class: "cursorInteraction" and a background-color, of: white, the custom-cursor changes to black. – Inside the object, that the cursor is currently in, there is another object, a button, with: "background-color: black", on :hover, so the custom-cursor has to change again, in this case the custom-cursor will change in such a way, that it is no longer visible. – Such buttons, are assigned the following class: "cursorInteraction_Negative".
var cursorInteractionObjects_Negative = document.querySelectorAll(".cursorInteraction_Negative");

cursorInteractionObjects_Negative.forEach(function(element){
    element.addEventListener('mouseenter', function(){
        cursor.style.opacity = "0";
        cursor2.style.zIndex = "-1";
    });
    
    element.addEventListener('mouseleave', function(){
        cursor.style.opacity = "1";
        cursor2.style.zIndex = "0";
    });
});
// … END
@charset "UTF-8";
/* CSS Document */

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    list-style: none;
    cursor: none;
}

a {
    text-decoration: none;
    color: black;
}

a:hover {
    color: white;
}

body {
    background-color: black;
}

body, html {
    height: 100%; /* <- Firefox needs this. – This might not be necessary in the final website, because there will be objects spanning the whole viewport. */
}

.test_div {
    background-color: white;
    position: fixed;
    width: 400px;
    height: 200px;
    margin: 15px;
    
    display: flex;
    justify-content: center;
    align-items: center;
}

.test_button {
    border: 1px solid black;
    height: 50px;
    width: 150px;
    line-height: 50px;
    text-align: center;
    font-family: proxima-nova, sans-serif;
}

.test_button:hover {
    background-color: black;
}

/* Custom Cursor START */
.cursor {
    z-index: 10;
    width: .5rem;
    height: .5rem;
    border-radius: 50%;
    background-color: white;
    position: fixed;
    transform: translate(-50%, -50%);
    pointer-events: none;
    display: none;
}

.cursor_2 {
    z-index: 9;
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    background-color: white;
    opacity: .3;
    position: fixed;
    transform: translate(-50%, -50%);
    pointer-events: none;
    display: none;
    
    transition: all .1s ease-out;
}
/* Custom Cursor END */
<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Personal-Website Build-5</title>
        
        <link rel="stylesheet" href="style.css">
        <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.12.0/underscore-min.js" integrity="sha512-BDXGXSvYeLxaldQeYJZVWXJmkisgMlECofWFXKpWwXnfcp/R708nrs/BtNLH5cb/5TE7aeYRTDBRXu6kRL4VeQ==" crossorigin="anonymous"></script>
    </head>

    <body>
        <div class="test_div cursorInteraction">
            <a href="" class="test_button cursorInteraction_Negative"> Test Link </a>
        </div>
        
        <!-- Custom-Cursor START -->
        <div class="cursor"></div>
        <div class="cursor_2"></div>
        
        <script src="scripts/customCursor.js"></script>
        <!-- Custom-Cursor END -->
    </body>
</html>

Related