最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

addeventlistener - Multiple cursors follow mouse in Javascript - Stack Overflow

programmeradmin5浏览0评论

I have a grid with multiple items. Each item has its own custom cursor with the div .grid__item-cursor.

What I'm trying to achieve is when hovering an item, its cursor follows the mouse with a smooth speed.

The issue is when hovering an item, all the cursors from the document move at the same time. And there is also too much distance between the custom cursor and the mouse.

const cursors = document.querySelectorAll(".js-cursor");

let aimX = 0;
let aimY = 0;

cursors.forEach((cursor) => {
  let currentX = 0;
  let currentY = 0;

  let speed = 0.2;

  const animate = function () {
    currentX += (aimX - currentX) * speed;
    currentY += (aimY - currentY) * speed;

    cursor.style.left = currentX + "px";
    cursor.style.top = currentY + "px";

    requestAnimationFrame(animate);
  };

  animate();
});

const posts = document.querySelectorAll(".js-post");
posts.forEach((post) => {
  const cursor = post.querySelector(".js-cursor");

  post.addEventListener("mousemove", function (event) {
    aimX = event.pageX;
    aimY = event.pageY;
  });

  post.addEventListener("mouseenter", function () {
    cursor.classList.add("is-visible");
  });

  post.addEventListener("mouseleave", function () {
    cursor.classList.remove("is-visible");
  });
});
body {
  font-family: "helvetica", arial, sans-serif;
}

.grid {
  display: grid;
  width: 100%;
  grid-template-columns: repeat(4, 1fr);
  grid-column-gap: 1rem;
  grid-row-gap: 1rem;
}

.grid__item {
  display: flex;
  justify-content: center;
  align-content: center;
  position: relative;
  padding: 25%;
  overflow: hidden;
  background-color: #333;
}

.grid__item-number {
  color: #888;
  font-size: 5rem;
}

.grid__item-cursor {
  position: absolute;
  top: 0;
  left: 0;
  padding: 0.25em;
  background-color: red;
}

.grid__item-cursor.is-visible {
  background-color: yellow;
}
<div class="grid">
  <div class="grid__item js-post">
    <div class="grid__item-number">1</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">2</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">3</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">4</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">5</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">6</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">7</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">8</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
</div>

I have a grid with multiple items. Each item has its own custom cursor with the div .grid__item-cursor.

What I'm trying to achieve is when hovering an item, its cursor follows the mouse with a smooth speed.

The issue is when hovering an item, all the cursors from the document move at the same time. And there is also too much distance between the custom cursor and the mouse.

const cursors = document.querySelectorAll(".js-cursor");

let aimX = 0;
let aimY = 0;

cursors.forEach((cursor) => {
  let currentX = 0;
  let currentY = 0;

  let speed = 0.2;

  const animate = function () {
    currentX += (aimX - currentX) * speed;
    currentY += (aimY - currentY) * speed;

    cursor.style.left = currentX + "px";
    cursor.style.top = currentY + "px";

    requestAnimationFrame(animate);
  };

  animate();
});

const posts = document.querySelectorAll(".js-post");
posts.forEach((post) => {
  const cursor = post.querySelector(".js-cursor");

  post.addEventListener("mousemove", function (event) {
    aimX = event.pageX;
    aimY = event.pageY;
  });

  post.addEventListener("mouseenter", function () {
    cursor.classList.add("is-visible");
  });

  post.addEventListener("mouseleave", function () {
    cursor.classList.remove("is-visible");
  });
});
body {
  font-family: "helvetica", arial, sans-serif;
}

.grid {
  display: grid;
  width: 100%;
  grid-template-columns: repeat(4, 1fr);
  grid-column-gap: 1rem;
  grid-row-gap: 1rem;
}

.grid__item {
  display: flex;
  justify-content: center;
  align-content: center;
  position: relative;
  padding: 25%;
  overflow: hidden;
  background-color: #333;
}

.grid__item-number {
  color: #888;
  font-size: 5rem;
}

.grid__item-cursor {
  position: absolute;
  top: 0;
  left: 0;
  padding: 0.25em;
  background-color: red;
}

