使用putImageData的Firefox,Edge,Safari画布绘图失败

时间:2018-02-27 17:05:23

标签: javascript canvas

jsfiddle:https://jsfiddle.net/nragrkb8/3/

我试图绘制光谱图,在chrome中工作,但在所有其他浏览器中都没有显示任何内容。每次推入新数据时,我都会使用getImageData和putImageData将现有内容向下移动一个像素。然后将新数据绘制到屏幕外图像中,然后使用putimagedata将其应用到完整画布。

Spectrogram = (function() {

  //constructor function
  var Spectrogram = function(options) {
    //compose options
    this.canvas = options.canvas;
    this.width = options.size; //the size of the FFT
    this.height = options.length; //the number of FFT to keep

    if (typeof options.min !== 'undefined' && typeof options.max !== 'undefined') {
      this.rangeType = 'static';
      this.min = options.min;
      this.max = options.max;
    } else {
      this.rangeType = 'auto';
      this.min = Infinity;
      this.max = -Infinity;
    }

    //set the function used to determine a value's color
    this.colorScaleFn = options.colorScaleFn || defaultColorScaling;

    //precalculate the range used in color scaling
    this.range = this.max - this.min;

    //the 2d drawing context
    this.ctx = this.canvas.getContext('2d', { alpha: false });

    //single pixel image used to draw new data points
    this.im = this.ctx.createImageData(this.width, 1);

    this.canvas.width = this.width;
    this.canvas.height = this.height;

    //adds a new row of data to the edge of the spectrogram, shifting existing
    //contents by 1px
    this.addData = function (newData) {
      if (newData.length != this.width) {
        console.error('Unmatched dataset size. Expected ' + this.width + ', was + ' + newData.length);
        return;
      }

      if (this.rangeType == 'auto') {
        var changed = false;

        for (var i = 0; i < newData.length; ++i) {
          if (newData[i] < this.min) {
            this.min = newData[i];
            changed = true;
          } else if (newData[i] > this.max) {
            this.max = newData[i];
            changed = true;
          }
        }

        if (changed) {
          this.range = this.max - this.min;
        }
      }

      //move the current contents by 1px
      var im = this.ctx.getImageData(0, 0, this.width, this.height - 1);
      this.ctx.putImageData(im, 0, 1);

      //draw the new data values into the temporary image
      var j = 0;
      for (var i = 0; i < newData.length; ++i) {
        var c = this.colorScaleFn(newData[i]);
        this.im.data[j] = c[0];
        this.im.data[j + 1] = c[1];
        this.im.data[j + 2] = c[2];
        j += 4;
      }

      //put the new data colors into the full canvas
      this.ctx.putImageData(this.im, 0, 0);
    }
  }

  function defaultColorScaling(value) {
    var x = (value - this.min) / this.range;
    if (x < 0) {
      x = 0;
    } else if (x > 1) {
      x = 1;
    }

    var g = Math.pow(x, 2);
    var b = 0.75 - 1.5 * Math.abs(x - 0.45);
    var r = 0;

    return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
  }

  return Spectrogram;
})();

const size = 1024;
      const length = 200;

      const dataMax = 100;
      const velMax = 1;

      const canvas = document.getElementById('spec');
      canvas.width = size;
      canvas.height = length;

      const spec = new Spectrogram({
        canvas: canvas,
        size: size,
        length: length
      });

      const data = [];
      const vel = [];
      for (var i = 0; i < size; ++i) {
        data.push(0);
        vel.push(0);
      }

      setInterval(function() {
        for (var i = 0; i < size; ++i) {
          data[i] += vel[i];

          if (i > 0) data[i] += vel[i - 1];
          if (i < size - 1) data[i] += vel[i + 1];

          if (data[i] >= dataMax) {
            data[i] = dataMax;
            vel[i] = 0;
          } else if (data[i] <= -dataMax) {
            data[i] = -dataMax;
            vel[i] = 0;
          }

          if (vel[i] == 0) {
            vel[i] = (Math.random() - 0.5) * 100;
          }
        }

        spec.addData(data);
      }, 100);

1 个答案:

答案 0 :(得分:0)

你的问题是由误解造成的:

getContext('2d', {alpha: false})应该没有createImageData函数,因此您仍然需要将ImageData的alpha值设置为0以外的值。

