要使用JavaScript和数学函数实现环形排列的标签,我们需要按照以下步骤进行
(1)确定圆心: 首先,设定一个固定的圆心位置,通常设在父容器的正中心。假设父容器的宽度和高度相等,且为containerWidth和containerHeight。
(2)确定半径: 设定一个固定的半径radius,这个值将决定环形标签的紧凑程度。
(3)元素数量: 确定需要排列的元素数量,length。
(4)计算角度: 每个元素占据的角度为360° / length。这将帮助我们计算每个元素相对于圆心的位置。
(5)使用三角函数计算位置: 对于每一个元素,我们可以根据其在环中的顺序索引i来计算其在环上的角度θ(单位为弧度)。然后,利用三角函数sin和cos来计算该点在直角坐标系中的x坐标(即left值)和y坐标(即top值)。
x = radius * Math.cos(θ)
y = radius * Math.sin(θ)
注意,θ应该是从极坐标转换到直角坐标的弧度值,可以通过 θ = (i * 2 * Math.PI) / elementCount 来计算,其中i是从0开始的索引
。
(6)调整坐标以适应HTML元素定位: 由于HTML的坐标原点位于元素的左上角,而我们的计算是以圆心为原点,因此还需要对计算出的x和y值进行适当的偏移,使元素正确居中显示。具体来说,需要将x和y值分别加上容器的宽高的一半,并考虑容器的偏移(如果有的话)。
(7)最后,通过JavaScript为每个子元素设置position: absolute
,并根据计算出的left
和top
值进行定位。
原文介绍👉传送门 亲测好用
全部代码👇
<!-- Vue 2 Code -->
<template>
<div class="li" v-if="boxShow">
<div
class="box"
v-for="(item, index) in data"
:key="item.id"
@click="handleItem(item,index)"
>
<div class="text">
{{ item.name }}
</div>
</div>
</div>
</template>
<script>
export default {
name: "circle",
components: {},
data() {
return {
data: [],
boxShow: true,
};
},
created() {
this.initData();
},
mounted() {
this.$nextTick(() => this.initBox());
},
methods: {
// 模拟数据
initData() {
/*
* @Author: Do not edit
* @Date: 2024-05-20 17:55:50
* @LastEditors: Do not edit
* @LastEditTime: 2024-05-20 17:55:50
* @FilePath: Do not edit
* @Description: 拟生成数据Array,名称为随机的1、2、3、4、5
*/
// 生成条数
const steps = 10;
const item = () => {
// 生成随机名称
const name = Math.floor(Math.random() * steps);
// 获取当前时间戳
const timestamp = Date.now();
return {name, timestamp};
};
let array = [];
for (let i = 0; i < steps; i++) {
array.push(item());
}
// console.log(array)
this.data = array;
},
initBox() {
const dotLeft = 150; // 中心点横坐标
const dotTop = 150; // 中心点纵坐标
const radius = 100; // 半径
const boxs = document.getElementsByClassName("box"); // 每个box 数量
this.setBoxPositions(boxs, radius, dotLeft, dotTop);
},
// 处理元素排列位置
setBoxPositions(boxs, radius, dotLeft, dotTop) {
const boxCount = boxs.length;
const stard = 0; //起始角度
const angle = 360 / boxCount + stard; //每一个BOX对应的角度;
const radian = (angle * Math.PI) / 180; //每一个BOX对应的弧度;
for (let i = 0; i < boxCount; i++) {
boxs[i].style.left = Math.sin(radian * i) * radius + dotLeft + "px";
boxs[i].style.top = Math.cos(radian * i) * radius + dotTop + "px";
}
},
// 点击
handleItem(item,index) {
// 这里处理点击后的逻辑
console.log(item,index);
},
},
};
</script>
<style lang="scss" scoped>
.li {
position: relative;
z-index: 199;
width: 380px;
height: 380px;
margin: 10px;
.box {
width: 50px;
height: 50px;
line-height: 50px;
font-size: 15px;
position: absolute;
cursor: pointer;
text-align: center;
border: 1px solid #1ba1ff;
border-radius: 50%;
}
}
</style>
还需要有优化的地方
(1)使用Vue的响应式数据更新位置:虽然直接操作DOM可以实现功能,但在Vue中更推荐使用数据绑定来更新视图。可以考虑将计算好的left和top值直接存入每个item的数据对象中,利用Vue的响应式机制自动更新视图。
(2)考虑元素大小:当前代码未考虑box的实际尺寸,可能导致视觉上不是完美的环形排列。可以在计算left和top时,减去或加上box宽度的一半,以确保元素中心对齐。
(3)过渡动画:为了增强用户体验,可以考虑添加CSS过渡或Vue的动态过渡效果,使得元素在布局变化时有平滑的动画效果。
(4)自适应性:如果需要该组件在不同尺寸的容器中都能良好显示,可以考虑将中心点坐标、半径等值与容器尺寸动态绑定,实现更好的响应式设计。