画布上的奇怪行为上下文变量定义位置导致选项卡处于非活动状态时画布渲染冻结

时间:2016-03-04 19:26:59

标签: javascript html canvas

我遇到了一个非常奇怪的行为,导致Chrome出现问题(仅经过测试的浏览器)。我有一个用于渲染音频频谱的画布。每当我定义CanvasRenderingContext2D变量

var canvasCtx;
canvasCtx = canvas.getContext("2d");

或定义ScriptProcessorNode变量

var scriptNode;
scriptNode = audioCtx.createScriptProcessor(2048, 1, 1);

并且变量定义在任何闭包或函数内部,如果切换标签,更改窗口,甚至更改窗口宽度大小,可视化渲染将冻结。

但是,我发现如果这两种类型的变量定义是在所有函数之外的全局范围内定义的,那么即使页面处于非活动状态,画布也将继续呈现。

这是一个JSFiddle演示错误: http://jsfiddle.net/K8J26/541/ (由于跨源安全,音频可能无法在JSFiddle中播放,因此可能需要本地文件)

我想知道,为什么会这样? 即使页面处于非活动状态,浏览器是否仍然保持与全局范围关联的变量和对象的状态?或者它是否与变量定义的位置完全无关,而是我完全忽略了什么?

谢谢!

1 个答案:

答案 0 :(得分:0)

您的问题似乎是您正在计算新尺寸并创建渐变,而浏览器尚未更新元素的大小。

updateScale调用中传递requestAnimationFrame,以便浏览器有时间呈现元素:



(function initVisualizer() {
  var AudioContext = window.AudioContext || window.webkitAudioContext;
  if (!AudioContext) {
    return;
  }

  var PERCENT_HIGH_FREQ_TRIM = 0.3;
  var PERCENT_HEIGHT_OF_WITDH = 0.25;
  var SMOTHING_CONSTANT = 0.6;

  var audioCtx = new AudioContext();
  var canvas = document.getElementById("visualizer");
  canvas.style.width = "100%";

  // Defining canvasCtx here causes frozen canvas rendering when tab is inactive
  var canvasCtx = canvas.getContext("2d");
  // Defining scriptNode here causes frozen canvas rendering when tab is inactive
  var scriptNode = audioCtx.createScriptProcessor(2048, 1, 1);
  scriptNode.connect(audioCtx.destination);

  var analyser = audioCtx.createAnalyser();
  analyser.smoothingTimeConstant = SMOTHING_CONSTANT;
  analyser.connect(scriptNode);

  var widthMax;
  var heightMax;
  var dataCount;
  var barWidth;
  var barOffset;
  var gradient;

  scriptNode.onaudioprocess = function() {
    var frequencyData = new Uint8Array(dataCount);
    analyser.getByteFrequencyData(frequencyData);

    canvasCtx.clearRect(0, 0, widthMax, heightMax);

    canvasCtx.fillStyle = gradient;

    var modifier = heightMax / 255;
    for (var i = 0; i < dataCount; i++) {
      var value = ~~(frequencyData[i] * modifier);

      canvasCtx.fillRect(i * (barWidth + barOffset), heightMax - value, barWidth, heightMax);
    }
  };

  window.addEventListener("resize", function() {
    if (canvas.parentElement.offsetWidth !== widthMax) {
      requestAnimationFrame(updateScale);
    }
  });

  function updateScale() {

    widthMax = canvas.offsetWidth;
    heightMax = ~~(widthMax * PERCENT_HEIGHT_OF_WITDH);

    var size = nearestPow2(~~(widthMax / 4));
    dataCount = ~~((size / 2) * (1 - PERCENT_HIGH_FREQ_TRIM));
    var rectWidth = widthMax / dataCount;
    barOffset = rectWidth * 0.25;
    barWidth = rectWidth - barOffset;
    analyser.fftSize = size;

    canvas.setAttribute("width", widthMax);
    canvas.setAttribute("height", heightMax);

    gradient = canvasCtx.createLinearGradient(0, 0, 0, heightMax);
    gradient.addColorStop(1, "#ff6600");
    gradient.addColorStop(0, "#ffff00");
  }

  function playAudio(audio) {
    var sourceNode = audioCtx.createMediaElementSource(audio);
    sourceNode.connect(analyser);
    sourceNode.connect(audioCtx.destination);
    console.log(audio.play());
    setTimeout(function() {
      audio.pause();
    }, 30000);
  }

  function nearestPow2(size) {
    return Math.pow(2, Math.round(Math.log(size) / Math.log(2)));
  }

  updateScale();
  playAudio(document.getElementById('audio'));
})();
&#13;
body {
  background: #222;
}
&#13;
<audio id="audio" controls crossorigin="anonymous">
  <source src="https://upload.wikimedia.org/wikipedia/en/d/dc/Strawberry_Fields_Forever_%28Beatles_song_-_sample%29.ogg" />
  <source src="https://upload.wikimedia.org/wikipedia/en/d/dc/Strawberry_Fields_Forever_%28Beatles_song_-_sample%29.ogg" />
</audio>
<canvas id="visualizer"></canvas>
&#13;
&#13;
&#13;

但是,当你在全局范围内设置变量时,我不知道它为什么起作用......

Ps:你的小提琴没有在FF工作,只因为一个bug而在chrome上工作,你需要在加载资源之前设置crossorigin属性,否则它没有效果。