一 项目需求,按照椭圆形状布局一些元素
具体布局如下图所示:

react 代码如下:
javascript
import React, {useEffect, useRef} from 'react';
import "./index.less";
export default () => {
const containerRef = useRef<HTMLDivElement>(null);
// 计算周围圆点的位置。
const calculatePosition = () => {
if (containerRef.current) {
const childElements = containerRef.current?.querySelectorAll('.circle');
const longRadius = 450; // 椭圆长半轴 (900px/2)
const shortRadius = 200; // 椭圆短半轴 (400px/2)
const offset = 40; // 周围圆圈与椭圆的间距
childElements.forEach((circle: any, index: number) => {
// 计算元素的角度
const angle = Math.PI + (index / 13) * Math.PI * 2; // 写法2: 1号元素在椭圆的左边
let x: number = (longRadius + offset) * Math.cos(angle); // 计算元素的x轴距离
let y: number = (shortRadius + offset) * Math.sin(angle);// 计算元素的y轴距离
circle.style.setProperty('--x', `${x}px`); // 进行x轴偏移
circle.style.setProperty('--y', `${y}px`);//进行y轴偏移
});
}
}
useEffect(() => {
calculatePosition();
}, []);
return (
<div id={"pageContainer"} className="page">
<div id={"chartContainer"} className="container" ref={containerRef}>
<div className="centerCircle"></div>
<div className="circle">1</div>
<div className="circle">2</div>
<div className="circle">3</div>
<div className="circle">4</div>
<div className="circle">5</div>
<div className="circle">6</div>
<div className="circle">7</div>
<div className="circle">8</div>
<div className="circle">9</div>
<div className="circle">10</div>
<div className="circle">11</div>
<div className="circle">12</div>
<div className="circle">13</div>
</div>
</div>
);
};
less代码如下:
css
.page {
display: flex;
justify-content: center;
align-items: center;
}
.container {
position: relative;
width: 100%;
height: calc(100vh - 240px);
}
.centerCircle {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 900px;
height: 400px;
background-color: #4CAF50;
border-radius: 50%;
}
.circle {
position: absolute;
width: 40px;
height: 40px;
background-color: #ff5722;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-weight: bold;
transform: translate(-50%, -50%);
--x: 0;
--y: 0;
left: calc(50% + var(--x));
top: calc(50% + var(--y));
}
功能拓展,CSS样式都不会变。下面就不再写css样式了,用上面的即可。
二 功能拓展:以上布局,顶点1是在左边,顺时针围绕布局,现在想将顶点1放到右边,就要对angle计算进行调整。
改变前的效果如图:

改变后的效果如图:

原来的代码是:
javascript
const angle = Math.PI + (index / 13) * Math.PI * 2
调整后的代码是:
javascript
const angle: number = (index / 13) * Math.PI * 2; // 写法1:1号元素在椭圆的右边
三 功能拓展:单独对某个点位的元素进行位置调整
如图,调整第 8个元素的位置,调整结果如下图:

要达到以上效果,需要进行判断点位。在进行位置偏移前对x y值进行修改。新增点位判断代码如下图所示:
这里对 x 操作 就是进行左右偏移,对 y 轴操作,就是上下偏移。

四 功能拓展:对屏幕缩放时,同时对椭圆布局的内容进行缩放,并且不改变内容的布局、位置。

实现方式,将计算缩放比例和大小使用transform和scale方法进行调整。同时监听屏幕缩放事件,并且在计算布局时调用。计算函数如下:

五 以上所有功能的完整代码如下:
javascript
import React, {useEffect, useRef} from 'react';
import "./index.less";
export default () => {
const containerRef = useRef<HTMLDivElement>(null);
// 当窗口缩小时,将页面等比例缩小,这样就不会造成里面的元素错位和重叠
const scaleDataCompDiv = () => {
// 这里需要指定外部元素和内部元素,用于计算布局位置。
const outer = document.getElementById('pageContainer');
const inner = document.getElementById('chartContainer');
if (outer && inner) {
const outerWidth = outer.offsetWidth;
const outerHeight = outer.offsetHeight;
// 这里是里面内容的原始宽高,指定之后,才能根据页面的缩放进行比例的计算(这个为了比例好看,可以自定义)
const innerWidth = 800;// 内部 div 的原始宽度
const innerHeight = 600; // 内部 div 的原始高度
// 计算缩放比例(以宽度或高度的较小比例为准,避免超出)
const scale = Math.min(
outerWidth / innerWidth,
outerHeight / innerHeight
);
// 应用缩放
inner.style.transform = `scale(${scale})`;
}
}
useEffect(() => {
// 窗口大小变化时重新计算
window.addEventListener('resize', scaleDataCompDiv);
return () => {
window.removeEventListener('resize', scaleDataCompDiv);
}
}, []);
// 计算周围圆点的位置。
const calculatePosition = () => {
if (containerRef.current) {
const childElements = containerRef.current?.querySelectorAll('.circle');
const longRadius = 450; // 椭圆长半轴 (900px/2)
const shortRadius = 200; // 椭圆短半轴 (400px/2)
const offset = 40; // 周围圆圈与椭圆的间距
childElements.forEach((circle: any, index: number) => {
// 计算元素的角度
// const angle: number = (index / 13) * Math.PI * 2; // 写法1:1号元素在椭圆的右边
const angle = Math.PI + (index / 13) * Math.PI * 2; // 写法2: 1号元素在椭圆的左边
let x: number = (longRadius + offset) * Math.cos(angle); // 计算元素的x轴距离
let y: number = (shortRadius + offset) * Math.sin(angle);// 计算元素的y轴距离
// 这里对元素序号的判断,可以对 x y进行特殊调整。实现部分元素特殊位置的展示
if (index === 7) { // 如果index是7,则,对第8个元素的位置进行特殊调整。
x += 200; // 第8个元素向右再偏移100px的距离
}
circle.style.setProperty('--x', `${x}px`); // 进行x轴偏移
circle.style.setProperty('--y', `${y}px`);//进行y轴偏移
});
// 需要统一缩放比例。否则会导致视觉上的差异。
scaleDataCompDiv();
}
}
useEffect(() => {
calculatePosition();
}, []);
return (
<div id={"pageContainer"} className="page">
<div id={"chartContainer"} className="container" ref={containerRef}>
<div className="centerCircle"></div>
<div className="circle">1</div>
<div className="circle">2</div>
<div className="circle">3</div>
<div className="circle">4</div>
<div className="circle">5</div>
<div className="circle">6</div>
<div className="circle">7</div>
<div className="circle">8</div>
<div className="circle">9</div>
<div className="circle">10</div>
<div className="circle">11</div>
<div className="circle">12</div>
<div className="circle">13</div>
</div>
</div>
);
};