在 Framer Motion 中错开兄弟姐妹来执行共享按钮动画?

Stagger Siblings in Framer Motion to perform Share Buttons Animation?

我有一个如下所示的共享图标:

我想要显示前 5 个图标。第 5 个图标是分享图标。

我希望其余图标位于共享图标下方,当有人点击或悬停它时(点击或悬停,因为我不知道在这种情况下什么是最好的用户体验),它应该打开右边在他们通常的位置。

SocialShare.jsx

import React from 'react';
import { motion } from 'framer-motion';
import { ShareIcon } from '@heroicons/react/solid';

import {
  Facebook,
  HackerNews,
  Reddit,
  Twitter,
  LinkedIn,
  Pinterest,
  Telegram,
  Whatsapp,
  Pocket
} from '../icons/index';

const isProduction = process.env.NODE_ENV === 'production';

export const SocialShare = ({ title, slug }) => {
  const [share, openShare] = React.useState(false);
  const [host, setHost] = React.useState('');

  React.useEffect(() => {
    setHost(window.location.host);
  }, []);

  const url = `${isProduction ? 'https://' : 'http://'}${host}/${slug}`;
  const text = title + url;
  const via = 'deadcoder0904';
  const sharer = {
    facebook: `https://www.facebook.com/sharer/sharer.php?u=${url}`,
    twitter: `https://twitter.com/intent/tweet?url=${url}&text=${text}&via=${via}`,
    reddit: `https://www.reddit.com/submit?title=${title}&url=${url}`,
    hackernews: `https://news.ycombinator.com/submitlink?u=${url}&t=${title}`,
    linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${url}`,
    pinterest: `https://pinterest.com/pin/create/button/?url=${url}&description=${title}`,
    telegram: `https://telegram.me/share/url?url=${url}&text=${text}`,
    whatsapp: `https://wa.me/?text=${title}%0D%0A${url}%0D%0A%0D%0A${text}`,
    pocket: `https://getpocket.com/edit.php?url=${url}`
  };

  const variants = {
    hidden: {
      opacity: 0,
      translateX: -16
    },
    visible: {
      opacity: 1,
      translateX: 0
    }
  };

  return (
    <ul className="flex items-center mt-8 space-x-4">
      <li>
        <a className="" href={sharer.facebook} title="Share on Facebook">
          <Facebook />
        </a>
      </li>
      <li>
        <a className="" href={sharer.twitter} title="Share on Twitter">
          <Twitter />
        </a>
      </li>
      <li>
        <a className="" href={sharer.reddit} title="Share on Reddit">
          <Reddit />
        </a>
      </li>
      <li>
        <a className="" href={sharer.hackernews} title="Share on Hacker News">
          <HackerNews />
        </a>
      </li>
      <motion.li className="cursor-pointer" whileHover={{}}>
        <ShareIcon
          className="w-6 h-6 text-gray-300"
          onClick={() => {
            openShare(!share);
          }}
        />
      </motion.li>

      <motion.li
        className=""
        initial="hidden"
        animate="visible"
        variants={variants}
        transition={{
          type: 'tween',
          ease: 'easeInOut'
        }}
      >
        <a className="" href={sharer.linkedin} title="Share on LinkedIn">
          <LinkedIn />
        </a>
      </motion.li>
      <li>
        <a className="" href={sharer.pinterest} title="Share on Pinterest">
          <Pinterest />
        </a>
      </li>
      <li>
        <a className="" href={sharer.telegram} title="Share on Telegram">
          <Telegram />
        </a>
      </li>
      <li>
        <a className="" href={sharer.whatsapp} title="Share on Whatsapp">
          <Whatsapp />
        </a>
      </li>
      <li>
        <a className="" href={sharer.pocket} title="Share on Pocket">
          <Pocket />
        </a>
      </li>
    </ul>
  );
};

图标在 flex 容器中,所以我无法使用 staggerChildren。 Stagger Children 正是我要找的东西,但我找不到使用 Flexbox 的解决方案?

