You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何发射的子弹无法朝点击方向直线移动?

问题根源与修复方案

核心问题

子弹不朝点击方向直线移动的原因主要有两个:

1. 箭头中心位置未实时更新

页面加载时仅计算了一次arrowCenter,但之后修改了container的位置,导致箭头实际位置已改变,但计算角度和子弹方向时仍沿用初始的中心坐标,直接造成方向偏移。

2. 子弹初始位置设置错误

创建子弹时,用div.offsetTop + -3设置位置,但刚创建的子弹在容器中的初始偏移量并非箭头中心位置,导致子弹从错误起点出发,进一步加剧方向偏差。

修复后的完整代码

function getCenter(element) {
  const {left, top, width, height} = element.getBoundingClientRect();
  return {x: left + width / 2, y: top + height / 2}
}

const arrow = document.getElementById("arrow");
const container = document.getElementById("container");

// 确保容器为相对定位,让子弹的绝对定位基于容器
container.style.position = "relative";

window.addEventListener("mousemove", getAngleOfArrow)

function getAngleOfArrow(e) {
  // 每次计算角度时实时获取箭头中心
  const arrowCenter = getCenter(arrow);
  const rads = Math.atan2(e.clientY - arrowCenter.y, e.clientX - arrowCenter.x);
  const angle = rads * 180 / Math.PI;
  arrow.style.transform = `rotate(${angle}deg)`;
}

window.addEventListener("click", function(e){
  const shot = createShot();
  moveShot(shot, e);
})

function getRad(e){
  // 实时获取箭头中心计算弧度
  const arrowCenter = getCenter(arrow);
  return Math.atan2(e.clientY - arrowCenter.y, e.clientX - arrowCenter.x);
}

function createShot(){
  const div = document.createElement("div");
  div.setAttribute("class", "bullet");
  container.appendChild(div);
  
  // 基于箭头当前中心设置子弹初始位置(适配6x6尺寸的子弹)
  const arrowCenter = getCenter(arrow);
  const containerRect = container.getBoundingClientRect();
  div.style.position = "absolute";
  div.style.top = `${arrowCenter.y - containerRect.top - 3}px`;
  div.style.left = `${arrowCenter.x - containerRect.left - 3}px`;
  
  return div;
}

function moveShot(circle, e){
  const rad = getRad(e);
  const xshot = 7 * Math.cos(rad);
  const yshot = 7 * Math.sin(rad);
  
  const myInterval = setInterval(function () {
    // 解析数值避免字符串拼接异常
    circle.style.left = `${parseFloat(circle.style.left) + xshot}px`;
    circle.style.top = `${parseFloat(circle.style.top) + yshot}px`;
    
    if(!isInViewport(circle)){
      clearInterval(myInterval);
      circle.remove(); // 移除脱离视口的子弹,避免内存泄漏
    }
  }, 16);
}

function isInViewport(element) {
  const rect = element.getBoundingClientRect();
  return (
      rect.top >= 0 &&
      rect.left >= 0 &&
      rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
      rect.right <= (window.innerWidth || document.documentElement.clientWidth)
  );
}

关键修改说明

  • 实时获取箭头中心:在角度计算和弧度计算函数内,每次都调用getCenter(arrow),确保使用箭头当前位置计算方向。
  • 修正子弹初始位置:基于箭头实时中心和容器的视口坐标,计算子弹在容器内的绝对定位坐标,保证从箭头中心出发。
  • 优化位置更新逻辑:用parseFloat解析当前位置的数值,避免字符串相加导致的计算错误。
  • 内存泄漏修复:子弹脱离视口后主动移除元素,避免累积无效DOM节点。

内容的提问来源于stack exchange,提问作者mackinonon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 18:41:22