Posted in

深度解析 React 可拖动悬浮球_AI阅读总结 — 包阅AI

包阅导读总结

1.

关键词:`React`、`可拖动悬浮球`、`圆形菜单`、`右键菜单`、`应用切换`

2.

总结:本文介绍了基于 React 实现的高级可拖动悬浮球组件,包括其拖拽功能的实现、边界计算、边缘吸附效果、圆形应用菜单的创建原理、右键菜单的实现以及应用切换逻辑,还使用了多种技术和库,集成了复杂交互功能。

3.

主要内容:

– 可拖动悬浮球组件

– 利用 `react-draggable` 库实现拖拽功能,通过事件管理拖拽状态和位置,通过 `transform` 实现拖动效果,用 `x`、`y` 记录位置。

– 计算浏览器窗口和悬浮球的尺寸,得出拖拽边界值以限制悬浮球位置。

– 新增边缘吸附状态,实现吸附浏览器左右两边的效果及相应动画。

– 圆形应用菜单

– 通过对矩形元素变换创建扇形组成圆形菜单,计算每个扇形角度。

– 用特定代码实现容器变换,确保内容水平和面向圆心。

– 其他功能

– 右键菜单使用 `react-contexify` 实现。

– 点击应用图标可切换应用。

– 总结该组件集成了多种复杂交互功能,并使用了 `React Hooks` 管理多个状态。

思维导图:

文章地址:https://mp.weixin.qq.com/s/NulcPdszinHOqfJPlU76JQ

文章来源:mp.weixin.qq.com

作者:cv也要开心

发布时间:2024/8/31 8:23

语言:中文

总字数:1369字

预计阅读时间:6分钟

评分:91分

标签:React,前端开发,拖拽功能,边缘吸附,右键菜单


以下为原文内容

本内容来源于用户推荐转载,旨在分享知识与观点,如有侵权请联系删除 联系邮箱 media@ilingban.com

点击关注公众号,“技术干货”及时达!

深度解析 React 可拖动悬浮球

老板:我想要一个 web 悬浮球,能实现吗

我:ok

老板:我想要一个像 mac 一样的菜单栏

我:ok

小朋友才做选择,两个都安排上。

结果就是,普通的应用栏有悬浮球也有,右键切换,如下图。

本文将介绍一个 React 实现的高级可拖动悬浮球组件,它不仅具备基本功能,还集成了多项交互特性,大大提升了用户体验。

组件概览

这个悬浮球组件主要具有以下特点:

可拖拽功能

使用 react-draggable 库实现拖拽功能:

<Draggable
onStart={(e,position)=>{
setDragging(true);
setStartPosition(position);
}}
onDrag={(e,position)=>{
setPosition(position);
}}
onStop={(e,position)=>{
handleDragBoundary(e,position);
setEndPosition(position);
setDragging(false);
}}
handle="#centerButton"
position={position}
>
{/*悬浮球内容*/}
</Draggable>

这里通过 onStart、onDrag 和 onStop 事件来管理拖拽状态和位置。

拖动是通过 transform: translate(110px, -260px); 实现的用x,y记录拖动的位置 const [position, setPosition] = useState({ x: 0, y: 0 });

边界计算

首先,我们需要获取浏览器窗口和悬浮球的尺寸信息:

constbrowserWidth=window.innerWidth;
constbrowserHeight=window.innerHeight;
constfloatButtonNav=document?.getElementById('floatButtonNav');
if(!floatButtonNav)return;

constdistanceLeft=floatButtonNav.getBoundingClientRect().left;
constfloatButtonNavWidth=floatButtonNav.clientHeight||64;
constfloatButtonNavHeight=floatButtonNav.clientHeight||64;

接下来,我们计算拖拽的边界值:

//120isabsolutepositioning;10Boundarydistance;
constleftBoundary=-browserWidth+floatButtonNavWidth+120+10;
constrightBoundary=120-10;
consttopBoundary=-browserHeight+floatButtonNavHeight+120;
constbottomBoundary=120;

120 是悬浮球初始定位,10是额外的间距

位置限制

然后,我们使用这些边界值来限制悬浮球的位置:

setPosition({
x:x<leftBoundary?leftBoundary:x>rightBoundary?rightBoundary:x,
y:y<topBoundary?topBoundary:y>bottomBoundary?bottomBoundary:y
});

边缘吸附效果

新增一个边缘吸附的状态,根据不同的状态,展示出吸附浏览器左右两边的效果

主要是一些动画的效果

consthandleSuction=(suction:Suction)=>{
onClose();
setSuction(suction);
setTimeout(()=>{
setLockSuction(false);
},1000);
};