我是否需要通过添加包装器来更改 DOM 元素?但这会破坏 ul>li 组合。

我想要的只是当我将鼠标悬停在右侧的共享上时打开所有图标,当我停止悬停时,它们会返回到共享图标下并消失。基本上,共享图标功能如 https://codepen.io/romswellparian/full/mJXdqV:

它应该只在右边并且应该始终显示前 4 个图标。

我制作了完整的 Stackblitz 复制品 → https://stackblitz.com/edit/share-animation-framer-motion?file=components%2FSocialShare.jsx

交错Children

Flexbox 不应阻止您使用 staggerChildren.

要使用 staggerChildren,请将其添加到 parent 变体的 transition 属性 中。这意味着您的列表项的 parent 需要是一个运动组件,您想要设置动画的列表项也是如此。

const listVariants = {
  hidden: {
    transition: {
      staggerChildren: 0.1,
      staggerDirection: -1
    }
  },
  visible: {
    transition: {
      staggerChildren: 0.1
    }
  }
};

const itemVariants = {
  hidden: {
    opacity: 0,
    x: -16
  },
  visible: {
    opacity: 1,
    x: 0
  }
};

return (
    <motion.ul
      variants={listVariants}
      initial="hidden"
      animate={share ? 'visible' : 'hidden'}
    >
      <motion.li variants={itemVariants}>
        <a href={sharer.linkedin} title="Share on LinkedIn">
          <LinkedIn />
        </a>
      </motion.li>
      {/* ...etc */}
    </motion.ul>
);

这是您的项目的快速分支,可以执行您所描述的内容:
https://stackblitz.com/edit/share-animation-framer-motion-e5fp5p?file=components/SocialShare.jsx

动画存在

如果你真的想从 DOM 中删除隐藏的项目,那么你需要使用 AnimatePresence。这要求您将所有将进入和退出 DOM 的项目包装在 <AnimatePresence> 标记中。每个项目都需要一个描述动画的 initialanimateexit 变体,并且 每个项目需要一个唯一的键 。最后一个容易忘记。

不幸的是,我认为 StaggerChildren 无法按照您希望的方式与 AnimatePresence 一起使用。要错开进入和退出动画,您可以使用 Dynamic Variants 和自定义道具来定义每个项目的动画延迟。

简短示例:

const itemVariants = {
  hidden: i => ({
    opacity: 0,
    x: -16,
    transition: {
      delay: i * 0.1
    }
  }),
  visible: i => ({
    opacity: 1,
    x: 0,
    transition: {
      delay: i * 0.1 // custom prop used to stagger delay
    }
  })
};

return (
  <ul className="flex items-center mt-8 space-x-4">
    <AnimatePresence>
      {share && (<>
        <motion.li
          variants={itemVariants}
          key="linkedin"    /* don't forget key! */
          initial="hidden"
          animate="visible"
          exit="hidden"
          custom={1}  /* custom prop used to stagger delay */
        >
          <a href={sharer.linkedin} title="Share on LinkedIn">
            <LinkedIn />
          </a>
        </motion.li>

        <motion.li
          variants={itemVariants}
          key="pinterest"
          initial="hidden"
          animate="visible"
          exit="hidden"
          custom={2}
        >
          <a href={sharer.pinterest} title="Share on Pinterest">
            <Pinterest />
          </a>
        </motion.li>
        {/* etc... */}
      </>)}
    </AnimatePresence>
  </ul>
)

这是带有 AnimatePresence 的 StackBlitz:
https://stackblitz.com/edit/share-animation-framer-motion-wjdxhc?file=components/SocialShare.jsx

您的示例的设置方式,从 <ul> 容器中删除元素会导致它移动(因为容器大小发生变化)。我通过设置一个明确的宽度来解决这个问题,但是你也可以改变浮动它的弹性对齐方式或者任何与你的实际项目一起工作的东西。