html部分:
<div> <div></div> <div></div> <div></div> <div></div> <div></div> <ul> <li v-for="item,index in bubbleData" :key="index" :style="{top:item.top,left:item.left,background:item.background,width:item.width+\'px\',height:item.width+\'px\',animationDuration:item.time+\'s\'}"> <span :style="{width:item.width+\'px\'}">{{item.width>40?item.name:\'\'}}</span> <span :style="{width:item.width+\'px\'}">{{item.width>40?item.value+\'%\':\'\'}}</span> <div>{{item.name+\':\'+item.value+\'%\'}}</div> </li> </ul> </div>js部分:
/** *获取n等分圆周坐标点 *半径:r *圆心坐标:(ox,oy) */ getPoint(r, ox, oy, n) { let point = []; var radians = (Math.PI / 180) * Math.round(360 / n); for (let i = 0; i < n; i++) { var x = ox + r * Math.sin(radians * i), y = oy + r * Math.cos(radians * i); point.unshift([Math.floor(x), Math.floor(y)]); } return point; }, // 初始化气泡图 initScatter() { this.bubbleData = [ {value:56,name:\'类目一\'}, {value:13,name:\'类目二\'}, {value:45,name:\'类目三\'}, {value:1,name:\'类目四\'}, {value:69,name:\'类目四\'} ] let offsets; let colors = ["#f1a137", "#8585f8", "#68ccf7", "#95da2f", "#f8a1ac"]; //气泡位置,只有一个时显示在中间,较多时围成一个圆 if (this.bubbleData.length === 1) { offsets = [[100, 100]]; } else { offsets = this.getPoint(100, 100, 110, this.bubbleData.length); } this.bubbleData.forEach((v, i) => { v.top = offsets[i][0] + "px"; v.left = offsets[i][1] + "px"; //循环使用colors颜色列表 v.background = colors[i % 5]; //将类目的value作为气泡的宽度,由于value为1-20时,气泡太小不美观,因此给每个气泡增加20的宽度 v.width = v.value + Math.ceil(v.value / (v.value + 1)) * 20; //气泡的动画时长 v.time = v.value>50?(v.value%6)+10:(v.value%6)+6 }); },css部分:
/* 气泡图 */ #scatter { width: 350px; height: 300px; position: relative; li { opacity: 0.7; border-radius: 50%; position: absolute; z-index: 1; display: flex; flex-direction: column; justify-content: center; align-items: center; animation-name: itemfloat; animation-timing-function: linear; animation-fill-mode: both; animation-iteration-count: infinite; .tip { width: 150px; height: 40px; line-height: 40px; text-align: center; color: #000; border: 1px solid #ccc; border-radius: 8px; background: #fff; display: none; position: absolute; top: -45px; z-index: 101; transition: display 2s; } span { color: #fff; font-size: 12px; text-align: center; padding: 0 5px; display: inline-block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } } li:hover .tip { display: block; } li:nth-child(2n) { animation-duration: 8s; } li:nth-child(2n-1) { animation-duration: 12s; } li:hover { z-index: 100; opacity: 1; transition: opacity 2 s; } .bubble_min:nth-child(1) { width: 20px; height: 20px; top: 100px; left: 40px; } .bubble_min:nth-child(2) { width: 60px; height: 60px; top: 130px; left: 100px; } .bubble_min:nth-child(3) { width: 50px; height: 50px; top: 90px; left: 180px; } .bubble_min:nth-child(4) { width: 30px; height: 30px; top: 250px; left: 300px; } .bubble_min:nth-child(5) { width: 30px; height: 30px; top: 110px; left: 240px; } .bubble_min { background-image: url(../../../../assets/bubble.png); background-size:100% 100% ; border-radius: 50%; position: absolute; z-index: 1; opacity: 0.5; animation-name: minibubble; animation-timing-function: linear; animation-fill-mode: both; animation-iteration-count: infinite; } .bubble8 { animation-duration: 8s; } .bubble10 { animation-duration: 10s; } .bubble12 { animation-duration: 12s; } @keyframes itemfloat { 0% { transform: translateY(0); } 25% { transform: translateY(-15px); } 50% { transform: translateY(0); } 75% { transform: translateY(15px); } 100% { transform: translateY(0); } } @keyframes minibubble { 0% { transform: translateY(200px) rotate(0deg); } 100% { transform: translateY(-240px) rotate(1500deg); } } }效果图如下: