0

Problem

I haven't been able to test this on MS Edge but Chrome and Firefox appear absolutely fine. My cursor uses two divs, one where the cursor/point would be, and a 2nd div which is follows the cursor with a bit of a delay/drag.

It's the 2nd div that seems to have a problem in Safari. When moving the mouse the 2nd (trailing) cursor shakes around and seems to have trouble tracking the cursor until you stop moving the mouse - when it jumps into the correct place.

It looks like maybe Safari is having trouble handling the transition: all .12s ease-out;? Could it be how that works with the following?

document.addEventListener('mousemove', function(e) {
    cursor.style.transform = `translate3d(calc(${e.clientX}px - 50%), calc(${e.clientY}px - 50%), 0)`;
});

CodePen

I'll add the JS to the post but I also have it on this CodePen: https://codepen.io/moy/pen/mdpVrgM

Can anyone see anything fundamentally wrong with this?

I thought it might work better if the cursor divs were nested instead of separate - but that seems to totally screw the offset to the main pointer.

As an aside...

I have two separate blocks of code for a.forEach, one for 'default' links and the other for ones with a data attribute. Is that ok? Or should I use a data attribute for everything including default a links? Just wondering what is more efficient?

var cursor = document.querySelector('.cursor');
var cursorinner = document.querySelector('.cursor2');
var a = document.querySelectorAll('a');

document.addEventListener('mousemove', function(e) {
    cursor.style.transform = `translate3d(calc(${e.clientX}px - 50%), calc(${e.clientY}px - 50%), 0)`;
});

document.addEventListener('mousemove', function(e) {
    var x = e.clientX;
    var y = e.clientY;
    cursorinner.style.left = x + 'px';
    cursorinner.style.top = y + 'px';
});

document.addEventListener('mousedown', function() {
    cursor.classList.add('click');
    cursorinner.classList.add('cursorinnerhover');
});

document.addEventListener('mouseup', function() {
    cursor.classList.remove('click');
    cursorinner.classList.remove('cursorinnerhover');
});

a.forEach(item => {
    item.addEventListener('mouseover', () => {
        cursor.classList.add('hover');
    });
    item.addEventListener('mouseleave', () => {
        cursor.classList.remove('hover');
    });
});

a.forEach((item) => {
    const interaction = item.dataset.interaction;
    
    item.addEventListener("mouseover", () => {
        cursorinner.classList.add(interaction);
    });
    item.addEventListener("mouseleave", () => {
        cursorinner.classList.remove(interaction);
    });
});
body, html {
  width: 100%;
  height: 100%;
}

section {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  flex-direction: column;
}

/* Cursor Stuff */

* {
  cursor: none;
}

.cursor {
    border: 2px solid black;
    border-radius: 100%;
    box-sizing: border-box;
    height: 32px;
    pointer-events: none;
    position: fixed;
    top: 0;
    left: 0;
    transform: translate(-50%, -50%);
    transition: all .12s ease-out;
    width: 32px;
    z-index: 100;
}

.cursor2 {
    background-color: black;
    border-radius: 100%;
/*   height: 4px; */
    height: 12px;
    opacity: 1;
    position: fixed;
    transform: translate(-50%, -50%);
    pointer-events: none;
    transition: height .12s, opacity .12s, width .12s;
/*  width: 4px; */
  width: 12px;
    z-index: 100;
}

.hover {
    height: 4px;
    opacity: 0;
    width: 4px;
}

.cursorinnerhover {
    height: 0;
    width: 0;   
}




/* Data Interaction Changes */

.red    {background: red;}
.green  {background: green;}
.blue   {background: blue;}
<div class="cursor"></div>
<div class="cursor2"></div>

<section>
  <a href="#" data-interaction="red">Hover Me</a>
  <br />
  <a href="#" data-interaction="green">Hover Me</a>
  <br />
  <a href="#" data-interaction="blue">Hover Me</a>
</section>
user1406440
  • 1,329
  • 2
  • 24
  • 59
  • Does this answer your question? [css transitions not working in safari](https://stackoverflow.com/questions/21767037/css-transitions-not-working-in-safari) – HerrAlvé Apr 03 '22 at 16:36
  • Thanks for that, I gave it ago and changed CSS to `transition: transform .12s ease-out;` from 'all' but that doesn't seem to improve it unfortunately – user1406440 Apr 03 '22 at 16:49

1 Answers1

1

Good News!

You can use margin-left and margin-top properties instead of cursor.style.transform = translate3d(calc(${e.clientX}px - 50%), calc(${e.clientY}px - 50%), 0);.

JS :-

document.addEventListener("mousemove", function (e) {
   cursor.style.marginLeft = `calc(${e.clientX}px)`;
   cursor.style.marginTop = `calc(${e.clientY}px)`;
});

CSS :-

transition: 400ms margin-left ease-out, 400ms margin-top ease-out;

This works like a charm for me.


EDIT :-

Good News, again! And this time for real :)

