如何在反应过渡组动画中正确居中旋转轴

How to center rotation axis properly in react-transition-group animation

我在 React 网页的菜单按钮动画中设置旋转轴时遇到问题。 我正在使用 react 17.0.2 和 react-transition-group 4.4.2.

我的菜单的概念是在单击按钮后旋转按钮。 如果之前点击了其他按钮,它应该旋转回到原来的位置,新按钮应该旋转 - 以显示它处于活动状态。 我在使动画旋转轴完美居中时遇到问题:

[旋转前的菜单按钮] [1] : https://i.stack.imgur.com/7nVnv.jpg

[旋转后菜单按钮右移][2] : https://i.stack.imgur.com/yB28J.jpg

如图所示 [2] 中间的菜单按钮向右移动并且没有与其余按钮对齐,我希望它与所有按钮完美对齐。

我的NavigationItems.js描述了按钮:

import React, { useState } from "react";
import { TransitionGroup } from "react-transition-group";
import classes from "./NavigationItems.module.scss";
import NavigationItem from "./NavigationItem/NavigationItem"

const data = [
    {in:false, id:0, desc:"About me",href:"/photo-gallery/info"},
    {in:false, id:1, desc:"Photo gallery",href:"/photo-gallery/photos"},
    {in:false, id:2, desc:"Some tests",href:"/photo-gallery/education"}
];

const NavigationItems = () => {

    const [allButtons, setAllButtons] = useState(data);
    const [prevButton, setPrevButton] = useState({
        in:false, id:-1, desc:"",href:""
    });

    const allButtonsDeepUpdate = (idx, obj, updatePrevButton) => {
        const allButtonsCpy = [];
        for(let i=0;i<allButtons.length;i++) {
            if(i===idx) {
                allButtonsCpy.push(Object.assign({},obj));
            } else if (updatePrevButton && i===prevButton.id) {
                allButtonsCpy.push(Object.assign({},prevButton));
            } else {
                allButtonsCpy.push(Object.assign({},allButtons[i]));
            };
        };
        setAllButtons(allButtonsCpy);
     };

    const enterAnimation = (idx) => {

        if(allButtons[idx].id !== prevButton.id) {
            const newButton = {...allButtons[idx], ...{in:true}};
            if (prevButton.id !== -1)
                setPrevButton({...prevButton,...{in:false}});
            console.log("newButton:",newButton) ;
            console.log("prevButton:",prevButton);
            allButtonsDeepUpdate(idx, newButton, prevButton.id>=0 ? true : false)
            setPrevButton(Object.assign({},allButtons[idx]));
        }
    };

    return ( 
            <div>   
            <TransitionGroup component="ul" className={classes.NavigationItems}>
                {allButtons.map((button) => (
                    <NavigationItem
                        starter={button.in}
                        pkey={button.id}
                        timeout={1000}
                        click={enterAnimation.bind(this,button.id)}
                        link={button.href}
                    >
                        {button.desc}
                    </NavigationItem>
                ))}
            </TransitionGroup>
            </div>
    );
};
 
export default NavigationItems;

NavigationItems.js 对应的 scss 类 (NavigationItems.module.scss) 如下所示:

    @import '../../../sass/abstracts/variables.scss';
    
    .NavigationItems {
        margin: 0;
        padding: 0;
        list-style: none;
        display: block;
        //flex-flow: column;
        //flex-direction: column;
        //justify-content: center;
        height: 100%;
    }
    
    @media (min-width: $min-width-small-res) {
        .NavigationItems {
            flex-flow: row;
            flex-direction: row;
            justify-content: space-evenly;
        }
    }

