简体   繁体   English

ReactJS:在拖动时计算“操纵杆”的方向(使用 Framer Motion)并根据位置更新 div 效果

[英]ReactJS: Calculating direction of 'joystick' while dragging (using Framer Motion) and updating div effects depending on position

I am learning how to use ReactJS by making a mock-up personal website.我正在通过制作一个模拟个人网站来学习如何使用 ReactJS。 The target design of the homepage is a joystick that, on drag, allows you to select which page you'd like to go to next.主页的目标设计是一个操纵杆,通过拖动,您可以选择下一个要转到的页面。 There are four buttons, positioned on the top, right, left, and bottom of the central circular GUI.有四个按钮,分别位于中央圆形 GUI 的顶部、右侧、左侧和底部。

My idea was, basically: click joystick, drag towards option.我的想法基本上是:单击操纵杆,向选项拖动。 If you approach option, it highlights it, and if if you get close enough to the option it navigates you to that page.如果您接近选项,它会突出显示它,如果您离该选项足够近,它会将您导航到该页面。

I am using framer motion's drag and onDragStart / onDragEnd / onDrag props to try and detect the direction of the drag.我正在使用成帧器运动的dragonDragStart / onDragEnd / onDrag道具来尝试检测拖动的方向。 Basically, I am getting the dragged element's clientX and clientY position fields (from the event packet emitted by the onDrag functional prop), and comparing it to the dragged element's original clientX and clientY positions.基本上,我正在获取拖动元素的clientXclientY位置字段(来自onDrag功能道具发出的event数据包),并将其与拖动元素的原始clientXclientY位置进行比较。 If the change in X is greater than the change in Y, I know that the closest option is on the X-axis, and I can perform some more calculations to figure out whether it's on the left or right side... ditto for the Y axis.如果 X 的变化大于 Y 的变化,我知道最接近的选项在 X 轴上,我可以执行更多计算来确定它是在左侧还是右侧......同上Y轴。 I know that these calculations work properly and quickly enough: WITHOUT using any [state, setState] to assign the hover effect to the target div, the script properly indicates and console.logs the correct 'link' it should go to.我知道这些计算工作正常且足够快:无需使用任何[state, setState]将悬停效果分配给目标 div,脚本会正确指示和 console.logs 应该转到的正确“链接”。

The issue is, once I try and use the same code with React's useState function (effectively telling the relevant component to re-render with a hover effect instead of just console.logging the direction), it doesn't work.问题是,一旦我尝试将相同的代码与 React 的useState函数一起使用(有效地告诉相关组件使用悬停效果重新渲染,而不仅仅是 console.logging 方向),它就不起作用了。 The hover effect gets stuck on the about page, no matter which direction I try and drag the joystick towards.无论我尝试向哪个方向拖动操纵杆,悬停效果都会卡在about页面上。

Here's some code to show what I'm doing.这是一些代码来显示我在做什么。 Again, I started learning React yesterday, so please let me know of any proper practices or more effective ways to do things if you see them.同样,我昨天开始学习 React,所以如果你看到它们,请告诉我任何正确的做法或更有效的做事方法。

const linkPaths = {
    top: "about",
    right: "resume",
    bot: "projects",
    left: "bots",
};

