英文:
Mui's Transition animation problem on item's position change within a list
问题
根据Mui的TransitionGroup示例,我尝试实现在单击时更改项目位置的过渡。从功能上来说,它确实可以工作,因为所选元素被移动到列表的顶部,但遗憾的是没有过渡效果。我做错了什么,以及实现在列表中更改项目位置时实现动画效果的最简单方法是什么?
import * as React from "react";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Collapse from "@mui/material/Collapse";
import IconButton from "@mui/material/IconButton";
import List from "@mui/material/List";
import ListItem from "@mui/material/ListItem";
import ListItemText from "@mui/material/ListItemText";
import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward";
import { TransitionGroup } from "react-transition-group";
import Container from "@mui/material/Container";
const FRUITS = [
"Apple",
"Banana",
"Pineapple",
"Coconut",
"Watermelon",
"Grapes",
"Orange",
];
interface RenderItemOptions {
item: string;
handleRemoveFruit: (item: string) => void;
}
function renderItem({ item, handleRemoveFruit }: RenderItemOptions) {
return (
<ListItem
secondaryAction={
<IconButton onClick={() => handleRemoveFruit(item)}>
<ArrowUpwardIcon />
</IconButton>
}
>
<ListItemText primary={item} />
</ListItem>
);
}
export default function TransitionGroupExample() {
const [fruitsInBasket, setFruitsInBasket] = React.useState(
FRUITS.slice(0, 3)
);
const handleAddFruit = () => {
const nextHiddenItem = FRUITS.find((i) => !fruitsInBasket.includes(i));
if (nextHiddenItem) {
setFruitsInBasket((prev) => [nextHiddenItem, ...prev]);
}
};
const handleRemoveFruit = (item: string) => {
setFruitsInBasket((prev) => {
return [prev.find((i) => i === item)!, ...prev.filter((i) => i !== item)];
});
};
const addFruitButton = (
<Button
variant="contained"
disabled={fruitsInBasket.length >= FRUITS.length}
onClick={handleAddFruit}
>
Add fruit to basket
</Button>
);
return (
<Container maxWidth="xs">
{addFruitButton}
<Box sx={{ mt: 1 }}>
<List>
<TransitionGroup>
{fruitsInBasket.map((item) => (
<Collapse key={item}>
{renderItem({ item, handleRemoveFruit })}
</Collapse>
))}
</TransitionGroup>
</List>
</Box>
</Container>
);
}
英文:
Based on a Mui's TransitionGroup example I've tried to implement the transition of an item's position upon clicking. Functionally it does work, as the selected element is being moved to the top of the list, but unfortunately without the transition effect. What am I doing wrong and what would be the simplest approach of achieving animated movement upon item's position change in the list?
import * as React from "react";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Collapse from "@mui/material/Collapse";
import IconButton from "@mui/material/IconButton";
import List from "@mui/material/List";
import ListItem from "@mui/material/ListItem";
import ListItemText from "@mui/material/ListItemText";
import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward";
import { TransitionGroup } from "react-transition-group";
import Container from "@mui/material/Container";
const FRUITS = [
"Apple",
"Banana",
"Pineapple",
"Coconut",
"Watermelon",
"Grapes",
"Orange",
];
interface RenderItemOptions {
item: string;
handleRemoveFruit: (item: string) => void;
}
function renderItem({ item, handleRemoveFruit }: RenderItemOptions) {
return (
<ListItem
secondaryAction={
<IconButton onClick={() => handleRemoveFruit(item)}>
<ArrowUpwardIcon />
</IconButton>
}
>
<ListItemText primary={item} />
</ListItem>
);
}
export default function TransitionGroupExample() {
const [fruitsInBasket, setFruitsInBasket] = React.useState(
FRUITS.slice(0, 3)
);
const handleAddFruit = () => {
const nextHiddenItem = FRUITS.find((i) => !fruitsInBasket.includes(i));
if (nextHiddenItem) {
setFruitsInBasket((prev) => [nextHiddenItem, ...prev]);
}
};
const handleRemoveFruit = (item: string) => {
setFruitsInBasket((prev) => {
return [prev.find((i) => i === item)!, ...prev.filter((i) => i !== item)];
});
};
const addFruitButton = (
<Button
variant="contained"
disabled={fruitsInBasket.length >= FRUITS.length}
onClick={handleAddFruit}
>
Add fruit to basket
</Button>
);
return (
<Container maxWidth="xs">
{addFruitButton}
<Box sx={{ mt: 1 }}>
<List>
<TransitionGroup>
{fruitsInBasket.map((item) => (
<Collapse key={item}>
{renderItem({ item, handleRemoveFruit })}
</Collapse>
))}
</TransitionGroup>
</List>
</Box>
</Container>
);
}
答案1
得分: 0
我成功找到了解决这个问题的方法,因为添加和删除列表元素的操作都运行得很完美。在下面的实现中,单击向上箭头后,元素会被成功移除,就像在 Mui 的示例中一样。然后,使用回调函数 Collapse.onExited,元素会简单地重新插入到列表中,从而执行正确的动画过渡。
```jsx
import * as React from "react";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Collapse from "@mui/material/Collapse";
import IconButton from "@mui/material/IconButton";
import List from "@mui/material/List";
import ListItem from "@mui/material/ListItem";
import ListItemText from "@mui/material/ListItemText";
import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward";
import { TransitionGroup } from "react-transition-group";
import Container from "@mui/material/Container";
const FRUITS = [
"Apple",
"Banana",
"Pineapple",
"Coconut",
"Watermelon",
"Grapes",
"Orange",
];
interface RenderItemOptions {
item: string;
handleRemoveFruit: (item: string) => void;
}
function renderItem({ item, handleRemoveFruit }: RenderItemOptions) {
return (
<ListItem
secondaryAction={
<IconButton onClick={() => handleRemoveFruit(item)}>
<ArrowUpwardIcon />
</IconButton>
}
>
<ListItemText primary={item} />
</ListItem>
);
}
export default function TransitionGroupExample() {
const [fruitsInBasket, setFruitsInBasket] = React.useState(
FRUITS.slice(0, 3)
);
const handleAddFruit = () => {
const nextHiddenItem = FRUITS.find((i) => !fruitsInBasket.includes(i));
if (nextHiddenItem) {
setFruitsInBasket((prev) => [nextHiddenItem, ...prev]);
}
};
const handleRemoveFruit = (item: string) => {
setFruitsInBasket((prev) => {
return [...prev.filter((i) => i !== item)];
});
};
const addFruitButton = (
<Button
variant="contained"
disabled={fruitsInBasket.length >= FRUITS.length}
onClick={handleAddFruit}
>
Add fruit to basket
</Button>
);
return (
<Container maxWidth="xs">
{addFruitButton}
<Box sx={{ mt: 1 }}>
<List>
<TransitionGroup>
{fruitsInBasket.map((item) => (
<Collapse
key={item}
onExited={() => setFruitsInBasket((prev) => [item, ...prev])}
>
{renderItem({ item, handleRemoveFruit })}
</Collapse>
))}
</TransitionGroup>
</List>
</Box>
</Container>
);
}
英文:
I was able to figure out the workaround to this problem, as the addition and removing the list's elements worked perfectly fine. In the following implementation upon clicking an up arrow the element is removed just as well as in the Mui's example. Later on, using the callback Collapse.onExited the element is simply inserted back to the list again upon which the correct animation transition is performed.
import * as React from "react";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Collapse from "@mui/material/Collapse";
import IconButton from "@mui/material/IconButton";
import List from "@mui/material/List";
import ListItem from "@mui/material/ListItem";
import ListItemText from "@mui/material/ListItemText";
import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward";
import { TransitionGroup } from "react-transition-group";
import Container from "@mui/material/Container";
const FRUITS = [
"Apple",
"Banana",
"Pineapple",
"Coconut",
"Watermelon",
"Grapes",
"Orange",
];
interface RenderItemOptions {
item: string;
handleRemoveFruit: (item: string) => void;
}
function renderItem({ item, handleRemoveFruit }: RenderItemOptions) {
return (
<ListItem
secondaryAction={
<IconButton onClick={() => handleRemoveFruit(item)}>
<ArrowUpwardIcon />
</IconButton>
}
>
<ListItemText primary={item} />
</ListItem>
);
}
export default function TransitionGroupExample() {
const [fruitsInBasket, setFruitsInBasket] = React.useState(
FRUITS.slice(0, 3)
);
const handleAddFruit = () => {
const nextHiddenItem = FRUITS.find((i) => !fruitsInBasket.includes(i));
if (nextHiddenItem) {
setFruitsInBasket((prev) => [nextHiddenItem, ...prev]);
}
};
const handleRemoveFruit = (item: string) => {
setFruitsInBasket((prev) => {
return [...prev.filter((i) => i !== item)];
});
};
const addFruitButton = (
<Button
variant="contained"
disabled={fruitsInBasket.length >= FRUITS.length}
onClick={handleAddFruit}
>
Add fruit to basket
</Button>
);
return (
<Container maxWidth="xs">
{addFruitButton}
<Box sx={{ mt: 1 }}>
<List>
<TransitionGroup>
{fruitsInBasket.map((item) => (
<Collapse
key={item}
onExited={() => setFruitsInBasket((prev) => [item, ...prev])}
>
{renderItem({ item, handleRemoveFruit })}
</Collapse>
))}
</TransitionGroup>
</List>
</Box>
</Container>
);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论