Framer Motion - fade out element according to scroll position - reactjs

Using framer motion, I am trying to fade in an object when it enters the viewport. Then I would like it to stay fixed for a while and eventually fade out.
So far I have only managed to fade the element in and fade it out when it leaves the viewport, which is too late. I was not able to figure out how to fix the element at a certain scroll position.
Here is what I have achieved:
This is what I am trying to achieve:
This is the Code Sandbox: https://codesandbox.io/s/vigorous-dawn-q4figi?file=/src/App.js
Here is the code:
export default function App() {
const [ref, inView] = useInView({
threshold: 0.5,
triggerOnce: false
});
const variants = {
visible: { opacity: 1, scale: 1, y: 0 },
hidden: {
opacity: 0,
scale: 0.65,
y: 50
}
};
return (
<div className="App">
<div style={{ height: 500 }} />
<motion.div
animate={inView ? "visible" : "hidden"}
variants={variants}
exit="hidden"
transition={{ duration: 2, ease: "easeOut" }}
className="box"
ref={ref}
/>
</div>
);
}
I would be very thankful for any kind of help!

You're almost there, just a couple of tweaks - mostly CSS for how you should position your animated element.
body {
margin: 0;
}
.App {
height: 300vh;
display: flex;
justify-content: center;
align-items: center;
background-color: #3b6fe0;
}
.box {
width: 200px;
height: 200px;
background-color: #fff;
border-radius: 20px;
}
Here's a full demo: https://codesandbox.io/s/flamboyant-sara-ce4pvx?file=/src/App.js
Note: it's a bit buggy to combine the useInView hook with the scale animation, as scaling the element will affect the in-view threshold. You could also consider use-scroll as an alternative, it may be harder to implement but provide more control over the animation.

Related

Framer Motion Animate Presence vertically inline

I am trying to display a count where when a digit changes the original digit exits downwards and the new digit comes in from the top. When I try to do this, the numbers appear side by side briefly. I have tried using position absolute but then the digits don't exit downwards.
Here is a codesandbox showing the issue I am having.
Thanks!
You need to wrap your motion.divs in a container so the width doesn't change on the animation. Then give the container position relative and a fixed width and every motion.div a position absolute and for each motion.div higher left px.
<div className="myComponent">
<div className="counts-container">
<AnimatePresence>
{counts.map((count, index) => (
<motion.div
key={index + Math.random()}
className={`count count-${index}`}
animate={{ y: 0 }}
initial={{ y: -50 }}
exit={{ y: 50 }}
transition={{ duration: 0.5 }}
>
{count}
</motion.div>
))}
</AnimatePresence>
</div>
<button onClick={() => setCounts((c) => [...c].reverse())}>
Change Counts
</button>
</div>
your css:
body {
margin: 0;
padding: 0;
}
#root {
font-family: sans-serif;
text-align: center;
width: 100vw;
height: 100vh;
display: flex;
place-content: center;
place-items: center;
background: rgba(0, 85, 255, 1);
margin: 0;
padding: 0;
}
.myComponent {
display: flex;
}
.counts-container {
display: flex;
position: relative;
width: 40px;
background-color: yellow;
}
.count {
position: absolute;
}
.count-0 {
left: 0px;
}
.count-1 {
left: 15px;
}
.count-2 {
left: 30px;
}
MIGHT NOT BE THE BEST SOLUTION BUT IT IS A SOLUTION! :D

framer-motion: How do you share different animations between parent and child components?

I was making a navigationbar.
And I would like to add scale effect and underline effect when mouse is placed on the button.
I made a lower component in the component and added animation using framer-motion. However, if i mouse over the underline, it works well, but i mouse over the item component, the underline animation effect does not work.
Attempt to resolve with LayoutGroup failed.
plz help..
const itemBtnVariant = {
normal: {},
action: {
scale: 1.2,
},
underAction: {
opacity: 1,
width: "30px",
},
};
const Item = styled(motion.li)<{ route: boolean }>`
position: relative;
margin-right: 20px;
display: flex;
justify-content: center;
`;
const UnderBar = styled(motion.div)`
position: absolute;
background-color: white;
width: 15px;
height: 1px;
bottom: -5px;
left: 0;
right: 0;
margin: 0 auto;
`;
<Link to={"/"}>
<LayoutGroup>
<Item
layout
variants={itemBtnVariant}
initial="normal"
whileHover="action"
layoutId="1"
route={Boolean(homeRoute)}
>
Home
<UnderBar
layout
variants={itemBtnVariant}
initial="normal"
layoutId="1"
whileHover="underAction"
/>
</Item>
</LayoutGroup>
</Link>
I solved it.
The key was inheritance.
Make itemBtnVariant and underBarVariant the same property.
Because animation is inherited from parent to child.
Therefore, you use variants and animations for the parent component and only change variants for the child component.
const itemBtnVariant = {
normal: {
scale: 1,
},
action: {
scale: 1.2,
},
};
const underBarVariant = {
normal: {
opacity: 1,
scale: 1,
width: "15px",
},
action: {
opacity: 1,
width: "30px",
},
};
<Item
variants={itemBtnVariant}
whileHover="action"
initial="normal"
route={Boolean(homeRoute)}
>
Home
<UnderBar variants={underBarVariant} />
</Item>