.grid__item-cursor.is-visible {
  background-color: yellow;
}
<div class="grid">
  <div class="grid__item js-post">
    <div class="grid__item-number">1</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">2</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">3</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">4</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">5</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">6</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">7</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">8</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
</div>

Share Improve this question edited 12 hours ago Mathieu Préaud asked 12 hours ago Mathieu PréaudMathieu Préaud 4111 gold badge4 silver badges18 bronze badges 1
  • They all move, because you got got an instance of your animate function running in an interval, for all of them, all the time. – C3roe Commented 11 hours ago
Add a comment  | 

1 Answer 1

Reset to default 1

Here is a version that only follows the mouse in the grid element. When the mouse leaves the element, the position of the read READ ME is reset to 0,0 of the grid element. I wanted to delegate but the event bubbling made that non-trivial

const posts = document.querySelectorAll('.js-post');

let activePost = null;
let activeCursor = null;
let currentX = 0, currentY = 0;
let aimX = 0, aimY = 0;
const speed = 0.2;

const animate = () => {
  if (activeCursor) {
    currentX += (aimX - currentX) * speed;
    currentY += (aimY - currentY) * speed;
    activeCursor.style.left = currentX + 'px';
    activeCursor.style.top = currentY + 'px';
  }
  requestAnimationFrame(animate);
};

animate();

posts.forEach(post => {
  post.addEventListener('mouseenter', (e) => {
    // Hide the previous grid element's cursor immediately, if any.
    if (activePost && activePost !== post && activeCursor) {
      activeCursor.classList.remove('is-visible');
      // Reset the previous cursor to 0,0 relative to its container.
      activeCursor.style.left = '0px';
      activeCursor.style.top = '0px';
    }
    activePost = post;
    activeCursor = post.querySelector('.js-cursor');

    // Get grid item's bounding rectangle for local coordinate conversion.
    const rect = post.getBoundingClientRect();
    currentX = e.clientX - rect.left;
    currentY = e.clientY - rect.top;
    aimX = currentX;
    aimY = currentY;
    
    // Position the cursor immediately at the mouse's location.
    activeCursor.style.left = currentX + 'px';
    activeCursor.style.top = currentY + 'px';
    activeCursor.classList.add('is-visible');
  });

  post.addEventListener('mousemove', (e) => {
    if (activePost === post && activeCursor) {
      const rect = post.getBoundingClientRect();
      aimX = e.clientX - rect.left;
      aimY = e.clientY - rect.top;
    }
  });

  post.addEventListener('mouseleave', () => {
    if (activePost === post && activeCursor) {
      activeCursor.classList.remove('is-visible');
      // Reset the coordinates to the top-left (0,0) of the grid element.
      activeCursor.style.left = '0px';
      activeCursor.style.top = '0px';
      // Also reset the internal coordinates so the next activation starts from 0,0.
      currentX = 0;
      currentY = 0;
      aimX = 0;
      aimY = 0;
      activePost = null;
      activeCursor = null;
    }
  });
});
body {
  font-family: "helvetica", arial, sans-serif;
}

.grid {
  display: grid;
  width: 100%;
  grid-template-columns: repeat(4, 1fr);
  grid-column-gap: 1rem;
  grid-row-gap: 1rem;
}

.grid__item {
  display: flex;
  justify-content: center;
  align-content: center;
  position: relative;
  padding: 25%;
  background-color: #333;
}

.grid__item-number {
  color: #888;
  font-size: 5rem;
}

.grid__item-cursor {
  position: absolute;
  top: 0;
  left: 0;
  padding: 0.25em;
  background-color: red;
}

.grid__item-cursor.is-visible {
  background-color: yellow;
}
<div class="grid">
  <div class="grid__item js-post">
    <div class="grid__item-number">1</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">2</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">3</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">4</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">5</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">6</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">7</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
  <div class="grid__item js-post">
    <div class="grid__item-number">8</div>
    <div class="grid__item-cursor js-cursor">Read more</div>
  </div>
</div>

发布评论

评论列表(0)

  1. 暂无评论