代码之家  ›  专栏  ›  技术社区  ›  Max

jQuery:鼠标跟随元素在滚动时不会粘住光标

  •  1
  • Max  · 技术社区  · 7 年前

    我有个小问题,无法解决。

    我开发了一个跟随光标的元素,它需要的功能是光标周围的边界需要与光标的位置保持一致。但我现在遇到的问题是,向下滚动时它不会粘住。

    你可以在下面的演示中查看我的意思。

    问题似乎是它没有正确检查页面的高度,这就是为什么它没有正确定位的原因。我说得对吗?

    const windowW = window.innerWidth;
    const windowH = window.innerHeight;
    const maxLength = Math.max(windowW, windowH);
    
    const cursorWidth = 100;
    const cursorR = cursorWidth >> 1;
    const cursorDelay = 10;
    
    const buttons = Array.from(document.querySelectorAll('.border-button'));
    
    const cursor = {
        el: document.querySelector('.border-cursor'),
        x: windowW >> 1,
        y: windowH >> 1,
        scaleX: 1,
        scaleY: 1,
    };
    
    const target = {
        x: windowW >> 1,
        y: windowH >> 1,
        width: cursorWidth,
        followMouse: true,
    };
    
    const norm = (val, max, min) => (val - min) / (max - min);
    const toDegrees = r => r * (180 / Math.PI);
    const distanceBetween = (v1, v2) => Math.sqrt((v1.x - v2.x) * (v1.x - v2.x) + (v1.y - v2.y) * (v1.y +- v2.y));
    
    const loop = () => {
        const destX = target.x - cursorR;
        const destY = target.y - cursorR;
    
        const newX = cursor.x + ((destX - cursor.x) / cursorDelay);
        const newY = cursor.y + ((destY - cursor.y) / cursorDelay);
        const angle = angleBetween(cursor.x, cursor.y, newX, newY);
    
        if (target.followMouse) {
            const distance = Math.abs(distanceBetween(target, cursor));
            const scale = norm(distance, maxLength, cursorR);
            cursor.scaleX = 1 + scale;
            cursor.scaleY = 1 - scale;
        } else {
            const targetScale = target.width / cursorWidth;
    
            cursor.scaleX += (targetScale - cursor.scaleX) / (cursorDelay / 2);
            cursor.scaleY = cursor.scaleX;
        }
    
        cursor.x = newX;
        cursor.y = newY;
    
        cursor.el.style.transform = `translate(${cursor.x}px, ${cursor.y}px) rotate(${toDegrees(angle)}deg) scale(${cursor.scaleX}, ${cursor.scaleY})`;
    
        requestAnimationFrame(loop);
    };
    
    const angleBetween = (x1, y1, x2, y2) => Math.atan2(y2 - y1, x2 - x1);
    
    const onPointerMove = (e) => {
        if (!target.followMouse) {
            return;
        }
    
        const pointer = (e.touches && e.touches.length) ? e.touches[0] : e;
        const { clientX: x, clientY: y } = pointer;
    
        target.x = x;
        target.y = y;
    };
    
    const onPointerOver = (e) => {
        const btn = e.target;
        const rect = btn.getBoundingClientRect();
    
        target.followMouse = false;
        target.x = rect.left + (rect.width >> 1);
        target.y = rect.top + (rect.height >> 1);
    
        target.width = Math.max(rect.width, rect.height) + 50;
    };
    
    const onPointerOut = () => {
        target.followMouse = true;
        target.width = cursorWidth;
    };
    
    document.body.addEventListener('mousemove', onPointerMove);
    document.body.addEventListener('touchmove', onPointerMove);
    
    buttons.forEach((btn) => {
        btn.addEventListener('touchstart', onPointerOver);
        btn.addEventListener('mouseover', onPointerOver);
    
        btn.addEventListener('touchend', onPointerOut);
        btn.addEventListener('mouseout', onPointerOut);
    });
    
    loop();
    

    const windowW = window.innerWidth;
    const windowH = window.innerHeight;
    const maxLength = Math.max(windowW, windowH);
    
    const cursorWidth = 100;
    const cursorR = cursorWidth >> 1;
    const cursorDelay = 10;
    
    const buttons = Array.from(document.querySelectorAll('.border-button'));
    
    const cursor = {
    	el: document.querySelector('.border-cursor'),
    	x: windowW >> 1,
    	y: windowH >> 1,
    	scaleX: 1,
    	scaleY: 1,
    };
    
    const target = {
    	x: windowW >> 1,
    	y: windowH >> 1,
    	width: cursorWidth,
    	followMouse: true,
    };
    
    const norm = (val, max, min) => (val - min) / (max - min);
    const toDegrees = r => r * (180 / Math.PI);
    const distanceBetween = (v1, v2) => Math.sqrt((v1.x - v2.x) * (v1.x - v2.x) + (v1.y - v2.y) * (v1.y +- v2.y));
    
    const loop = () => {
    	const destX = target.x - cursorR;
    	const destY = target.y - cursorR;
    
    	const newX = cursor.x + ((destX - cursor.x) / cursorDelay);
    	const newY = cursor.y + ((destY - cursor.y) / cursorDelay);
    	const angle = angleBetween(cursor.x, cursor.y, newX, newY);
    
    	if (target.followMouse) {
    		const distance = Math.abs(distanceBetween(target, cursor));
    		const scale = norm(distance, maxLength, cursorR);
    		cursor.scaleX = 1 + scale;
    		cursor.scaleY = 1 - scale;
    	} else {
    		const targetScale = target.width / cursorWidth;
    
    		cursor.scaleX += (targetScale - cursor.scaleX) / (cursorDelay / 2);
    		cursor.scaleY = cursor.scaleX;
    	}
    
    	cursor.x = newX;
    	cursor.y = newY;
    
    	cursor.el.style.transform = `translate(${cursor.x}px, ${cursor.y}px) rotate(${toDegrees(angle)}deg) scale(${cursor.scaleX}, ${cursor.scaleY})`;
    
    	requestAnimationFrame(loop);
    };
    
    const angleBetween = (x1, y1, x2, y2) => Math.atan2(y2 - y1, x2 - x1);
    
    const onPointerMove = (e) => {
    	if (!target.followMouse) {
    		return;
    	}
    
    	const pointer = (e.touches && e.touches.length) ? e.touches[0] : e;
    	const { clientX: x, clientY: y } = pointer;
    
    	target.x = x;
    	target.y = y;
    };
    
    const onPointerOver = (e) => {
    	const btn = e.target;
    	const rect = btn.getBoundingClientRect();
    
    	target.followMouse = false;
    	target.x = rect.left + (rect.width >> 1);
    	target.y = rect.top + (rect.height >> 1);
    
    	target.width = Math.max(rect.width, rect.height) + 50;
    };
    
    const onPointerOut = () => {
    	target.followMouse = true;
    	target.width = cursorWidth;
    };
    
    document.body.addEventListener('mousemove', onPointerMove);
    document.body.addEventListener('touchmove', onPointerMove);
    
    buttons.forEach((btn) => {
    	btn.addEventListener('touchstart', onPointerOver);
    	btn.addEventListener('mouseover', onPointerOver);
    
    	btn.addEventListener('touchend', onPointerOut);
    	btn.addEventListener('mouseout', onPointerOut);
    });
    
    loop();
    html,
    body {
      margin: 0;
      padding: 0;
    }
    
    .wrapper {
      width: 100vw;
      min-height: 1500px;
      display: flex;
      flex-direction: row;
      align-items: center;
    }
    
    .container {
      width: 100%;
      display: flex;
      padding: 0 1rem;
    }
    
    .cursor {
      position: absolute;
      z-index: 10;
      width: 100px;
      height: 100px;
      border: 2px solid #23bfa0;
      border-radius: 50%;
    
      pointer-events: none;
    }
    
    .button {
      padding: 1rem;
    
      background-color: #23bfa0;
      border: none;
      box-shadow: 0 0 7px 0px rgba(0, 0, 0, 0.2);
    
      color: white;
      font-size: 1.2rem;
    
      cursor: pointer;
    
      transition: box-shadow 0.1s ease-in, transform 0.1s ease-in;
      
      &--small {
        padding: 0.75rem;
        font-size: 0.75rem;
      }
      
      &:hover {
        transform: translate(0%, -2px);
        box-shadow: 0px 4px 9px 2px rgba(0, 0, 0, 0.2)
      }
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <body>
    <div class="cursor border-cursor"></div>
    
    <div class="wrapper">
      <div class="container">
        <button class="button button--small border-button">small</button>
        <button class="button border-button">hover me</button>
        <button class="button border-button">hover me more</button>
      </div>
    </div>
    </body>
    1 回复  |  直到 7 年前
        1
  •  0
  •   Jeto    7 年前

    在里面 onPointerMove ,尝试替换:

    const { clientX: x, clientY: y } = pointer;
    

    使用:

    const { pageX: x, pageY: y } = pointer;
    

    下面是一篇很好的帖子,解释了这些值之间的差异: https://stackoverflow.com/a/9335517/965834

    此外,更改:

    target.x = rect.left + (rect.width >> 1);
    target.y = rect.top + (rect.height >> 1);
    

    进入:

    target.x = window.scrollX + rect.left + (rect.width >> 1);
    target.y = window.scrollY + rect.top + (rect.height >> 1);
    

    这将在计算按钮位置时考虑滚动。

    演示 :

    const windowW = window.innerWidth;
    const windowH = window.innerHeight;
    const maxLength = Math.max(windowW, windowH);
    
    const cursorWidth = 100;
    const cursorR = cursorWidth >> 1;
    const cursorDelay = 10;
    
    const buttons = Array.from(document.querySelectorAll('.border-button'));
    
    const cursor = {
    	el: document.querySelector('.border-cursor'),
    	x: windowW >> 1,
    	y: windowH >> 1,
    	scaleX: 1,
    	scaleY: 1,
    };
    
    const target = {
    	x: windowW >> 1,
    	y: windowH >> 1,
    	width: cursorWidth,
    	followMouse: true,
    };
    
    const norm = (val, max, min) => (val - min) / (max - min);
    const toDegrees = r => r * (180 / Math.PI);
    const distanceBetween = (v1, v2) => Math.sqrt((v1.x - v2.x) * (v1.x - v2.x) + (v1.y - v2.y) * (v1.y +- v2.y));
    
    const loop = () => {
    	const destX = target.x - cursorR;
    	const destY = target.y - cursorR;
    
    	const newX = cursor.x + ((destX - cursor.x) / cursorDelay);
    	const newY = cursor.y + ((destY - cursor.y) / cursorDelay);
    	const angle = angleBetween(cursor.x, cursor.y, newX, newY);
    
    	if (target.followMouse) {
    		const distance = Math.abs(distanceBetween(target, cursor));
    		const scale = norm(distance, maxLength, cursorR);
    		cursor.scaleX = 1 + scale;
    		cursor.scaleY = 1 - scale;
    	} else {
    		const targetScale = target.width / cursorWidth;
    
    		cursor.scaleX += (targetScale - cursor.scaleX) / (cursorDelay / 2);
    		cursor.scaleY = cursor.scaleX;
    	}
    
    	cursor.x = newX;
    	cursor.y = newY;
    
    	cursor.el.style.transform = `translate(${cursor.x}px, ${cursor.y}px) rotate(${toDegrees(angle)}deg) scale(${cursor.scaleX}, ${cursor.scaleY})`;
    
    	requestAnimationFrame(loop);
    };
    
    const angleBetween = (x1, y1, x2, y2) => Math.atan2(y2 - y1, x2 - x1);
    
    const onPointerMove = (e) => {
    	if (!target.followMouse) {
    		return;
    	}
    
    	const pointer = (e.touches && e.touches.length) ? e.touches[0] : e;
    	const { pageX: x, pageY: y } = pointer;
    
    	target.x = x;
    	target.y = y;
    };
    
    const onPointerOver = (e) => {
    	const btn = e.target;
    	const rect = btn.getBoundingClientRect();
    
    	target.followMouse = false;
    	target.x = window.scrollX + rect.left + (rect.width >> 1);
    	target.y = window.scrollY + rect.top + (rect.height >> 1);
    
    	target.width = Math.max(rect.width, rect.height) + 50;
    };
    
    const onPointerOut = () => {
    	target.followMouse = true;
    	target.width = cursorWidth;
    };
    
    document.body.addEventListener('mousemove', onPointerMove);
    document.body.addEventListener('touchmove', onPointerMove);
    
    buttons.forEach((btn) => {
    	btn.addEventListener('touchstart', onPointerOver);
    	btn.addEventListener('mouseover', onPointerOver);
    
    	btn.addEventListener('touchend', onPointerOut);
    	btn.addEventListener('mouseout', onPointerOut);
    });
    
    loop();
    html,
    body {
      margin: 0;
      padding: 0;
    }
    
    .wrapper {
      width: 100vw;
      min-height: 1500px;
      display: flex;
      flex-direction: row;
      align-items: center;
    }
    
    .container {
      width: 100%;
      display: flex;
      padding: 0 1rem;
    }
    
    .cursor {
      position: absolute;
      z-index: 10;
      width: 100px;
      height: 100px;
      border: 2px solid #23bfa0;
      border-radius: 50%;
    
      pointer-events: none;
    }
    
    .button {
      padding: 1rem;
    
      background-color: #23bfa0;
      border: none;
      box-shadow: 0 0 7px 0px rgba(0, 0, 0, 0.2);
    
      color: white;
      font-size: 1.2rem;
    
      cursor: pointer;
    
      transition: box-shadow 0.1s ease-in, transform 0.1s ease-in;
      
      &--small {
        padding: 0.75rem;
        font-size: 0.75rem;
      }
      
      &:hover {
        transform: translate(0%, -2px);
        box-shadow: 0px 4px 9px 2px rgba(0, 0, 0, 0.2)
      }
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <body>
    <div class="cursor border-cursor"></div>
    
    <div class="wrapper">
      <div class="container">
        <button class="button button--small border-button">small</button>
        <button class="button border-button">hover me</button>
        <button class="button border-button">hover me more</button>
      </div>
    </div>
    </body>