简体   繁体   中英

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

I have problem with setting rotation axis in a animation of menu buttons in react webpage. Im using react 17.0.2 and react-transition-group 4.4.2.

The concept of my menu is to rotate the button after it has been clicked. If other button was clicked before, it shoud rotate back to its original position, and the new button should rotate - to show it's active. I have the problem with making animation axis of rotation perfectlly centered:

[Menu buttons before rotation] [1]: https://i.stack.imgur.com/7nVnv.jpg

[After rotation menu button is moved to the right] [2]: https://i.stack.imgur.com/yB28J.jpg

As you can see in the image [2] middle menu button moved to the right and is not aligned with the rest of the buttons, I want it to be perfectlly aligned with all the buttons.

My NavigationItems.js describes the buttons:

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;

The coresponding scss classes (NavigationItems.module.scss) of NavigationItems.js look like this:

    @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;
        }
    }

Note that when I switch styles to flexbox the rotation axis is also not centered properly. The NavigationItem.js I am animating looks like this (I am using react-router-dom 6 but it can be also used with 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;

The coresponding classes (NavigationItem.module.scss) look like this:

@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;
        }
    }
}

The last files are package.json and 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"
  }
}

As you can see I tried to center rotation axis with "transform-origin: center center 0;" (also tried "transform-origin: center center" and "transform-origin: center) but with no luck. I think I'm missing something here, any help, suggestions would be appreciated!

I also have two warnings with js code above regarding keys in rendering list and deprecated findDOMNode in StrictMode but that is the material for 2 next posts.

Thanks in advance for your answers!!!

As it was mentioned in first comment, the value of an anchor was set to 15px which made it rotate out of center. In order to make it work I either have to remove margin-right, or add margin-left with the same value.

Thanks a lot A Howorth!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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