react + css 实现 椭圆布局

一 项目需求,按照椭圆形状布局一些元素

具体布局如下图所示:

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>
  );
};
相关推荐
学习OK呀1 分钟前
后端上手学习react基础知识
前端
星火飞码iFlyCode2 分钟前
大模型提效之服务端日常开发
前端
zoahxmy09293 分钟前
Canvas 实现单指拖动、双指拖动和双指缩放
前端·javascript
花花鱼3 分钟前
vue3 动态组件 实例的说明,及相关的代码的优化
前端·javascript·vue.js
Riesenzahn5 分钟前
CSS的伪类和伪对象有什么不同?
前端·javascript
Riesenzahn5 分钟前
请描述下null和undefined的区别是什么?这两者分别运用在什么场景?
前端·javascript
代码小学僧5 分钟前
一行代码顶二十行代码! 🔧 修复 React 聊天室滚动加载问题 Bugfix 解决方法分享
前端·css·react.js
__不想说话__6 分钟前
前端视角下的AI应用:技术融合与工程实践指南
前端·javascript·aigc
niusir6 分钟前
使用 useCallback 和 useMemo 进行 React 性能优化
前端·javascript·react.js
niusir8 分钟前
深入理解 React 自定义 Hook
前端·react.js·前端框架