请注意,当我将样式切换为 flexbox 时,旋转轴也没有正确居中。 我正在制作动画的 NavigationItem.js 看起来像这样(我正在使用 react-router-dom 6 但它也可以与 5 一起使用):

    import React from 'react';
    import { NavLink } from 'react-router-dom';
    import { CSSTransition } from 'react-transition-group';
    import classes from './NavigationItem.module.scss';
     
    const NavigationItem = (props) => {
        const nodeRef = React.createRef();
    
        return (
            <CSSTransition
                key={props.pkey}
                nodeRef={nodeRef}
                in={props.starter}
                classNames={{
                    enter: classes.NavigationItemEnter,
                    enterActive: classes.NavigationItemEnterActive,
                    enterDone: classes.NavigationItemEnterDone,
                    exit: classes.NavigationItemExit,
                    exitActive: classes.NavigationItemExitActive,
                    exitDone: classes.NavigationItemExitDone,
                }}
                timeout={props.timeout}
            >
                <li ref={nodeRef} className={classes.NavigationItem} onClick={props.click}>
    
                    <NavLink
                        // activeClassName={classes.active} // react-router-dom v.5
                        //className={({isActive}) => isActive ? classes.active : ''} // v.6
                        to={props.link}
                        exact={props.exact}
                    >
                        {props.children}
                    </NavLink>
                </li>
            </CSSTransition>
        );
    }
     
    export default NavigationItem;

相应的 类 (NavigationItem.module.scss) 看起来像这样:

@import '../../../../sass/abstracts/variables.scss';

.NavigationItem {
    box-sizing: border-box;
    display: inline-block;
    width: 100%;
    //backface-visibility: hidden;
    transform-origin: center center 0;

    &Enter {
        transform: rotate(0deg);
    }

    &EnterActive {
        transform: rotate(180deg);
        transition: transform 500ms linear
    }

    &EnterDone {
        transform: rotate(180deg);
    }

    &Exit {
        transform: rotate(180deg);
    }

    &ExitActive {
        transform: rotate(0deg);
        transition: transform 500ms linear;
    }

    &ExitDone {
        transform: rotate(0deg);
    }

    & a {
        border-radius: 15%;
        margin-right: 15px;
        background-color: $color-secondary-light;
        color: $color-primary;
        text-decoration: none;
        width: 100%;
        //box-sizing: border-box;
        display: block;
    }

    & a:hover {
        color: $color-alert;
    }

    & a:active,
    & a.active {
        color: $color-quaduprary;
        background-color: $color-secondary-dark;
    }
}

@media (min-width: $min-width-small-res) {
    .NavigationItem {
        margin: 0;
        left: 0;
        display: flex;
        height: 100%;
        width: auto;
        align-items: center;

        & a {
            color: $color-tertiary;
            height: 100%;
            padding: 10px 10px;
        }

        & a:active,
        & a.active {
            background-color: $color-secondary-dark;
            color: $color-quaduprary;
            border-color: $color-alert;
        }
    }
}

最后的文件是 package.json 和 variables.scss:

$color-primary: #845EC2;

$color-secondary-light: #FF6F91;
$color-secondary-dark: #D65DB1;

$color-tertiary: #009b1a;

$color-quaduprary: #009b1a;

$color-alert: #FFC75F;

//sizes
$menu-button-width: 9.5rem;

//text

$text-large: 1.5rem;
$text-small: 1rem;
$text-supersmall: 0.8rem;

//round pixel

$round-small: 3px;
$round-medium: 10rem;
$round-large: 50%;

//CV header

$photoHeight: 80%;
$small-res-photoHeight: 90%;

//Media Queries

$max-width-intermediate-res: 1050px;
$max-width-medium-res: 730px;

$max-width-small-res: 564px;
$min-width-small-res: 565px;

//csv filters:

$filter-main:  invert(93%) sepia(90%) saturate(2%) hue-rotate(357deg) brightness(108%) contrast(100%);

json:

{
  "name": "photo-gallery",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.16.2",
    "@testing-library/react": "^12.1.4",
    "@testing-library/user-event": "^13.5.0",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-router-dom": "^6.2.2",
    "react-scripts": "5.0.0",
    "react-transition-group": "^4.4.2",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "sass": "^1.49.9"
  }
}

如您所见,我尝试使用“transform-origin: center center 0;”使旋转轴居中(也尝试过“transform-origin:center center”和“transform-origin:center”)但没有成功。 我想我在这里遗漏了一些东西,我们将不胜感激任何帮助和建议!

上面的 js 代码也有两个关于渲染列表中的键和 StrictMode 中已弃用的 findDOMNode 的警告,但这是 material 接下来的 2 个帖子。

提前感谢您的回答!!!

正如第一条评论中提到的,锚点的值被设置为 15px,这使得它旋转出中心。为了让它工作,我要么必须删除 margin-right,要么添加具有相同值的 margin-left。

非常感谢 A Howorth!