const Home = () => {
    const [isHovering, setIsHovering] = useState(false);
    const [joystickIsClicked, setJoystickIsClicked] = useState(false);
    const [hoveredLink, setHoveredLink] = useState("");

    var draggingTowards = "";
    var prevDraggingTowards = "";

    var dragStartX, dragStartY;

    const joystickWidth = 90;
    const outerCircleWidth = 600;
    const innerCircleWidth = 550;

    return (
            <Wrapper>
                <Content>
                    <OuterCircle width={outerCircleWidth} isHovering={isHovering}> 
                        <InnerCircle
                            width={innerCircleWidth}
                            isHovering={isHovering}
                            joystickIsClicked={joystickIsClicked}
                        >
                            <JoystickContainer
                                layout
                                drag
                                dragElastic={0}
                                width={joystickWidth}
                                dragConstraints={{
                                    top: -innerCircleWidth / 4,
                                    left: -innerCircleWidth / 4,
                                    right: innerCircleWidth / 4,
                                    bottom: innerCircleWidth / 4,
                                }}
                                whileHover={{
                                    scale: 1.2,
                                    cursor: "pointer",
                                    backgroundImage:
                                        "linear-gradient(black, black), linear-gradient(to bottom right, cyan, magenta)",
                                }}
                                onHoverStart={() => {
                                    setIsHovering(true);
                                }}
                                onHoverEnd={() => {
                                    setIsHovering(false);
                                    setJoystickIsClicked(false);
                                }}
                                onTapStart={() => {
                                    setJoystickIsClicked(true);
                                }}
                                onTap={() => {
                                    setJoystickIsClicked(false);
                                    console.log("finished tap");
                                }}
                                onDragStart={(event, info) => {
                                    dragStartX = event.clientX;
                                    dragStartY = event.clientY;
                                    // console.log(`(${dragStartX}, ${dragStartY})`);
                                }}
                                onDragEnd={(event, info) => {
                                    // console.log(`(${event.clientX}, ${event.clientY})`);
                                }}
                                onDrag={(event, info) => {
                                    if (Math.abs(event.clientX - dragStartX) > Math.abs(event.clientY - dragStartY)) {
                                        // more movement in x direction
                                        if (event.clientX > dragStartX) {
                                            draggingTowards = linkPaths.right;
                                        } else {
                                            draggingTowards = linkPaths.left;
                                        }
                                    }
                                    //more movement in y direction
                                    else {
                                        if (event.clientY > dragStartY) draggingTowards = linkPaths.bot;
                                        else draggingTowards = linkPaths.top;
                                    }

                                    if (draggingTowards !== prevDraggingTowards) {
                                        // only update state function on a change of direction dragging
                                        setHoveredLink(draggingTowards);
                                        prevDraggingTowards = draggingTowards;
                                    }
                                    console.log(hoveredLink);
                                }}
                            </JoystickContainer>
                        </InnerCircle>
                    </OuterCircle>
                </Content>
            </Wrapper>

Is there some key thing regarding stateful values and functions in React that I'm missing?我缺少关于 React 中的有状态值和函数的一些关键信息吗? I wondered if the re-rendering of the prop was causing issues with the drag, but if that's the case I am unsure how to proceed.我想知道道具的重新渲染是否会导致阻力问题,但如果是这种情况,我不确定如何继续。

GUI Layout with the effect visible -- dragging direction stuck on "about"效果可见的 GUI 布局——拖动方向停留在“关于”

Console.logs of failing hoveredLink state失败的 hoveredLink 状态的 Console.logs

Note: This was originally added as part of OP's question, I just edited it out and posted it as an answer.注意:这最初是作为 OP 问题的一部分添加的,我只是将其编辑并发布为答案。 I take no credit for the following content.我不相信以下内容。

Turns out that Framer Motion does not work well with the clientX and clientY packages.事实证明,Framer Motion 不适用于 clientX 和 clientY 包。 When you change the state, those variables end up messing up and defaulting to some value.当您更改状态时,这些变量最终会混乱并默认为某个值。 Instead, what I did to fix this, was I used the info event that is also sent out during Framer Motion's drag event.相反,我为解决这个问题所做的是使用了在 Framer Motion 的drag事件期间也发送的info事件。 The packet is structured like this:数据包的结构如下:

{
  delta: {x, y} // a Point that tracks the change in X and Y from the last packet
  offset: {x, y} // didn't really figure this out, but I assume it's error propagation from the delta packet.
  point: {x, y} // the new Point location of the dragged div
  velocity: {x, y} // x and y velocity of the dragged div
}

Essentially, I just made two variables in the React component: deltaX and deltaY.本质上,我只是在 React 组件中创建了两个变量:deltaX 和 deltaY。 Whenever the dragged component moved, I added the info.delta.x and info.delta.y to the deltaX and deltaY variables, respectively.每当拖动的组件移动时,我将info.delta.xinfo.delta.y分别添加到 deltaX 和 deltaY 变量中。 This gave me a really good 'position' of the dragged element.这给了我一个非常好的拖动元素的“位置”。 Wish that FM's clientX and clientY worked as advertised, but this was a decent enough workaround for my own purposes.希望 FM 的clientX and clientY像宣传的clientX and clientY工作,但对于我自己的目的来说,这是一个足够体面的解决方法。

TL;DR:特尔;博士:

Found a pretty janky solution that is computation intensive but it worked flawlessly on multiple devices without any visible jank.找到了一个计算密集型的非常笨拙的解决方案,但它可以在多个设备上完美运行,没有任何可见的卡顿。 Issue stems from Framer Motion's drag packets emitting clientX / clientY location objects that are undefined when the state of the component is changed – basically, re-rendering components messes with the geometry somehow.问题来自成帧器Motion的茎drag发射分组clientX / clientY被位置对象undefined基本上,重新呈现组件混乱与几何不知何故-当所述组件的状态被改变。 Instead of using the clientX / clientY fields of the event packet, I used the delta.x / delta.y fields of the info packet.而不是使用的clientX / clientY的领域event包,我用delta.x / delta.y该领域info包。

For anyone else struggling with some of the framer motion event packets, it's not just you!对于其他正在为一些成帧器运动事件数据包而苦苦挣扎的人来说,不仅仅是您! But there are ways to get around it.但是有办法绕过它。 Feel free to message me if you'd like to see the full code -- I didn't paste the finished code here to avoid too long of a post.如果您想查看完整代码,请随时给我发消息——我没有将完成的代码粘贴在这里以避免帖子太长。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM