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

为何频闪控制函数内manualBPM/tapBPM变量未实时更新?

问题:频闪运行时BPM参数无法实时更新

我正在开发一款Web应用,可根据用户设置的节拍(BPM)在屏幕上显示频闪效果,希望频闪运行时也能修改控制参数,为此添加了事件监听,但遇到问题:manualBPM和tapBPM变量在频闪控制函数内无法实时更新,控制台能显示变量值正确变化,但duration变量却能正常更新。

相关代码

// manual bpm variable definition and event listener
var manualBPM = 0;
var manualBPM_text = document.getElementById("manual-value");
manualBPM_text.addEventListener("change", function() {
  manualBPM = manualBPM_text.value;
}, false);

// strobe duration variable definition and event listener
var duration = 100;
var slider = document.getElementById("MM-duration-slider");
slider.addEventListener("input", function() {
  duration = slider.value;
}, false);

var strobeActive = false;

/* ----- Manual mode ON button ----- */
function MM_turnOn() {
  if (!manualBPM && BPMvalueSource == "manual") {
    window.alert("Please insert a BPM value or use the tempo tapper.");
  } else if (!tapBPM && BPMvalueSource == "tap") {
    window.alert("Please use the tempo tapper or insert a BPM value.");
  } else {
    var MM_onBtn = document.getElementById("MM-on");
    var MM_offBtn = document.getElementById("MM-off");
    var resetTapperBtn_a = document.getElementById("reset-tapbpm-a");
    var resetTapperBtn_h = document.getElementById("reset-tapbpm-h");

    // disable on button (selected) and reset tapper bpm and enable off button
    MM_onBtn.setAttribute("disabled", "true");
    MM_offBtn.removeAttribute("disabled");
    resetTapperBtn_a.setAttribute("disabled", "true");
    resetTapperBtn_a.removeAttribute("onclick");

    // styling
    MM_onBtn.classList.remove("text-gray-700");
    MM_onBtn.classList.remove("hover:scale-1125");
    MM_onBtn.classList.add("text-themeOrange");

    MM_offBtn.classList.add("text-gray-700");
    MM_offBtn.classList.add("hover:scale-1125");
    MM_offBtn.classList.remove("text-themeOrange");

    resetTapperBtn_h.classList.remove("cursor-pointer");
    resetTapperBtn_h.classList.remove("hover:text-themeOrange");

    strobeActive = true;

    const body = document.querySelector("body");
    var interval = 0;
    var ranTimes = 0;

    if (BPMvalueSource == "manual") {
      interval = (60 / manualBPM) * 1000; //manualBPM not updating

      // repeat once the interval expires
      window.strobeTimeout = setInterval(function() {
        // trigger strobe
        body.classList.remove("bg-black");
        body.classList.add("bg-white");

        // kill strobe once the strobe duration expires
        setTimeout(function() {
          body.classList.remove("bg-white");
          body.classList.add("bg-black");
        }, duration);

        ranTimes++;
        console.log("BPM: " + manualBPM + " (source: " + BPMvalueSource + ") | Strobe duration: " + duration + "ms | " + "Times ran: " + ranTimes);
      }, interval);
    } else if (BPMvalueSource == "tap") {
      interval = (60 / tapBPM) * 1000; //tapBPM not updating

      // repeat once the interval expires
      window.strobeTimeout = setInterval(function() {
        // trigger strobe
        body.classList.remove("bg-black");
        body.classList.add("bg-white");

        // kill strobe once the strobe duration expires
        setTimeout(function() {
          body.classList.remove("bg-white");
          body.classList.add("bg-black");
        }, duration);

        ranTimes++;
        console.log("BPM: " + tapBPM + " (source: " + BPMvalueSource + ") | Strobe duration: " + duration + "ms | " + "Times ran: " + ranTimes);
      }, interval);
    }
  }
}

function killStrobe() {
  strobeActive = false;
  // function to turn off the strobe cycle
  clearInterval(strobeTimeout);
}

问题原因

核心问题在于setInterval的间隔时间是在初始化时一次性计算并固定的,后续修改manualBPM或tapBPM不会自动更新这个间隔值。而duration能正常工作,是因为它是在每次定时器触发时才被读取的,并非固定在初始化阶段。

解决方案

推荐使用递归setTimeout替代setInterval,这样每次执行频闪前都会读取最新的BPM值计算间隔,确保参数修改后立即生效。

修改后的关键代码

var strobeActive = false;
const body = document.querySelector("body");
var ranTimes = 0;

// 封装递归频闪函数
function runStrobe() {
  if (!strobeActive) return;

  // 每次执行前动态计算最新的间隔
  let currentInterval;
  if (BPMvalueSource == "manual") {
    currentInterval = (60 / manualBPM) * 1000;
  } else if (BPMvalueSource == "tap") {
    currentInterval = (60 / tapBPM) * 1000;
  }

  // 执行频闪逻辑
  body.classList.remove("bg-black");
  body.classList.add("bg-white");

  setTimeout(function() {
    body.classList.remove("bg-white");
    body.classList.add("bg-black");
  }, duration);

  ranTimes++;
  console.log(`BPM: ${manualBPM} (source: ${BPMvalueSource}) | Strobe duration: ${duration}ms | Times ran: ${ranTimes}`);

  // 递归调用,实现动态间隔的循环
  window.strobeTimeout = setTimeout(runStrobe, currentInterval);
}

/* ----- Manual mode ON button ----- */
function MM_turnOn() {
  if (!manualBPM && BPMvalueSource == "manual") {
    window.alert("Please insert a BPM value or use the tempo tapper.");
  } else if (!tapBPM && BPMvalueSource == "tap") {
    window.alert("Please use the tempo tapper or insert a BPM value.");
  } else {
    // 保留原有的按钮状态和样式修改逻辑
    var MM_onBtn = document.getElementById("MM-on");
    var MM_offBtn = document.getElementById("MM-off");
    var resetTapperBtn_a = document.getElementById("reset-tapbpm-a");
    var resetTapperBtn_h = document.getElementById("reset-tapbpm-h");

    MM_onBtn.setAttribute("disabled", "true");
    MM_offBtn.removeAttribute("disabled");
    resetTapperBtn_a.setAttribute("disabled", "true");
    resetTapperBtn_a.removeAttribute("onclick");

    MM_onBtn.classList.remove("text-gray-700");
    MM_onBtn.classList.remove("hover:scale-1125");
    MM_onBtn.classList.add("text-themeOrange");

    MM_offBtn.classList.add("text-gray-700");
    MM_offBtn.classList.add("hover:scale-1125");
    MM_offBtn.classList.remove("text-themeOrange");

    resetTapperBtn_h.classList.remove("cursor-pointer");
    resetTapperBtn_h.classList.remove("hover:text-themeOrange");

    strobeActive = true;
    ranTimes = 0;
    // 启动递归频闪
    runStrobe();
  }
}

function killStrobe() {
  strobeActive = false;
  clearTimeout(strobeTimeout); // 改为清除setTimeout
}

方案优势

  • 每次频闪触发前都会读取最新的BPM值,修改参数后下一次频闪立即生效
  • 避免了setInterval可能出现的累积延迟问题
  • 代码结构更清晰,逻辑分离更合理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 06:25:35