带Label的韦恩图(vue)
韦恩图
效果:
实现:
图形使用venn.js
和d3.js
绘制
label使用元素定位绘制(可根据数据动态计算)
代码:
- tempalte
<div class="venn mgt-4 center-flex" v-else ref="vennBox" style="height: 140px;">
<div class="venn-right" :style="{ left: lengedPos.rightX + 'px' }" ref="vennRight">
<p class="color-666 fs-12 ellipsis lh-17">XXXX</p>
<p class="lh-20">720人</p>
</div>
<div class="venn-left" :style="{ right: lengedPos.leftX + 'px' }" ref="vennRight">
<p class="color-666 fs-12 ellipsis lh-17">XXX</p>
<p class="lh-20">720人</p>
</div>
<div class="venn-bottom flex-align" ref="vennBottom">
<div class="venn-text">
<p class="color-666 fs-12 lh-17">XXXX</p>
<p class="lh-20">720人</p>
</div>
<img src="@/assets/img/dataApplication/bottom.png" />
</div>
<div ref="vennContainer" class="venn-container" style="width: 325px;height: 120px;"></div>
</div>
- script
import * as d3 from 'd3';
import { VennDiagram } from 'venn.js';
data() {
return {
intersectionNum: 20,
lengedPos: {
rightX: 0,
leftX: 0
},
};
},
mounted() {
this.$nextTick(() => {
setTimeout(() => {
this.renderVennDiagram()
}, 100);
})
},
methods: {
renderVennDiagram() {
const container = this.$refs.vennContainer;
// 数据
const sets = [
{ sets: ['A'], size: 100, label: 'A' },
{ sets: ['B'], size: 100, label: 'B' },
{ sets: ['A', 'B'], size: this.intersectionNum, label: 'A ∩ B' }
];
// 绘制韦恩图
const chart = VennDiagram().styled(false).width(325).height(120);
const svg = d3.select(container).append('svg')
.attr('width', 325)
.attr('height', 120);
svg.datum(sets).call(chart);
this.$nextTick(() => {
const gElement = document.querySelector('g.venn-area.venn-circle[data-venn-sets="B"]');
const gRect = gElement.getBoundingClientRect();
const parentRect = this.$refs.vennBox.getBoundingClientRect();
this.lengedPos.rightX = gRect.left - parentRect.left + gRect.width + 2;
const gAElement = document.querySelector('g.venn-area.venn-circle[data-venn-sets="A"]');
const gARect = gAElement.getBoundingClientRect();
this.lengedPos.leftX = parentRect.right - gARect.left + 0
})
},
- style
::v-deep .venn-container {
>svg {
transform: scaleY(0.95);
}
g {
position: relative;
}
svg {
text {
fill: transparent;
display: none;
}
}
[data-venn-sets="A"] {
fill: #1083FB;
}
[data-venn-sets="B"] {
fill: #6CD3FF;
}
[data-venn-sets="A_B"] {
fill: #FFC300;
path {
stroke: white;
stroke-width: 2px;
}
}
}
.venn {
position: relative;
&-left {
position: absolute;
top: 15px;
&::after {
content: '';
position: absolute;
right: -17.23px;
background: url(~@/assets/img/dataApplication/left.png) center/100% 100% no-repeat;
top: 50%;
transform: translateY(-50%);
width: 17.23px;
height: 4.8px;
}
}
&-right {
position: absolute;
top: 15px;
&::before {
content: '';
position: absolute;
left: -21.06px;
background: url(~@/assets/img/dataApplication/right.png) center/100% 100% no-repeat;
top: 50%;
transform: translateY(-50%);
width: 21.06px;
height: 4.8px;
}
}
&-bottom {
position: absolute;
left: 50%;
transform: translateX(-100%) translateY(100%);
bottom: 37px;
.venn-text {
position: absolute;
left: 0;
top: -3px;
}
img {
width: 84px;
height: 20px;
}
&::after {
width: 84px;
height: 20px;
display: inline-block;
background: url(~@/assets/img/dataApplication/bottom.png) center/100% 100% no-repeat;
content: '';
position: absolute;
right: 0;
}
.venn-text {
position: absolute;
left: 0;
transform: translateX(-100%);
}
}
}