I have a Share Icon that looks like:
I want the first 5 icons to be shown. The 5th icon being the share icon.
I want the rest of the icons to be under share icon & when someone taps or hovers over it (either tap or hover as I don’t know what would be best UX in this case), it should open to the right at their usual positions.
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>
);
};
The icons are in a flex
container so I can’t use staggerChildren
. Stagger Children is exactly what I’m looking for but I can’t find a solution to this using Flexbox?
Do I need to change the DOM elements by adding a wrapper? But that would break the ul>li
combo.
All I want is it all the icons open when I hover on share to the right & when I stop hovering, they come back under share icon & disappear. Basically, share icon functionality like https://codepen.io/romswellparian/full/mJXdqV:
It only should be to the right & first 4 icons should be always shown.
I’ve made a complete Stackblitz reproduction → https://stackblitz.com/edit/share-animation-framer-motion?file=components%2FSocialShare.jsx
2
Answers
Stagger Children
Flexbox shouldn’t prevent you from using
staggerChildren
.To use
staggerChildren
you add it to thetransition
property of the parent variants. That means the parent of your list items needs to be a motion component, as do the list items you want to animate.Here’s a quick fork of your project that does what you’re describing:
https://stackblitz.com/edit/share-animation-framer-motion-e5fp5p?file=components/SocialShare.jsx
Animate Presence
If you actually want to remove the items from the DOM when they are hidden, then you need to use AnimatePresence. This requires that you wrap all the items what will enter and exit the DOM in the
<AnimatePresence>
tag. Each item will need aninitial
,animate
andexit
variant that describes the animation, and each item needs a unique key. The last one is easy to forget.Unfortunately, I don’t think StaggerChildren works with AnimatePresence in the way you want it to. To stagger the enter and exit animations you can use Dynamic Variants with a custom prop to define the animation delay for each item.
Short example:
Here’s the StackBlitz with AnimatePresence:
https://stackblitz.com/edit/share-animation-framer-motion-wjdxhc?file=components/SocialShare.jsx
The way your example is set up, removing elements from the
<ul>
container causes it to shift (since the container size changes). I fixed that by setting an explicit width, but you could also change the flex alignment of float it or whatever is going to work with your actual project.Following on from @Cadin answer I’ve found you can use both
staggerChildren
andAnimatePresense
to provide a smoother animation when rendering lists.