效果如下:如下六个图标围绕平台中心点旋转
- template
<!-- 容器 -->
<div class="container">
<!-- 容器中的每个气泡 -->
<div
v-for="item in bubbleList"
:key="item.name"
class="bubble-item"
:style="{
left: `${
item.x}px`,
top: `${
item.y}px`,
transform: `scale(${
item.scale})`,
transformOrigin: `50% ${
item.transformY}%`,
zIndex: item.zIndex,
backgroundImage: `url(${
item.url})`,
backgroundSize: '100% 100%',
opacity: item.opacity,
}"
:aria-label="item.name?.substring(0, 2)"
:aria-value="item.name"
@click="bubbleClick(item)"
>
<!-- 气泡中的名字 -->
<div class="bubble-item-name">{
{
item.name }}</div>
<!-- 气泡中的数量 -->
<div class="bubble-item-count" :style="{ color: item.color }">{
{
item.count }}</div>
</div>
</div>
- data数据源
data() {
return {
/**模板数据 */
templateList: [
{
name: "探访关爱",
code: "1",
count: 100,
color: "rgba(0, 240, 255, 1)",
url: require("../../images/visit-care.png"),
},
{
name: "适老化换新",
code: "2",
count: 200,
color: "rgba(254, 189, 76, 1)",
url: require("../../images/aging.png"),
},
{
name: "家庭床位",
code: "3",
count: 300,
color: "rgba(76, 254, 227, 1)",
url: require("../../images/bed-care.png"),
},
{
name: "助餐服务",
code: "4",
count: 400,
color: "rgba(251, 150, 255, 1)",
url: require("../../images/meal.png"),
},
{
name: "居家服务",
code: "5",
count: 400,
color: "rgba(0, 240, 255, 1)",
url: require("../../images/visit-care.png"),
},
{
name: "智能看护",
code: "6",
count: 500,
color: "rgba(76, 204, 254, 1)",
url: require("../../images/smart-care.png"),
},
],
/**气泡实例数组 */
bubbleList: [],
/**每一帧应该旋转的角度 */
angle: 0,
/**最新更新时间 */
lastTime: null,
/**是否已经销毁 */
destroyed: false,
/**浏览器动画优化实例 */
animationFrameId: null,
};
},
- mounted
mounted() {
// requestAnimationFrame是浏览器用于调度动画帧的一种机制,它会根据浏览器的刷新频率来调用回调函数,从而优化动画性能。
this.animationFrameId = requestAnimationFrame(this.updateScenePosition);
this.$once("hook:beforeDestroy", () => {
cancelAnimationFrame(this.animationFrameId);
this.animationFrameId = null;
});
},
- methods
methods: {
updateScenePosition(timestamp) {
if (this.destroyed) return;
// 初始化开始时间
if (!this.lastTime) this.lastTime = timestamp;
// 计算时间差(毫秒)
const deltaTime = timestamp - this.lastTime;
this.lastTime = timestamp;
// 设定每秒旋转的角度(比如90度/秒)
const rotationSpeed = 20; // 度/秒
// 根据时间差计算这一帧应该旋转的角度
this.angle = (this.angle + (rotationSpeed * deltaTime) / 1000) % 360;
this.calculateItemPosition(this.angle);
this.animationFrameId = requestAnimationFrame(this.updateScenePosition);
},
/**
* 动画icon点击事件
* @param item
*/
bubbleClick(item) {
},
/**
* 计算列表中item的坐标,根据item的index计算坐标
* @param {
*} list
* @param {
number} angle 椭圆的角度
* @returns
*/
calculateItemPosition(angle = 0) {
// 深拷贝一份模板数据
const list = this.toolClass.deepClone(this.templateList);
const a = 200; // 椭圆的水平半轴
const b = 100; // 椭圆的垂直半轴
const h = 400; // 椭圆的中心点x
const k = 200; // 椭圆的中心点y
let n = list.length; // 总项数
let angleInPI = (2 * Math.PI * angle) / 360;
list.forEach((item, i) => {
const index = i;
const theta = (2 * Math.PI * index) / n;
const x = a * Math.cos(theta + angleInPI) + h;
const y = b * Math.sin(theta + angleInPI) + k;
// 由于item尺寸从100px变为110px,需要将偏移量从50调整为55
item.x = x - 55; // 调整x坐标使得item的中心对准椭圆上的坐标
item.y = y - 55; // 调整y坐标使得item的中心对准椭圆上的坐标
const itemAngle = ((360 * index) / n + angle) % 360;
let scale = 1;
let translateY = "0px";
let transformY = 50;
if (itemAngle < 180) {
scale = 1 - 0.3 * Math.abs((itemAngle - 90) / 90);
item.opacity = 1;
} else {
scale = 0.5 + 0.2 * Math.abs((itemAngle - 270) / 90);
transformY = 50 * (1 - Math.abs((itemAngle - 270) / 90));
item.opacity = 0.3 + 0.4 * Math.abs((itemAngle - 270) / 90);
}
item.translateY = translateY;
item.scale = scale;
item.angle = itemAngle.toFixed(1);
item.zIndex = itemAngle < 180 ? 1 : 0;
});
// 更新气泡实例数组
this.bubbleList = list;
},
},
- style
.container {
width: 800px;
height: 400px;
position: relative;
// 背景
background: url("@/views/bed-care/images/bed-care-bg.png") no-repeat;
background-size: 100% 100%;
.bubble-item {
position: absolute;
width: 110px;
height: 110px;
cursor: pointer;
transition: all 0.3s;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
.bubble-item-name {
font-family: Adobe Heiti Std;
font-weight: normal;
font-size: 14px;
color: #ffffff;
}
.bubble-item-count {
font-family: DingTalk JinBuTi;
font-weight: 400;
font-size: 20px;
margin-top: 5px;
}
}
}