网站addEventListener事件选择疑问:仅mousepad聚焦时控制星空背景移动
解决方案
问题根源
<div>元素默认不支持聚焦,所以focus事件无法触发,需要给.mousepad添加tabindex属性使其可聚焦。- 你的思路偏差:不是要在聚焦时调用
render,而是需要控制鼠标移动事件是否能影响星空的运动状态——只有当.mousepad聚焦时,才让鼠标移动驱动星空;失焦时停止响应。
具体修改步骤
1. 修改HTML,让mousepad可聚焦
给.mousepad添加tabindex属性,值设为0(保证它能被键盘或鼠标聚焦):
<div class="mousepad" tabindex="0"></div>
2. 修改JavaScript逻辑
- 添加状态变量控制鼠标追踪开关
- 绑定
focus/blur事件切换状态 - 限制鼠标移动事件的执行条件
在JS开头添加:
const mousepad = document.querySelector('.mousepad'); let trackingMouse = false; // 控制是否响应鼠标移动 // 绑定聚焦/失焦事件 mousepad.addEventListener('focus', () => { trackingMouse = true; }); mousepad.addEventListener('blur', () => { trackingMouse = false; // 失焦后重置指针,避免星空继续滑动 pointerX = null; pointerY = null; });
然后修改onMouseMove和onTouchMove函数,只有trackingMouse为true时才执行逻辑:
function onMouseMove(event) { if (!trackingMouse) return; touchInput = false; movePointer(event.clientX, event.clientY); } function onTouchMove(event) { if (!trackingMouse) return; touchInput = true; movePointer(event.touches[0].clientX, event.touches[0].clientY, true); event.preventDefault(); }
修改后的完整代码
HTML
<canvas></canvas> <div class="mousepad" tabindex="0"></div>
CSS
body { width: 100%; height: 100vh; background-color: #000; background-image: radial-gradient(circle at top right, rgba(84, 187, 115, 0.13), transparent), radial-gradient(circle at 20% 80%, rgba(41, 196, 255, 0.13), transparent) } canvas { position: fixed; width: 100%; height: 100%; } .mousepad { width: 700px; height: 100px; background-color: rgba(41, 196, 255, 0.13); border-radius: 10px; margin: auto; outline: none; /* 去掉默认聚焦边框,可选 */ } /* Hide Scrollbar for aesthetic looks*/ ::-webkit-scrollbar { overflow: auto; display: none; }
JavaScript
const STAR_COLOR = '#fff'; const STAR_SIZE = 3; const STAR_MIN_SCALE = 0.2; const OVERFLOW_THRESHOLD = 50; const STAR_COUNT = (window.innerWidth + window.innerHeight) / 20; const canvas = document.querySelector('canvas'), context = canvas.getContext('2d'); const mousepad = document.querySelector('.mousepad'); let trackingMouse = false; let scale = 1, // device pixel ratio width, height; let stars = []; let pointerX, pointerY; let velocity = { x: 0, y: 0, tx: 0, ty: 0, z: 0.0005 }; let touchInput = false; generate(); resize(); step(); window.onresize = resize; canvas.onmousemove = onMouseMove; canvas.ontouchmove = onTouchMove; canvas.ontouchend = onMouseLeave; document.onmouseleave = onMouseLeave; // 绑定聚焦失焦事件 mousepad.addEventListener('focus', () => { trackingMouse = true; }); mousepad.addEventListener('blur', () => { trackingMouse = false; pointerX = null; pointerY = null; }); function generate() { for (let i = 0; i < STAR_COUNT; i++) { stars.push({ x: 0, y: 0, z: STAR_MIN_SCALE + Math.random() * (1 - STAR_MIN_SCALE) }); } } function placeStar(star) { star.x = Math.random() * width; star.y = Math.random() * height; } function recycleStar(star) { let direction = 'z'; let vx = Math.abs(velocity.x), vy = Math.abs(velocity.y); if (vx > 1 || vy > 1) { let axis; if (vx > vy) { axis = Math.random() < vx / (vx + vy) ? 'h' : 'v'; } else { axis = Math.random() < vy / (vx + vy) ? 'v' : 'h'; } if (axis === 'h') { direction = velocity.x > 0 ? 'l' : 'r'; } else { direction = velocity.y > 0 ? 't' : 'b'; } } star.z = STAR_MIN_SCALE + Math.random() * (1 - STAR_MIN_SCALE); if (direction === 'z') { star.z = 0.1; star.x = Math.random() * width; star.y = Math.random() * height; } else if (direction === 'l') { star.x = -OVERFLOW_THRESHOLD; star.y = height * Math.random(); } else if (direction === 'r') { star.x = width + OVERFLOW_THRESHOLD; star.y = height * Math.random(); } else if (direction === 't') { star.x = width * Math.random(); star.y = -OVERFLOW_THRESHOLD; } else if (direction === 'b') { star.x = width * Math.random(); star.y = height + OVERFLOW_THRESHOLD; } } function resize() { scale = window.devicePixelRatio || 1; width = window.innerWidth * scale; height = window.innerHeight * scale; canvas.width = width; canvas.height = height; stars.forEach(placeStar); } function step() { context.clearRect(0, 0, width, height); update(); render(); requestAnimationFrame(step); } function update() { velocity.tx *= 0.96; velocity.ty *= 0.96; velocity.x += (velocity.tx - velocity.x) * 0.8; velocity.y += (velocity.ty - velocity.y) * 0.8; stars.forEach((star) => { star.x += velocity.x * star.z; star.y += velocity.y * star.z; star.x += (star.x - width / 2) * velocity.z * star.z; star.y += (star.y - height / 2) * velocity.z * star.z; star.z += velocity.z; // recycle when out of bounds if (star.x < -OVERFLOW_THRESHOLD || star.x > width + OVERFLOW_THRESHOLD || star.y < -OVERFLOW_THRESHOLD || star.y > height + OVERFLOW_THRESHOLD) { recycleStar(star); } }); } function render() { stars.forEach((star) => { context.beginPath(); context.lineCap = 'round'; context.lineWidth = STAR_SIZE * star.z * scale; context.globalAlpha = 0.5 + 0.5 * Math.random(); context.strokeStyle = STAR_COLOR; context.beginPath(); context.moveTo(star.x, star.y); var tailX = velocity.x * 2, tailY = velocity.y * 2; // stroke() wont work on an invisible line if (Math.abs(tailX) < 0.1) tailX = 0.5; if (Math.abs(tailY) < 0.1) tailY = 0.5; context.lineTo(star.x + tailX, star.y + tailY); context.stroke(); }); } function movePointer(x, y) { if (typeof pointerX === 'number' && typeof pointerY === 'number') { let ox = x - pointerX, oy = y - pointerY; velocity.tx = velocity.tx + (ox / 8 * scale) * (touchInput ? 1 : -1); velocity.ty = velocity.ty + (oy / 8 * scale) * (touchInput ? 1 : -1); } pointerX = x; pointerY = y; } function onMouseMove(event) { if (!trackingMouse) return; touchInput = false; movePointer(event.clientX, event.clientY); } function onTouchMove(event) { if (!trackingMouse) return; touchInput = true; movePointer(event.touches[0].clientX, event.touches[0].clientY, true); event.preventDefault(); } function onMouseLeave() { pointerX = null; pointerY = null; }
内容的提问来源于stack exchange,提问作者user13608015
相关产品推荐
相关产品推荐