x<leftBoundary
?handleSuction(Suction.Left)
:x>rightBoundary
?handleSuction(Suction.Right)
:null;
.floatBtn{
will-change:auto;
position:absolute;
z-index:9999;
width:64px;
height:64px;
border-radius:50%;
background:rgba(28,32,35,0.9);
box-shadow:0px0px0px1pxrgba(0,0,0,0.1);
display:flex;
justify-content:center;
align-items:center;
transition:all0.5sease;

&[data-suction='0']{
opacity:1;
}

&[data-suction='left']{
transform:translateX(-100px);
animation:slide-right-left300mscubic-bezier(0.55,0.085,0.68,0.53);
}

&[data-suction='right']{
transform:translateX(100px);
animation:slide-left-right300mscubic-bezier(0.55,0.085,0.68,0.53);
}
}

@keyframesslide-right-left{
0%{
transform:translateX(40px);
}
100%{
transform:translateX(-100px);
opacity:0;
}
}

@keyframesslide-left-right{
0%{
transform:translateX(-40px);
}
100%{
transform:translateX(100px);
opacity:0;
}
}

圆形应用菜单(悬浮球)

通过对矩形元素进行变换来创建扇形,进而组成圆形菜单。

效果

实现原理

  1. 一个圆形,被均匀分割成6-8个扇形。每个扇形代表一个菜单项。中心有一个小圆,代表悬浮球本身
  • 扇形的角度(degree)= 360 / 菜单项数量
  • const[degree,contentSkewDegree,contentRotateDegree]=useMemo(()=>{
    constlen=apps?.length<6?6:apps?.length>8?8:apps?.length;
    consttemp:number=360/len;
    constskewDegree=-(90-temp);
    constrotateDegree=-(90-temp/2);
    return[temp,skewDegree,rotateDegree];
    },[apps.length]);

    <Box
    transform={
    isOpen
    ?`rotate(${degree*(index+1)}deg)skew(${90-degree}deg)`
    :`rotate(75deg)skew(60deg)`
    }
    //...其他属性
    >
    {/*内容*/}
    </Box>
    1. 有了扇形之后,还需要一个盒子放内容,想象一下,如果我们能看到这个过程的动画:
    transform={`skew(${contentSkewDegree}deg)rotate(${contentRotateDegree}deg)`}

    这行代码是整个容器的精髓所在。它包含两个关键的变换:

    • skew(${contentSkewDegree}deg): 这个倾斜变换是为了抵消父元素(扇形)的倾斜效果。还记得我们如何通过倾斜矩形来创建扇形吗?这里我们做的是反向操作,目的是让内容回到水平状态。
    • rotate(${contentRotateDegree}deg): 旋转变换确保内容面向圆心。没有这个旋转,图标可能会歪歪扭扭,不能很好地展示。
    <Flex
    justifyContent={'center'}
    pt="12px"
    className={styles.subItem}
    //Theiconisperpendiculartothecenterofthecircle
    transform={`skew(${contentSkewDegree}deg)rotate(${contentRotateDegree}deg)`}
    >
    constcalculateDegree=(index:number)=>{
    consttemp=-(degree*index+contentRotateDegree);
    return`rotate(${temp}deg)`;
    };
    <Flex
    w="32px"
    h="32px"
    backgroundColor={'rgba(244,246,248,0.9)'}
    border={'1pxsolid#FFFFFF'}
    borderRadius={'50%'}
    boxShadow={'0px0.5px1pxrgba(0,0,0,0.2)'}
    justifyContent={'center'}
    alignItems={'center'}
    //Theiconisperpendiculartothex-axisofthepage
    transform={calculateDegree(index+1)}
    >
    icon
    </Flex>

    右键菜单

    使用 react-contexify 实现右键菜单:

    const{show}=useContextMenu({
    id:Floating_Button_Menu_Id
    });

    constdisplayMenu=(e:MouseEvent<HTMLDivElement>)=>{
    e.stopPropagation();
    setIsRightClick(true);
    onClose();
    show({
    event:e,
    position:{
    x:'-65%',
    y:'-80%'
    }
    });
    };

    应用切换

    点击应用图标切换应用:

    consthandleNavItem=(e:MouseEvent<HTMLDivElement>,item:AppInfo)=>{
    if(item.key==='system-home'){
    //处理主页逻辑
    }elseif(item.pid===currentAppPid&&item.size!=='minimize'){
    //最小化当前应用
    }else{
    //切换到选中应用
    switchAppById(item.pid);
    }
    };

    总结

    本文深入剖析了一个基于 React 实现的高级可拖动悬浮球组件。这个组件集成了多项复杂的交互功能。

    我们详细分析了以下核心方面:

    • 使用 react-draggable 实现基础拖拽。
    • 利用 CSS 变换将矩形元素转化为扇形,组成圆形菜单。
    • 使用 React Hooks 管理组件的多个状态。