如何在反应过渡组动画中正确居中旋转轴
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!
我在 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!