频谱图 js+canvas

使用到了Colormap,可以去npm上下载一个,查看下使用文档,来用数据对应颜色,在画布上显示

其中碰到了一些坑,canvas的画布大小属性和style设置的不是同一个东西,这个和svg的一样

<script src="./Colormap.js"></script>
 <button>+1 绘制</button><br /><br />
  <div class="myChart">
    <div class="info"></div>
    <canvas id="myCanvas" style="border: 1px solid #999;"></canvas>
  </div>
// 数据模拟
    var xData = [];
    for (let i = 0; i <= 2260; i++) {
      xData.push(i)
    };

    // canvas四周间隔
    var gridGap = 40;

    // canvas : 0-150数值,对应的颜色值
    // 数据值 => 颜色映射
    var maxValue = 150;
    var dataToColor = createColormap({
      colormap: "jet",
      nshades: maxValue,
      format: "hex",
      alpha: 1,
    });




    // canvas
    var canvas = document.getElementById("myCanvas");
    var ctx = canvas.getContext("2d");

    // canvas: 设置真实高度, 行内宽高是物理性宽高
    var canvasWidth = canvas.width = 600;
    var canvasHeight = canvas.height = 200;
    canvasWidth -= gridGap * 2;
    canvasHeight -= gridGap;

    // canvas: 文字设置
    ctx.font = "12px '微软雅黑'"; //设置字体
    ctx.textAlign = "center";
    ctx.textBaseline = 'hanging'; //在绘制文本时使用的当前文本基线

    // 图例
    var gradient = ctx.createLinearGradient(0, 0, 0, canvasHeight);

    // 图例 : 线性渐变,根据maxValue数值渲染
    for (let i = 0; i < maxValue; i++) {
      gradient.addColorStop(i / maxValue, dataToColor[i]);
    };
    ctx.fillStyle = gradient;
    ctx.fillRect(25, 0, 10, canvasHeight);

    // 图例: 数字说明
    ctx.fillStyle = "#666";
    ctx.fillText(0, 10, canvasHeight - 10); //设置文本内容
    ctx.fillText(maxValue / 2, 10, (canvasHeight - 10) / 2); //设置文本内容
    ctx.fillText(maxValue, 10, 0); //设置文本内容

    // 时间轴
    var testTime = ['7:00', '7:20', '7:40', '8:00', '8:20', '8:40', '9:00', '9:20', '9:40', '10:00'];
    var timeArr = [];
    function time(data) {
      // 清除之前绘制的
      ctx.clearRect(canvasWidth + gridGap, 0, gridGap, canvasHeight);

      // 重新绘制
      data.forEach((val, index) => {
        ctx.fillStyle = "#666";
        ctx.fillText(val, canvasWidth + gridGap * 1.5, (canvasHeight / 10) * index); //设置文本内容
      });
    };
    // 数据最多10条

    // 绘制 X 轴
    var xNum = 10; // 10个分割点
    var maxUnit = Math.ceil((Math.max(...xData) / 10)); // 分割单位(显示的文字)
    ctx.translate(0.5, 0.5); // 防止1像素模糊

    // X轴: 线
    ctx.beginPath();
    ctx.moveTo(gridGap, canvasHeight);
    ctx.lineTo(canvasWidth + gridGap, canvasHeight);
    ctx.strokeStyle = "#aaa"
    ctx.stroke();
    ctx.closePath();

    // X轴 :分割线 + 文字
    var x_gap = canvasWidth / 10;
    for (let i = 0; i <= 10; i++) {
      var posX = i * x_gap + gridGap;
      ctx.beginPath();
      ctx.moveTo(posX, canvasHeight);
      ctx.lineTo(posX, canvasHeight + 10);
      ctx.fillStyle = "#666";
      ctx.fillText(maxUnit * i, posX, canvasHeight + 10); //设置文本内容
      ctx.strokeStyle = "#aaa";
      ctx.stroke();
    };

    // 一个单元色块的大小
    var unitWidth = 150;
    var unitHeight = 30;

    // 绘制单元
    function drawUnit(posX, posY, w, h, color) {
      ctx.beginPath();
      ctx.fillStyle = color;
      ctx.fillRect(posX, posY, w, h);
      ctx.closePath();
    };

    var tempData = null; // 缓存之前canvasData

    document.querySelector('button').onclick = function () {
      // 绘制时间轴
      timeArr.unshift(Math.ceil(Math.random() * 100));
      timeArr.length = timeArr.length > 10 ? 10 : timeArr.length;
      time(timeArr);

      // 填充之前已绘制数据,并且Y轴位置下降一个单位高度
      tempData && ctx.putImageData(tempData, 0 + gridGap, unitHeight);

      // 横向填充次数, 并且数据绘制,永远在第一行,以前的数据下沉
      for (let i = 0; i < Math.ceil(canvasWidth / unitWidth); i++) {
        //当前单元坐标+单元宽度,超出有效画布时,裁剪绘制的单元
        var x = i * unitWidth;
        if (x + unitWidth > canvasWidth) {
          drawUnit(x + gridGap, 0, canvasWidth - x, unitHeight, dataToColor[~~(Math.random() * 150)]);
        } else {
          drawUnit(x + gridGap, 0, unitWidth, unitHeight, dataToColor[~~(Math.random() * 150)]);
        }
      };

      // 获取整个画布高度-单元高度, 再次绘制的时候,填充进去,并且位置下降一个单元块的高度
      tempData = ctx.getImageData(0 + gridGap, 0, canvasWidth, canvasHeight - unitHeight);
    }

    // 鼠标经过,计算出该点在整个画布中的索引位置 => 缓存中的后端数据找出来
    // x轴索引 * y轴索引 => 数据位置索引
    var infoBox = document.querySelector('.info');

    canvas.onmousemove = function (event) {
      var posX = event.offsetX;
      var posY = event.offsetY;

      // X轴+Y轴有效画布区域
      var flagX = posX > gridGap && posX < canvasWidth + gridGap;
      var flagY = posY < canvasHeight;

      if (flagX && flagY) {

        // x轴索引
        var index_X = Math.ceil((posX - gridGap) / unitWidth);

        // y轴索引
        var index_Y = Math.ceil(posY / unitHeight);

        //  一行的数据个数
        var xNum = Math.ceil(canvasWidth / unitWidth);

        // 数据索引
        var dataNum = (index_Y - 1) * xNum + index_X;
        infoBox.innerHTML = dataNum;

        var pos = `display:block; left: ${posX}px; top:${posY}px `;

        infoBox.style.cssText = pos;
      } else {
        infoBox.style.display = 'none';
      }
    };

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值