词云气泡图canvas版

如图所示,需求就是这样,于是就是受挫了一个,

先这样,然后在哪样,最好就是这样了

思路:

1、根据数据计算出最大的圆,然后固定坐标在中间靠左些

2、其他圆由画布中心向四周随机发散分布,位置随机

3、圆不能重叠,也不能超出画布

4、开始用canvas画圆

    // 检查是否发生重叠
    isOverlapping(x, y, radius) {
      for (let bubble of this.bubbles) {
        const distance = Math.sqrt(Math.pow(x - bubble.x, 2) + Math.pow(y - bubble.y, 2));
        if (distance < (radius + bubble.radius)) {
          return true; // 如果距离小于两个半径之和,表示重叠
        }
      }
      return false;
    },
    // 检查圆是否超出画布
    isOutOfBounds(x, y, radius) {
      return (
          x - radius < 0 || // 左边界
          x + radius > this.canvasWidth || // 右边界
          y - radius < 0 || // 上边界
          y + radius > this.canvasHeight // 下边界
      );
    },
// 绘制气泡
    drawBubbles(data) {
      const canvas = this.$refs.bubbleCanvas;
      const ctx = canvas.getContext("2d");

      // 清空画布
      ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);

      const maxValue = Math.max(...data.map(d => d.value));
      const centerX = this.canvasWidth / 2;
      const centerY = this.canvasHeight / 2;
      const maxRadiusOffset = Math.min(this.canvasWidth, this.canvasHeight) / 4;

      // 按value从大到小排序
      const sortedData = data.sort((a, b) => b.value - a.value);

      this.bubbles = []; // 清空气泡信息,重新生成

      // 绘制所有圆
      sortedData.forEach((bubbleData, index) => {
        const radius = (bubbleData.value / maxValue) * (this.canvasWidth / 6);
        let x, y;

        if (index === 0) {
          // 最大圆居中
          x = centerX - radius;
          y = centerY;
        } else {
          let attempts = 0;
          do {
            const angle = Math.random() * 2 * Math.PI;
            const distanceFromCenter = Math.random() * maxRadiusOffset + radius;

            x = centerX + distanceFromCenter * Math.cos(angle);
            y = centerY + distanceFromCenter * Math.sin(angle);

            attempts++;
            if (attempts > 100) break;
          } while (this.isOverlapping(x, y, radius) || this.isOutOfBounds(x, y, radius));

          if (attempts > 100) return;
        }

        // 保存每个气泡的数据
        this.bubbles.push({x, y, radius, data: bubbleData, color: this.getRandomColor()});

        // 绘制气泡
        this.drawBubble(ctx, x, y, radius, this.bubbles[this.bubbles.length - 1].color, bubbleData.label, bubbleData.value);
      });

      // 绘制图例
      this.drawLegend(ctx);
    },
    // 绘制单个气泡
    drawBubble(ctx, x, y, radius, color, label, value) {
      ctx.beginPath();
      ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
      ctx.fillStyle = color;
      ctx.fill();
      ctx.lineWidth = 2;
      ctx.strokeStyle = '#ffffff';
      ctx.stroke();

      ctx.fillStyle = "#ffffff";
      ctx.font = "bold 14px Arial";
      ctx.textAlign = "center";
      ctx.fillText(label, x, y - 10);
      ctx.fillText(value, x, y + 10);
    },
    // 绘制图例
    drawLegend(ctx) {
      const legendX = this.canvasWidth - 120;
      const legendY = 20;
      const boxSize = 20;
      const spacing = 10;

      ctx.font = "14px Arial";
      ctx.textAlign = "left";

      this.bubbles.forEach((bubble, index) => {
        // 绘制颜色矩形
        ctx.fillStyle = bubble.color;
        ctx.fillRect(legendX, legendY + index * (boxSize + spacing), boxSize, boxSize);

        // 绘制文本
        ctx.fillStyle = "#000000";
        ctx.fillText(bubble.data.label, legendX + boxSize + 10, legendY + index * (boxSize + spacing) + 15);
      });
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值