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>
  );
};
相关推荐
耶啵奶膘2 小时前
uniapp+firstUI——上传视频组件fui-upload-video
前端·javascript·uni-app
视频砖家3 小时前
移动端Html5播放器按钮变小的问题解决方法
前端·javascript·viewport功能
lyj1689973 小时前
vue-i18n+vscode+vue 多语言使用
前端·vue.js·vscode
小白变怪兽5 小时前
一、react18+项目初始化(vite)
前端·react.js
ai小鬼头5 小时前
AIStarter如何快速部署Stable Diffusion?**新手也能轻松上手的AI绘图
前端·后端·github
墨菲安全6 小时前
NPM组件 betsson 等窃取主机敏感信息
前端·npm·node.js·软件供应链安全·主机信息窃取·npm组件投毒
GISer_Jing6 小时前
Monorepo+Pnpm+Turborepo
前端·javascript·ecmascript
天涯学馆6 小时前
前端开发也能用 WebAssembly?这些场景超实用!
前端·javascript·面试
我在北京coding7 小时前
TypeError: Cannot read properties of undefined (reading ‘queryComponents‘)
前端·javascript·vue.js
前端开发与ui设计的老司机7 小时前
UI前端与数字孪生结合实践探索:智慧物流的货物追踪与配送优化
前端·ui