How to loop a Draggable slider in React

I have a draggable horizontal slider in my current project and I would like to setting up it also to loop continuously. By loop continuously I mean it should respond to the process of showing images one after another when dragging? Right now, I do have only 3 images in my slider and when I drag slider to the left, slider with its 3rd image and a blank white space starts showing just after. Here at this point I want images to get start again continuously from the very beginning i.e. from the 1st image with aim to cover the white blank space.
Apart, one error I'm getting with my existing code is that when I start to drag slider to right side, suddenly a scroll comes up on browser and keep going in never ending state. By never ending state, I mean it still remain on screen when I drag all my 3 images fully in right direction.
So these are the two things I want to apply and want to resolve in my current project. I'm sharing my code below.
src > Routes > Home > Components > Carousel > Components > SliderDataItems > index.js
import React, { useRef, useEffect } from "react";
import { gsap } from "gsap";
import { Draggable } from "gsap/Draggable";
import { ZoomInOutlined } from '#ant-design/icons'
import { Images } from '../../../../../../Shared/Assets';
import ImagesIcon from '../../../../../../Components/Cells/ImagesIcon'
gsap.registerPlugin(Draggable);
const pictures = [
{
img: Images.xgallery1,
icon: <ZoomInOutlined />
},
{
img: Images.xgallery2,
icon: <ZoomInOutlined />
},
{
img: Images.xgallery4,
icon: <ZoomInOutlined />
},
];
const Slide = ({ img, icon }) => {
return (
<div className="slide">
<div className="image">
<ImagesIcon src={img} />
<div className="icon">
{icon}
</div>
</div>
</div>
);
};
export const Slider = () => {
const sliderRef = useRef(null);
useEffect(() => {
Draggable.create(sliderRef.current, {
type: "x"
});
}, []);
return (
<div className="slider" ref={sliderRef}>
{pictures.map((item, index) => {
return (
<Slide key={index} img={item.img} icon={item.icon} />
);
})}
</div>
);
};
export default Slider;
src > Routes > Home > Components > Carousel > style.scss
.slider {
display: flex;
cursor: unset !important;
overflow: hidden !important;
.slide {
.image {
position: relative;
img {
width: 100% !important;
height: auto !important;
object-fit: cover;
}
.icon {
transition: 0.5s ease;
opacity: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
text-align: center;
span {
svg {
font-size: 30px;
color: #fff;
}
}
}
}
}
.image:hover .icon {
opacity: 1;
}
}
.image:after {
content: "";
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: rgba(211, 208, 208, 0.6);
opacity: 0;
transition: all 0.5s;
-webkit-transition: all 0.5s;
}
.image:hover:after {
opacity: 1;
}
Here's the link of demo (kindly see just above the footer section) for your reference.
Thank you for any help.
For draggle Slider there is a very lightweight JS Carousel package - siema
It is a great, lightweight carousel that is made with JS. There are also other packages built on top of this purely made for React.
In your case, I would offer to try out react-siema.
With it, you can simply use the carousel like that and it will be draggable by default. Plus, no need to load any css.

Position Draft js emoji popover on top