The previous answer did turn out to be a little choppy after trying it out for some minutes.

But I found a solution that works way better.

The Problem (According to me) :-

For every mousemove event, we are updating the values of margin-left and margin-top. This seems to have reduced the performance of the page and thus was causing the lag. (Although I know i might be very wrong :))

The Solution :-

We can add a short interval before updating the values of margin-left and margin-top. The javascript code (snippet):-

var moveCursor = true;
var radiusOfCursor = parseInt(getComputedStyle(cursor).getPropertyValue('width')) / 2; // radiusOfCursor = (width_of_cursor / 2).
    
document.addEventListener('mousemove', function (e) {
  if (!moveCursor) return;
  
  cursor.style.marginLeft = `calc(${e.clientX}px - ${radiusOfCursor}px)`;
  cursor.style.marginTop = `calc(${e.clientY}px - ${radiusOfCursor}px)`; 
  
  moveCursor = false;
  
  setTimeout(() => {
    moveCursor = true;
  }, 95) // The wait time. I chose 95 because it seems to work just fine for me.
});

You could simplify the code further by compiling the 2 mousemove event-listeners. That would end up looking somewhat like this :-

var moveCursor = true;
var radiusOfCursor = parseInt(getComputedStyle(cursor).getPropertyValue('width')) / 2; // radiusOfCursor = (width_of_cursor / 2).
    
document.addEventListener('mousemove', function (e) {
  var x = e.clientX;
  var y = e.clientY;
  cursorinner.style.left = x + 'px';
  cursorinner.style.top = y + 'px';

  if (!moveCursor) return;
      
  cursor.style.marginLeft = `calc(${e.clientX}px - ${radiusOfCursor}px)`;
  cursor.style.marginTop = `calc(${e.clientY}px - ${radiusOfCursor}px)`; 
      
  moveCursor = false;
      
  setTimeout(() => {
     moveCursor = true;
  }, 95) // The wait time. I chose 95 because it seems to work just fine for me.
});

Okay, another update :)

As commented by the OP,

if you move the cursor slow, the dot isn't always centred.

So to fix this issue (without tweaking the wait-time (95ms), because otherwise the movement seems to be laggy), we can move the cursor to the centre after the mousemove function gets over. So this is how I did it :-

function mouseMoveEnd() {
  cursor.style.marginLeft = `calc(${cursorinner.style.left} - ${radiusOfCursor}px)`;
  cursor.style.marginTop = `calc(${cursorinner.style.top} - ${radiusOfCursor}px)`;
}

var x;
document.addEventListener('mousemove', function() { 
    if (x) clearTimeout(x); 
    x = setTimeout(mouseMoveEnd, 10); 
}, false);

Now after even the smallest mouse movements, the cursor is centred around the cursorinner.

Also, I let the wait time remain as 95ms.

Hope this helps :)

HerrAlvé
  • 587
  • 3
  • 17
  • Thanks man, just trying that now. Adding negative left/top margins didn't seem to do anything for me - I guess down to the position fixed top/left values. Is this what you mean? Not sure if it's still a bit choppy for me - ok for you? https://codepen.io/moy/pen/WNddeod – user1406440 Apr 03 '22 at 17:35
  • thanks for this again, sorry I didn't realise you'd updated! I think this works much better. Just to confirm, this is the correct (2nd) implementation? https://codepen.io/moy/pen/bGaaKzv - I noticed at `95` if you move the cursor slow, the dot isn't always centred. Looks like that's just a case of tweaking the value and seeing what works best across the browsers? Thanks again! :) – user1406440 Apr 04 '22 at 18:28
  • Actually may've fixed by adding `transform: translate(-50%, -50%);` again in the CSS with some top/left values to centre. Seems to behave ok (fingers crossed): https://codepen.io/moy/pen/dyJJjGv ...or not :( – user1406440 Apr 04 '22 at 18:34
  • 1
    Hey @user1406440! I have updated my answer. Please have a look :) Also, you may definitely use the `top`, `left` and `transform` properties. That's totally up to you :) – HerrAlvé Apr 05 '22 at 12:37
  • Thanks @AlveMonke, just to confirm, does this just replace everything from (and including) `if (!moveCursor) return;` and `moveCursor = false;`? – user1406440 Apr 06 '22 at 06:30
  • 1
    No! This is in addition to the previous snippet. If you wish you can combine the contents of **this** event-listener with **the other, previous** one. I have created another mousemove event-listener just for clarity. – HerrAlvé Apr 06 '22 at 13:19
  • 1
    Sorry for the delay and thank you for all your work on this! It is sooooo appreciated! – user1406440 Apr 08 '22 at 17:26
  • No worries! Best of luck :) – HerrAlvé Apr 09 '22 at 05:38