您的代码中还有其他内容不顺利,即使您当前的问题没有实现:

  • 你应该不惜一切代价避免高频率setInterval,你执行的任务可能需要超过间隔(在你的情况下它可以),而是使用类似requestAnimationFrame循环的东西
  • 请勿使用getImageData + putImageData自行绘制上下文,只需使用ctx.drawImage(ctx.canvas, x, y)

您的代码中可能还有其他一些问题需要解决,但这是一个包含以下三个主要内容的更新:

Spectrogram = (function() {

  //constructor function
  var Spectrogram = function(options) {
    //compose options
    this.canvas = options.canvas;
    this.width = options.size; //the size of the FFT
    this.height = options.length; //the number of FFT to keep

    if (typeof options.min !== 'undefined' && typeof options.max !== 'undefined') {
      this.rangeType = 'static';
      this.min = options.min;
      this.max = options.max;
    } else {
      this.rangeType = 'auto';
      this.min = Infinity;
      this.max = -Infinity;
    }

    //set the function used to determine a value's color
    this.colorScaleFn = options.colorScaleFn || defaultColorScaling;

    //precalculate the range used in color scaling
    this.range = this.max - this.min;

    //the 2d drawing context
    this.ctx = this.canvas.getContext('2d', {
      alpha: false
    });

    //single pixel image used to draw new data points
    this.im = this.ctx.createImageData(this.width, 1);

    this.canvas.width = this.width;
    this.canvas.height = this.height;

    //adds a new row of data to the edge of the spectrogram, shifting existing
    //contents by 1px
    this.addData = function(newData) {
      if (newData.length != this.width) {
        console.error('Unmatched dataset size. Expected ' + this.width + ', was + ' + newData.length);
        return;
      }

      if (this.rangeType == 'auto') {
        var changed = false;

        for (var i = 0; i < newData.length; ++i) {
          if (newData[i] < this.min) {
            this.min = newData[i];
            changed = true;
          } else if (newData[i] > this.max) {
            this.max = newData[i];
            changed = true;
          }
        }

        if (changed) {
          this.range = this.max - this.min;
        }
      }

      //move the current contents by 1px
      this.ctx.drawImage(this.ctx.canvas, 0, 1)

      //draw the new data values into the temporary image
      var j = 0;
      for (var i = 0; i < newData.length; ++i) {
        var c = this.colorScaleFn(newData[i]);
        this.im.data[j] = c[0];
        this.im.data[j + 1] = c[1];
        this.im.data[j + 2] = c[2];
        // don't forget the alpha channel, createImageData is always full of zeroes
        this.im.data[j + 3] = 255;
        j += 4;
      }

      this.ctx.putImageData(this.im, 0, 0);
    }
  }

  function defaultColorScaling(value) {
    var x = (value - this.min) / this.range;
    if (x < 0) {
      x = 0;
    } else if (x > 1) {
      x = 1;
    }

    var g = Math.pow(x, 2);
    var b = 0.75 - 1.5 * Math.abs(x - 0.45);
    var r = 0;

    return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
  }

  return Spectrogram;
})();

const size = 1024;
const length = 200;

const dataMax = 100;
const velMax = 1;

const canvas = document.getElementById('spec');
canvas.width = size;
canvas.height = length;

const spec = new Spectrogram({
  canvas: canvas,
  size: size,
  length: length
});

const data = [];
const vel = [];
for (var i = 0; i < size; ++i) {
  data.push(0);
  vel.push(0);
}
// our animation loop
function draw() {
  for (var i = 0; i < size; ++i) {
    data[i] += vel[i];

    if (i > 0) data[i] += vel[i - 1];
    if (i < size - 1) data[i] += vel[i + 1];

    if (data[i] >= dataMax) {
      data[i] = dataMax;
      vel[i] = 0;
    } else if (data[i] <= -dataMax) {
      data[i] = -dataMax;
      vel[i] = 0;
    }

    if (vel[i] == 0) {
      vel[i] = (Math.random() - 0.5) * 100;
    }
  }

  spec.addData(data);
  requestAnimationFrame(draw);
}
draw();
<div style="width: 100%; height: 100%; padding: 0; margin: 0;">
  <canvas style="width: 100%; height: 100%;" id="spec"></canvas>
</div>