如何用Framer Motion stagger效果实现Flex布局下的分享按钮动画?
实现方案
核心思路是直接将原生ul替换为motion.ul作为动画父容器,无需额外嵌套包装元素,完全保留ul > li的原生DOM结构,同时借助Framer Motion的父容器变体特性实现子元素错峰动画。
具体修改点如下:
- 替换外层
ul为motion.ul,定义父级变体,在可见状态中配置staggerChildren参数实现错峰效果 - 将分享图标后所有需要展开的
li统一改为motion.li,共享子级动画变体 - 给分享图标增加悬停事件绑定,同时保留点击切换逻辑,满足点击、悬停两种交互需求
- 优化动画变体属性,隐藏状态下配置宽度、边距归零,避免隐藏元素占用页面空间
修改后的完整代码:
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 parentVariants = { hidden: { transition: { staggerChildren: 0.08, staggerDirection: -1 // 收起时从最后一个元素开始动画 } }, visible: { transition: { staggerChildren: 0.1 // 每个子元素延迟0.1s触发动画 } } }; // 子元素公共动画变体 const itemVariants = { hidden: { opacity: 0, x: -16, width: 0, marginRight: 0, pointerEvents: 'none' }, visible: { opacity: 1, x: 0, width: 'auto', marginRight: '1rem', // 对应原space-x-4的间距 pointerEvents: 'auto', transition: { type: 'tween', ease: 'easeInOut' } } }; return ( <motion.ul className="flex items-center mt-8 relative" variants={parentVariants} initial="hidden" animate={share ? 'visible' : 'hidden'} > <li className="mr-4"> <a href={sharer.facebook} title="Share on Facebook"> <Facebook /> </a> </li> <li className="mr-4"> <a href={sharer.twitter} title="Share on Twitter"> <Twitter /> </a> </li> <li className="mr-4"> <a href={sharer.reddit} title="Share on Reddit"> <Reddit /> </a> </li> <li className="mr-4"> <a href={sharer.hackernews} title="Share on Hacker News"> <HackerNews /> </a> </li> <motion.li className="cursor-pointer mr-4" onMouseEnter={() => openShare(true)} onMouseLeave={() => openShare(false)} > <ShareIcon className="w-6 h-6 text-gray-300" onClick={() => openShare(!share)} /> </motion.li> <motion.li variants={itemVariants}> <a href={sharer.linkedin} title="Share on LinkedIn"> <LinkedIn /> </a> </motion.li> <motion.li variants={itemVariants}> <a href={sharer.pinterest} title="Share on Pinterest"> <Pinterest /> </a> </motion.li> <motion.li variants={itemVariants}> <a href={sharer.telegram} title="Share on Telegram"> <Telegram /> </a> </motion.li> <motion.li variants={itemVariants}> <a href={sharer.whatsapp} title="Share on Whatsapp"> <Whatsapp /> </a> </motion.li> <motion.li variants={itemVariants} className="!mr-0"> <a href={sharer.pocket} title="Share on Pocket"> <Pocket /> </a> </motion.li> </motion.ul> ); };
内容的提问来源于stack exchange,提问作者deadcoder0904
相关产品推荐
相关产品推荐

