svg画韦恩图其实挺好用的

50 阅读2分钟

image.png

在数据大屏项目中需要完成一个韦恩图,首先想到的是antv/g2中venn,后来正好在看svg相关的文章,我就在想是不是可以使用svg实现呢。

由于数据大屏需要考虑不同的屏幕尺寸,在画svg的时候需要适应容器的宽高,所以需要获取容器的宽高然后赋值给svg,并且为了保证居中效果,每个元素在定位的时候都是基于(svgWidth/2,svgHeight/2)为中心点。

<template>
  <div class="align-center flex h-full w-full justify-center" ref="containerRef">
    <svg ref="svgRef" :width="svgWidth" :height="svgHeight">
      <!-- 定义线性渐变 -->
      <defs>
        <linearGradient id="grad1" x1="50%" y1="50%" x2="75%" y2="100%">
          <stop offset="0%" style="stop-color: #3b82f6; stop-opacity: 1" />
          <stop offset="100%" style="stop-color: #3b82f6; stop-opacity: 0.3" />
        </linearGradient>
      </defs>
      <defs>
        <linearGradient id="grad2" x1="75%" y1="50%" x2="50%" y2="100%">
          <stop offset="0%" style="stop-color: #ef4444; stop-opacity: 1" />
          <stop offset="100%" style="stop-color: #ef4444; stop-opacity: 0.3" />
        </linearGradient>
      </defs>
      <defs>
        <linearGradient id="grad3" x1="100%" y1="50%" x2="100%" y2="25%">
          <stop offset="0%" style="stop-color: #f59e0b; stop-opacity: 1" />
          <stop offset="100%" style="stop-color: #f59e0b; stop-opacity: 0.3" />
        </linearGradient>
      </defs>
      <!-- 绘制维恩图的各个椭圆 -->
      <g>
        <!-- B - 蓝色椭圆 -->
        <ellipse
          :cx="getSize(svgWidth / 2 - svgWidth / 3 / 2 + 2)"
          :cy="getSize(svgHeight / 2 - svgHeight / 3 / 2 + 2)"
          :rx="getSize(svgWidth / 3)"
          :ry="getSize(svgHeight / 3)"
          fill="#ffffff"
          fill-opacity="0.3"
          stroke="url(#grad1)"
          stroke-width="2"
        />
        <!-- A - 红色椭圆 -->
        <ellipse
          :cx="getSize(svgWidth / 2 + svgWidth / 3 / 2 - 2)"
          :cy="getSize(svgHeight / 2 - svgHeight / 3 / 2 + 2)"
          :rx="getSize(svgWidth / 3)"
          :ry="getSize(svgHeight / 3)"
          fill="#ffffff"
          fill-opacity="0.3"
          stroke="url(#grad2)"
          stroke-width="2"
        />
        <!-- D - 橙色椭圆 -->
        <ellipse
          :cx="getSize(svgWidth / 2)"
          :cy="getSize(svgHeight / 2 + svgHeight / 3 / 2 - 2)"
          :rx="getSize(svgWidth / 3)"
          :ry="getSize(svgHeight / 3)"
          fill="#ffffff"
          fill-opacity="0.3"
          stroke="url(#grad3)"
          stroke-width="2"
        />
      </g>
      <!-- 绘制标签和数据 -->
      <text :x="getSize(svgWidth / 2 - svgWidth / 3 / 2)" :y="getSize(svgHeight / 2 - svgHeight / 3)" text-anchor="end">
        <tspan :font-size="setSize(12)">B独家</tspan>
        <tspan class="cursor-pointer" fill="#3b82f6" :font-size="setSize(14)" @click="click('B')">1781.4万</tspan>
      </text>
      <text
        :x="getSize(svgWidth / 2 - svgWidth / 3 / 2)"
        :y="getSize(svgHeight / 2 - svgHeight / 3 + setSize(14) + 4)"
        text-anchor="end"
      >
        <tspan class="font-bold" :font-size="setSize(16)">B</tspan>
      </text>
      <line
        :x1="getSize(svgWidth / 2 - svgWidth / 3 / 2)"
        :y1="getSize(svgHeight / 2 - svgHeight / 3 + setSize(14) + 8)"
        :x2="getSize(svgWidth / 2 - svgWidth / 3 / 2 - setSize(80))"
        :y2="getSize(svgHeight / 2 - svgHeight / 3 + setSize(14) + 8)"
        stroke="#3b82f6"
        stroke-width="2"
      />
      <text
        :x="getSize(svgWidth / 2 - svgWidth / 3 / 2)"
        :y="getSize(svgHeight / 2 - svgHeight / 3 + setSize(14) + setSize(16) + 12)"
        text-anchor="end"
      >
        <tspan class="font-bold" fill="#3b82f6" :font-size="setSize(16)">17752.7万</tspan>
      </text>

      <text :x="getSize(svgWidth / 2 + svgWidth / 3 / 2 - 2)" :y="getSize(svgHeight / 2 - svgHeight / 3)">
        <tspan :font-size="setSize(12)">A独家</tspan>
        <tspan fill="#3b82f6" :font-size="setSize(14)">1781.4万</tspan>
      </text>
      <text :x="getSize(svgWidth / 2 + svgWidth / 3 / 2 - 2)" :y="getSize(svgHeight / 2 - svgHeight / 3 + setSize(14) + 4)">
        <tspan class="font-bold" :font-size="setSize(16)">A</tspan>
      </text>
      <line
        :x1="getSize(svgWidth / 2 + svgWidth / 3 / 2 - 2)"
        :y1="getSize(svgHeight / 2 - svgHeight / 3 + setSize(14) + 8)"
        :x2="getSize(svgWidth / 2 + svgWidth / 3 / 2 - 2 + setSize(80))"
        :y2="getSize(svgHeight / 2 - svgHeight / 3 + setSize(14) + 8)"
        stroke="#3b82f6"
        stroke-width="2"
      />
      <text
        :x="getSize(svgWidth / 2 + svgWidth / 3 / 2 - 2)"
        :y="getSize(svgHeight / 2 - svgHeight / 3 + setSize(14) + setSize(16) + 12)"
      >
        <tspan class="font-bold" fill="#3b82f6" :font-size="setSize(16)">17752.7万</tspan>
      </text>

      <text :x="getSize(svgWidth / 2)" :y="getSize(svgHeight / 2 + svgHeight / 3 / 2 + 10)" text-anchor="middle">
        <tspan :font-size="setSize(12)">D独家</tspan>
        <tspan fill="#3b82f6" :font-size="setSize(14)">1781.4万</tspan>
      </text>
      <text
        :x="getSize(svgWidth / 2)"
        :y="getSize(svgHeight / 2 + svgHeight / 3 / 2 + 10 + setSize(14) + 4)"
        text-anchor="middle"
      >
        <tspan class="font-bold" :font-size="setSize(16)">D</tspan>
      </text>
      <line
        :x1="getSize(svgWidth / 2 - setSize(40))"
        :y1="getSize(svgHeight / 2 + svgHeight / 3 / 2 + 10 + setSize(14) + 8)"
        :x2="getSize(svgWidth / 2 + setSize(40))"
        :y2="getSize(svgHeight / 2 + svgHeight / 3 / 2 + 10 + setSize(14) + 8)"
        stroke="#3b82f6"
        stroke-width="2"
      />
      <text
        :x="getSize(svgWidth / 2)"
        :y="getSize(svgHeight / 2 + svgHeight / 3 / 2 + 10 + setSize(14) + setSize(16) + 12)"
        text-anchor="middle"
      >
        <tspan class="font-bold" fill="#3b82f6" :font-size="setSize(16)">17752.7万</tspan>
      </text>

      <text :x="getSize(svgWidth / 2)" :y="getSize(svgHeight / 2)">
        <tspan fill="#3b82f6" text-anchor="middle" :font-size="setSize(12)">B&A&D</tspan>
      </text>
      <text :x="getSize(svgWidth / 2)" :y="getSize(svgHeight / 2 - (svgHeight / 3 / 4) * 3)">
        <tspan fill="#3b82f6" text-anchor="middle" :font-size="setSize(12)">B&A</tspan>
      </text>
      <text :x="getSize(svgWidth / 2 + svgWidth / 3 / 2)" :y="getSize(svgHeight / 2 + svgHeight / 3 / 4)">
        <tspan fill="#3b82f6" text-anchor="middle" :font-size="setSize(12)">A&D</tspan>
      </text>
      <text :x="getSize(svgWidth / 2 - svgWidth / 3 / 2)" :y="getSize(svgHeight / 2 + svgHeight / 3 / 4)">
        <tspan fill="#3b82f6" text-anchor="middle" :font-size="setSize(12)">B&D</tspan>
      </text>
    </svg>
  </div>
</template>

<script setup>
import { ref, onMounted } from "vue";
import { setSize } from "@/utils/index.js";
const containerRef = ref(null);
const svgWidth = ref(0);
const svgHeight = ref(0);
onMounted(() => {
  if (containerRef.value) {
    const container = containerRef.value;
    const { width, height } = container.getBoundingClientRect();
    svgWidth.value = Number(width).toFixed();
    svgHeight.value = Number(height).toFixed();
  }
});
const getSize = val => {
  return Number(val).toFixed();
};
const click = val => {
  console.log(val);
};
</script>