Bootstrap

canvas做一个简单气泡图


数据结构: 

[{
    name: '土豆',
    num: 200,
  }, {
    name: '西瓜',
    num: 80,
  }, {
    name: '黄瓜',
    num: 85,
  }, {
    name: '粉丝',
    num: 70,
  }, {
    name: '苹果',
    num: 75,
  }, {
    name: '香蕉',
    num: 30,
  }, {
    name: '樱桃',
    num: 5,
  }, {
    name: '橙子',
    num: 100,
  }, {
    name: '梨',
    num: 80,
  }, {
    name: '菠菜',
    num: 85,
  }, {
    name: '西红柿',
    num: 70,
  }, {
    name: '大白菜',
    num: 75,
  }, {
    name: '西蓝花',
    num: 30,
  }];复制代码

要求

将n个不同大小(num决定圆大小)的圆在当前画板中呈现,要求每个圆都不会与其他圆重叠;

实现:

网页代码:

<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
  <meta charset="utf-8">
  <title></title>
  <style media="screen">
    * {
      margin: 0;
      padding: 0;
    }

    #oc {
      border: 1px solid #000;
      display: block;
    }
  </style>
</head>


<body>
  <canvas id="canvas" width="1280" height="680"></canvas>
</body>
</html>复制代码

1. js获取当前canvas

// 获取canvas
let canvas = document.querySelector('#canvas');
let ctx = canvas.getContext('2d');复制代码

2.  使用canvas API画圆

这里使用 arc(x,y,r,sAngle,eAngle, counterclockwise);

x: 圆心x坐标;

y: 圆心y坐标;

r: 圆的半径;

sAngle: 起始角; 一般设置0就行

eAmhle: 结束角;  整个圆使用 2* Math.PI, 半圆为Math.PI

counterclockwise: 可选,配置逆时针还是顺时针画圆; flase=逆时针

使用arc画圆

ctx.beginPath();
ctx.arc(100,100,50,0,2*Math.PI);
ctx.stroke();复制代码

效果图:


创建多个后 圆会重合并不满足我们的需求如图


随机位置: 

有多少条数据就生成多少个圆心坐标代码:

  data.forEach(d => {
    let ballPrefix = randomPrefix();
    ctx.arc(ballPrefix.x, ballPrefix.y, d.num, 0, 2* Math.PI);
    ctx.stroke();
  })
      
  data.forEach(d => {
    let ballPrefix = randomPrefix();
    ctx.arc(ballPrefix.x, ballPrefix.y, d.num, 0, 2* Math.PI);
    ctx.stroke();
  })
  function randomPrefix() {
    let x = 0;
    let y = 0;
    x = 80 + Math.random() * (canvas.width - 160);
    y = 80 + Math.random() * (canvas.height - 160);
    return {
      x,
      y
    }
  }复制代码

生成了一个奇怪的图


在data forEach中增加 ctx.beginPath(); 即可


我们看到圆圈是重叠的;

则需要在生产圆心的时候进行判断,该圆是否与其他圆有重叠

查找方法

// 传入坐标和半径 在圆数组中找到与之重叠的那个圆
  function findCircle(circle) {
    var len = balls.length;
    let axiosBall;
    for (var i = 0; i < len; i++) {
      var x1 = balls[i].x;
      var y1 = balls[i].y;
      var r1 = balls[i].r;
      var x2 = circle.x;
      var y2 = circle.y;
      var r2 = circle.r;
      if ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) < (r2 + r1) * (r2 + r1)) {
        axiosBall = balls[i];
        break;
      }
    }
    return axiosBall;
  }复制代码

生成圆心方法

// 为当前的所有数据都随机出来一个圆心位置并且 判断是否有重叠圆
  data.forEach(d => {
    // 算出当前圆的合适的半径
    let r = (canvas.height * (d.num / total))
    // 随机出来一个圆心位置
    let ball = randomPrefix();
    // 循环计数器 防止死循环
    let count = 0;
    //每个圆只生成200次
    while (findCircle({
        x: ball.x,
        y: ball.y,
        r: r
      }) && count < 200) {
      ball = randomPrefix();
      count++;
    }

    // 记录圆的半径和圆的元数据
    ball.r = r;
    ball.data = d;
    // 将生成的新圆放入数据中 下次检查是否重叠和绘制时使用
    balls.push(ball);
  })复制代码

以上就生成了所有的圆心位置 (一般都是不重叠的)

画圆方法:

  function draw() {
    // 遍历圆数组画圆
    balls.forEach(ball => {
      ctx.beginPath();
      ctx.arc(ball.x, ball.y, ball.r, 0, 2 * Math.PI);
      ctx.stroke();
    })
  }复制代码

效果:


生成了一个完全不重叠的简单气泡图(效果还差的远,但我会优化的)

给气泡加颜色以及给气泡加文字

随机颜色 以及 气泡上增加文字

function draw() {
    // 遍历圆数组画圆
    balls.forEach(ball => {
      ctx.beginPath();
      ctx.arc(ball.x, ball.y, ball.r, 0, 2 * Math.PI);
      // 给圆填充颜色
      ctx.fillStyle = randomColor();
      ctx.fill();
      // 将数据中的name显示在圆上
      ctx.fillStyle = '#fff';
      ctx.fillText(ball.data.name, ball.x - ball.r / 2, ball.y)
      ctx.stroke();
    })
  }

  function randomColor() {
    return `rgba(${Math.floor(Math.random() * 256)},
         ${Math.floor(Math.random() * 256)},
         ${Math.floor(Math.random() * 256)})`;
  }复制代码

效果图: 



最后 添加事件

canvas是不能做交互的;

但是我们可以获取canvas的所有事件以及Event对象;

Event对象可以获取触发事件时候鼠标所在的位置;

鼠标的位置可以用于查找当前位置是否有圆;

代码如下:

  canvas.addEventListener('click', function(e) {
    let clickBall = findCircle({
      x: e.x,
      y: e.y,
      r: 5
    });
    console.log(clickBall.data);
  })复制代码

点击土豆的圆显示






就是一个简单的canvas 后续还要优化; 

有什么有误的地方 敬请指教

代码地址:

 github.com/garyhan/stu…


悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;