I'm using the #draft-js-plugins/emoji but I'm having problem positioning the emoji popover on top of the editor. Currently it's showing on bottom like this I want to show this popover on top rather than the bottom.
I found an answer here, but it's not working for me.
const {
MentionSuggestions,
EmojiSuggestions,
EmojiSelect,
plugins,
} = useMemo(() => {
const mentionPlugin = createMentionPlugin();
// eslint-disable-next-line no-shadow
const { MentionSuggestions } = mentionPlugin;
const emojiPlugin = createEmojiPlugin({
positionSuggestions: (settings) => {
return {
left: settings.decoratorRect.left + 'px',
top: settings.decoratorRect.top - 40 + 'px', // change this value (40) for manage the distance between cursor and bottom edge of popover
display: 'block',
transform: 'scale(1) translateY(-100%)', // transition popover on the value of its height
transformOrigin: '1em 0% 0px',
transition: 'all 0.25s cubic-bezier(0.3, 1.2, 0.2, 1)',
position: 'fixed',
};
},
useNativeArt: true,
});
// eslint-disable-next-line no-shadow
const { EmojiSuggestions, EmojiSelect } = emojiPlugin;
// eslint-disable-next-line no-shadow
const plugins = [mentionPlugin, emojiPlugin];
return {
plugins,
MentionSuggestions,
EmojiSuggestions,
EmojiSelect,
};
}, []);
<div className="flex w-100 items-center">
<div
className={`${editorStyles.editor} flex w-100 pa2`}
onClick={() => {
ref.current!.focus();
}}
>
<Editor
editorKey={'editor'}
editorState={editorState}
handleKeyCommand={(cmd) => handleKeyCommand(cmd)}
keyBindingFn={myKeyBindingFn}
onChange={onChange}
plugins={plugins}
ref={ref}
placeholder="Type your message and hit ENTER to send"
/>
<MentionSuggestions
open={open}
onOpenChange={onOpenChange}
suggestions={suggestions}
onSearchChange={onSearchChange}
onAddMention={() => {
// get the mention object selected
}}
/>
<EmojiSuggestions />
</div>
<div style={{ margin: '0 .5rem' }}>
<EmojiSelect closeOnEmojiSelect />
</div>
<SendIcon
className="pointer"
style={{ color: '#3F61C5', margin: '0 .5rem' }}
onClick={handleSend}
/>
</div>
I also inspected the elements, the css I wrote in
positionSuggestions are not there. Any way I can place this popover on top?
I had the same issue and I have resolved it using the CSS option.
Create a copy of this file in your project: https://github.com/draft-js-plugins/draft-js-plugins/blob/master/packages/emoji/src/theme.ts
Find emojiSelectPopover class and replace it with below:
emojiSelectPopover: css`
padding: 0 0.3em;
position: absolute;
z-index: 1000;
box-sizing: content-box;
background: #fff;
border: 1px solid #e0e0e0;
box-shadow: 0 4px 30px 0 gainsboro;
transform: scale(1) translateY(-100%);
transform-origin: 1em 0% 0px;
top: 65%;
#media (min-width: 320px) and (max-width: 480px) {
right: 0;
top: 50%;
}
Use your custom theme file(which you have created in #1) in the configuration:
const emojiPlugin = createEmojiPlugin({
selectButtonContent: '😊',
theme: defaultTheme // This should be imported from your created theme file
});
For your scenario, you need to move right: 0; outside of the media query as your emoji button is close to the right side of the browser.

How to show Vertical Progress Bar in react-transition-group with animation

Here is a jQuery example for a progress bar animation. and I want this feature in Reactjs without jQuery. How to implement this feature.
I hope you are still interested in this question. I just tinker with react-spring and I really love it. The best animation library in React IMHO.
You can create a neat component with the Spring component. It will always animate to the to property of the Spring component. First time from the from value of the from property.
import React from "react";
import { Spring } from "react-spring";
const VerticalProgress = ({ progress }) => {
return (
<Spring from={{ percent: 0 }} to={{ percent: progress }}>
{({ percent }) => (
<div className="progress vertical">
<div style={{ height: `${percent}%` }} className="progress-bar">
<span className="sr-only">{`${progress}%`}</span>
</div>
</div>
)}
</Spring>
);
};
export default VerticalProgress;
Here is the complete code: https://codesandbox.io/s/mqo1r9wo4j
Horizontal Example
Here is how to do it.
make 2 divs(container, progressing one)
you can change the height of progressing div based on state change.
const styled = styled.default;
const Bar = styled.div`
position: relative;
height: 500px;
width: 100%;
border-radius: 3px;
border: 1px solid #ccc;
margin: 1rem auto;
`
const Fill = styled.div`
background: #0095da;
width: 100%;
border-radius: inherit;
transition: height 0.2s ease-in;
height: ${(props) => `${props.percentual}%`};
`
const ProgressBar = ({ percentage }) => {
return (
<div>
<Bar>
<Fill percentage={percentage} />
</Bar>
</div>
);
}
ReactDOM.render(<ProgressBar percentage={you state for progress percentage} />, document.getElementById('bar'));
you don't even need react for that tho.
https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_progressbar_label_js

Resources