Mui的过渡动画问题:在列表中项目位置发生更改时。

huangapple go评论65阅读模式
英文:

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 &quot;react&quot;;
import Box from &quot;@mui/material/Box&quot;;
import Button from &quot;@mui/material/Button&quot;;
import Collapse from &quot;@mui/material/Collapse&quot;;
import IconButton from &quot;@mui/material/IconButton&quot;;
import List from &quot;@mui/material/List&quot;;
import ListItem from &quot;@mui/material/ListItem&quot;;
import ListItemText from &quot;@mui/material/ListItemText&quot;;
import ArrowUpwardIcon from &quot;@mui/icons-material/ArrowUpward&quot;;
import { TransitionGroup } from &quot;react-transition-group&quot;;
import Container from &quot;@mui/material/Container&quot;;
const FRUITS = [
&quot;Apple&quot;,
&quot;Banana&quot;,
&quot;Pineapple&quot;,
&quot;Coconut&quot;,
&quot;Watermelon&quot;,
&quot;Grapes&quot;,
&quot;Orange&quot;,
];
interface RenderItemOptions {
item: string;
handleRemoveFruit: (item: string) =&gt; void;
}
function renderItem({ item, handleRemoveFruit }: RenderItemOptions) {
return (
&lt;ListItem
secondaryAction={
&lt;IconButton onClick={() =&gt; handleRemoveFruit(item)}&gt;
&lt;ArrowUpwardIcon /&gt;
&lt;/IconButton&gt;
}
&gt;
&lt;ListItemText primary={item} /&gt;
&lt;/ListItem&gt;
);
}
export default function TransitionGroupExample() {
const [fruitsInBasket, setFruitsInBasket] = React.useState(
FRUITS.slice(0, 3)
);
const handleAddFruit = () =&gt; {
const nextHiddenItem = FRUITS.find((i) =&gt; !fruitsInBasket.includes(i));
if (nextHiddenItem) {
setFruitsInBasket((prev) =&gt; [nextHiddenItem, ...prev]);
}
};
const handleRemoveFruit = (item: string) =&gt; {
setFruitsInBasket((prev) =&gt; {
return [prev.find((i) =&gt; i === item)!, ...prev.filter((i) =&gt; i !== item)];
});
};
const addFruitButton = (
&lt;Button
variant=&quot;contained&quot;
disabled={fruitsInBasket.length &gt;= FRUITS.length}
onClick={handleAddFruit}
&gt;
Add fruit to basket
&lt;/Button&gt;
);
return (
&lt;Container maxWidth=&quot;xs&quot;&gt;
{addFruitButton}
&lt;Box sx={{ mt: 1 }}&gt;
&lt;List&gt;
&lt;TransitionGroup&gt;
{fruitsInBasket.map((item) =&gt; (
&lt;Collapse key={item}&gt;
{renderItem({ item, handleRemoveFruit })}
&lt;/Collapse&gt;
))}
&lt;/TransitionGroup&gt;
&lt;/List&gt;
&lt;/Box&gt;
&lt;/Container&gt;
);
}

答案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 &quot;react&quot;;
import Box from &quot;@mui/material/Box&quot;;
import Button from &quot;@mui/material/Button&quot;;
import Collapse from &quot;@mui/material/Collapse&quot;;
import IconButton from &quot;@mui/material/IconButton&quot;;
import List from &quot;@mui/material/List&quot;;
import ListItem from &quot;@mui/material/ListItem&quot;;
import ListItemText from &quot;@mui/material/ListItemText&quot;;
import ArrowUpwardIcon from &quot;@mui/icons-material/ArrowUpward&quot;;
import { TransitionGroup } from &quot;react-transition-group&quot;;
import Container from &quot;@mui/material/Container&quot;;
const FRUITS = [
&quot;Apple&quot;,
&quot;Banana&quot;,
&quot;Pineapple&quot;,
&quot;Coconut&quot;,
&quot;Watermelon&quot;,
&quot;Grapes&quot;,
&quot;Orange&quot;,
];
interface RenderItemOptions {
item: string;
handleRemoveFruit: (item: string) =&gt; void;
}
function renderItem({ item, handleRemoveFruit }: RenderItemOptions) {
return (
&lt;ListItem
secondaryAction={
&lt;IconButton onClick={() =&gt; handleRemoveFruit(item)}&gt;
&lt;ArrowUpwardIcon /&gt;
&lt;/IconButton&gt;
}
&gt;
&lt;ListItemText primary={item} /&gt;
&lt;/ListItem&gt;
);
}
export default function TransitionGroupExample() {
const [fruitsInBasket, setFruitsInBasket] = React.useState(
FRUITS.slice(0, 3)
);
const handleAddFruit = () =&gt; {
const nextHiddenItem = FRUITS.find((i) =&gt; !fruitsInBasket.includes(i));
if (nextHiddenItem) {
setFruitsInBasket((prev) =&gt; [nextHiddenItem, ...prev]);
}
};
const handleRemoveFruit = (item: string) =&gt; {
setFruitsInBasket((prev) =&gt; {
return [...prev.filter((i) =&gt; i !== item)];
});
};
const addFruitButton = (
&lt;Button
variant=&quot;contained&quot;
disabled={fruitsInBasket.length &gt;= FRUITS.length}
onClick={handleAddFruit}
&gt;
Add fruit to basket
&lt;/Button&gt;
);
return (
&lt;Container maxWidth=&quot;xs&quot;&gt;
{addFruitButton}
&lt;Box sx={{ mt: 1 }}&gt;
&lt;List&gt;
&lt;TransitionGroup&gt;
{fruitsInBasket.map((item) =&gt; (
&lt;Collapse
key={item}
onExited={() =&gt; setFruitsInBasket((prev) =&gt; [item, ...prev])}
&gt;
{renderItem({ item, handleRemoveFruit })}
&lt;/Collapse&gt;
))}
&lt;/TransitionGroup&gt;
&lt;/List&gt;
&lt;/Box&gt;
&lt;/Container&gt;
);
}

huangapple
  • 本文由 发表于 2023年2月27日 16:07:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/75578042.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定