I want my component to have an animation that if my mouse enter,it will be fully displayed in 0.3s,and if my mouse leave,it will disappear in 0.1s.But useSpring can just define one duration just like the code below,which cause that the component will be displayed and disappear all in 0.3s.How can I define different duration for from->to and to->from?Thanks for anyone who can help.
const animationStyle = useSpring({
bottom: show ? 0 : -71,
from: {
bottom: -71
},
config: { duration: 300 }
})
Like this
import import React, { useState } from 'react';
import { animated, useSpring } from '#react-spring/web';
const SuspendedComponent: React.FC = ({ children }) => {
const [isMouseEnter, setMouseEnter] = useState<boolean>(false);
const _style = useSpring({
bottom: isMouseEnter ? 0 : -71,
from: {
bottom: -71,
},
config: { duration: 300 },
});
function onMouseHandler(isEnter: boolean) {
setMouseEnter(isEnter);
}
return (
<div
style={{ display: 'block', width: '100px', height: '100px' }}
onMouseEnter={() => onMouseHandler(true)}
onMouseLeave={() => onMouseHandler(false)}
>
{isMouseEnter && <animated.div style={_style}>{children}</animated.div>}
</div>
);
};
export default SuspendedComponent;
You can control animated.div element display by onMouseEnter and onMouseLeave event.
Related
So I'm using react-visibility-sensor with react-spring to animate text sliding char by char from any side I want.
In my home page the animation is running smoothly, I use it twice one from the right side and another from the top side.
When I switch routes and go to another page the animation does not work.
I have my code divided in an "Title" component "Char" component and a custom hook "useAnimatedText".
Title component:
import React from "react";
import VisibilitySensor from "react-visibility-sensor";
import useAnimatedText from "../../hooks/useAnimatedText";
import Char from './Char'
const Title = ({title, side}) => {
// HERE I CALL A CUSTOM HOOK THAT WILL DIFINE IF THE ELEMENT IS VISIBLE OR NOT
// AND HANDLE THE ANIMATION WHEN NECESSARY
const [isVisible, onChange, objArray] = useAnimatedText(title)
let elements = objArray.map((item, i) => {
return(
<Char
key={i}
isVisible={isVisible}
item={item}
delay={400 + (i * 40)}
side={side}
/>
)
})
console.log(isVisible)
return(
<VisibilitySensor onChange={onChange} >
<span className="title-box">
<h1 className="my-heading divided-heading">
{elements}
</h1>
<hr className="title-ruller"></hr>
</span>
</VisibilitySensor>
)
}
export default Title
Char component:
import { useSpring, animated } from "react-spring"
const Char = (props) => {
const { isVisible, item, delay, isBouncy, side} = props
const [ref, addBounce] = useBounce()
let springConfig = {}
if (side === 'right') {
springConfig = {
to: {
opacity: isVisible ? 1 : 0,
translateX : isVisible ? '0px' : '1000px'
},
config: { mass:2, tension: 200, friction: 30},
delay: delay
}
}
else if (side === 'top') {
springConfig = {
to: {
opacity: isVisible ? 1 : 0,
translateY: isVisible ? '0px' : '-500px'
},
config:{ mass:2, tension: 250, friction: 35},
delay: delay
}
}
const spring = useSpring({...springConfig})
return(
<animated.span
style={ spring }
className={isVisible ? 'is-visible' : 'is-not-visible'}
>
{item.char === ' ' ? <span> </span> : item.char}
</animated.span>
)
}
export default Char
This is the custom Hook:
import { useState } from "react";
import { stringToArray } from '../helpers'
// HOOK THAT HANDLES THE TEXT ANIMATION BY SETTING A STATE OF VISIBILITY
function useAnimatedText(string) {
const [isVisible, setVisibility] = useState(false);
const onChange = visiblity => {
visiblity && setVisibility(visiblity);
};
let objArray = stringToArray(string)
return [isVisible, onChange, objArray]
}
export default useAnimatedText
I did a console.log(isVisible) and the value was true but it was rendering in the page the spring values as if it was false(not visible).
I really can´t understand where I'm going wrong here, the only problem I have is when I'm not at my main route, could it be because of react-router-dom?
If someone has any clue, let me know.
In my react app, there is a refresh icon. I am using material UI for the icon. I want that after clicking on it, it should spin for a second.
I have no idea how to do it.
I have tried this but it didn't work.
const useStyles = makeStyles((theme) => ({
refresh: {
marginTop: "20px",
cursor: "pointer",
margin: "auto",
animation: "spin 1s 1",
},
"#keyframes spin": {
"0%": {
transform: "translateY(-200%)",
},
"100%": {
transform: "translateY(0)",
},
},
}));
function Refresh() {
const [spin, setSpin] = React.useState(0);
const classes= useStyles();
const refreshCanvas = () => {
setSpin(1);
console.log("Refreshed");
};
return (
<AutorenewIcon
className={classes.refresh}
onClick={refreshCanvas}
onAnimationEnd={() => setSpin(0)}
spin={spin}
/>
)
}
Seprate the class of animaion let's assume it is refresh as of now and stored in classes.refresh.
You can do it by conditionally applying className to your element
function Refresh() {
const [spin, setSpin] = React.useState(0);
const classes= useStyles();
const refreshCanvas = () => {
setSpin(1);
console.log("Refreshed");
};
return (
<AutorenewIcon
className={spin === 1 ? classes.refresh : ''}
onClick={refreshCanvas}
onAnimationEnd={() => setSpin(0)}
spin={spin}
/>
)
}
set the spin true on click and then add a setTimeout for 1000ms, which will set that spin state again false. And then add a class conditionally based on the value of the spin state and add the animation to that class.
const useStyles = makeStyles((theme) => ({
refresh: {
marginTop: "20px",
cursor: "pointer",
margin: "auto",
"&.spin": {
animation: "$spin 1s 1",
// pointerEvents:'none'
}
},
"#keyframes spin": {
"0%": {
transform: "rotate(0deg)"
},
"100%": {
transform: "rotate(360deg)"
}
}
}));
function Refresh() {
const [spin, setSpin] = React.useState(false);
const classes = useStyles();
const refreshCanvas = () => {
setSpin(true);
setTimeout(() => {
setSpin(false);
}, 1000);
};
return (
<Autorenew
className={clsx({
[classes.refresh]: true,
spin: spin
})}
onClick={refreshCanvas}
spin={360}
/>
);
}
Optional Update: Also you can add pointerEvents:"none" in spin class to disable the click for that period of time until the animation is going which is 1000ms here.
Here is the working demo:
Got the answer... Thanx for both the answers I got here... I got the idea from those answers.
const useStyles = makeStyles((theme) => ({
refresh: {
margin: "auto",
},
spin: {
margin: "auto",
animation: "$spin 1s 1",
},
"#keyframes spin": {
"0%": {
transform: "rotate(0deg)",
},
"100%": {
transform: "rotate(360deg)",
},
}
}))
function Refresh() {
const [spin, setSpin] = React.useState(0);
const classes= useStyles();
const refreshCanvas = () => {
setSpin(true);
setTimeout(() => {
setSpin(false);
}, 1000);
console.log("Refreshed");
};
return (
<AutorenewIcon
className={spin ? classes.spin : classes.refresh}
onClick={refreshCanvas}
spin={spin}
/>
)
}
Can't resize React-Player Video.
I change the width to any number and the video stays unchanged.
I am trying to optimize it for computer view and then add breakpoints to resize for smaller screens like phones.
Bellow is the file where I render the React-Player video inside a on which I apply the desired height and width I would like my react-player video to adopt.
import React, { useContext, useEffect, useState } from 'react'
import { makeStyles } from '#material-ui/core/styles'
import Modal from '#material-ui/core/Modal'
import { MovieContext } from './MovieContext'
import ReactPlayer from 'react-player'
import { getTrailer } from '../utils/movieDB'
// potential of adding controls
// import { Slider, Direction } from 'react-player-controls'
//slider to be implemented
//https://www.npmjs.com/package/react-player-controls#playericon-
const useStyles = makeStyles((theme) => ({
video: {
width: 'auto',
height: 'auto',
top: '25%',
right: '25%',
position: 'fixed',
[theme.breakpoints.down('xs')]: {},
},
}))
const styles = {
player: {
width: '300px',
},
}
export default function SimpleModal({ open }) {
const classes = useStyles(),
//receives movie from Home > DisplayCard > MovieContext
{ setOpenTrailer, movie, setMovie } = useContext(MovieContext),
[trailer, setTrailer] = useState(),
[key, setKey] = useState(),
[modalStyle] = useState()
useEffect(() => {
if (movie) {
getTrailer(movie).then((data) => {
setKey(data.videos.results[0].key)
setTrailer(data)
})
}
}, [movie])
const handleOpen = () => {
setOpenTrailer(true)
}
const handleClose = () => {
setOpenTrailer(false)
setMovie(undefined)
setTrailer(undefined)
setKey(undefined)
}
const renderVideo = (
<>
{key && (
<div className={classes.video}>
<ReactPlayer style={styles.player} url={`https://www.youtube.com/watch?v=${key}`} />
</div>
)}
</>
)
return (
<div>
<Modal
open={open || false}
onClose={handleClose}
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
>
{renderVideo}
</Modal>
</div>
)
}
Which is the proper way to set the dimensions on a react-player object?
Using the width and height props as such:
<ReactPlayer
width={“300px”}
url={`https://www.youtube.com/watch?v=${key}`} />
this code is from the react-player library. This made my video responsive
its from: https://github.com/cookpete/react-player
class ResponsivePlayer extends Component {
render () {
return (
<div className='player-wrapper'>
<ReactPlayer
className='react-player'
url='https://www.youtube.com/watch?v=ysz5S6PUM-U'
width='100%'
height='100%'
/>
</div>
)
}
}
----- CSS ------
.player-wrapper {
position: relative;
padding-top: 56.25% /* Player ratio: 100 / (1280 / 720) */
}
.react-player {
position: absolute;
top: 0;
left: 0;
}
I have this Codesandbox with a Hamburger menu that refuse to let onClick work.
This image show the simple menu with the Timeline link
(it's suppose to be text color white but Codesandbox refuses)
Clicking the link does not fire the onClick it's like it's being blocket by another component.
I have tested to remove the Component NavLinkPage that is rendered at the same time and then my Menu works. It's like it's blocking even it's set to be off screen.
I have tried to set e.g. the z-index but with no luck the only think that works is removing the:
<NavLinkPage close={close} />..
The simple Menu is the Component:
<SideMenu close={close} />..
NavLinkPage are controlled by #media (max-width: 1098px).. and will show on large screens only.
here is CollapseMenu.jsx code with NavLinkPage and NavLinkPage :
import React, { useRef, useCallback, useEffect } from 'react';
import styled from 'styled-components';
import { useSpring, animated } from 'react-spring';
import useOnClickOutside from './UseOnClickOutside';
import NavLinkPage from './NavLinkPage';
import SideMenu from './SideMenu';
const CollapseMenu = props => {
const { show, close } = props;
const { open } = useSpring({ open: show ? 0 : 1 });
const reference = useRef();
useOnClickOutside(reference, close, 'burgerMenu');
const escFunction = useCallback(
event => {
// only accept 27 if it's visible
if (event.keyCode === 27 && show === true) {
close();
}
},
[close],
);
useEffect(() => {
document.addEventListener('keydown', escFunction, false);
return () => {
document.removeEventListener('keydown', escFunction, false);
};
}, [escFunction]);
if (show === true) {
return (
<CollapseWrapper
style={{
transform: open
.interpolate({
range: [0, 0.2, 0.3, 1],
output: [0, -20, 0, -200],
})
.interpolate(openValue => `translate3d(0, ${openValue}px, 0`),
}}
>
<NavLinkPage close={close} />
<SideMenu close={close} />
</CollapseWrapper>
);
}
return null;
};
export default CollapseMenu;
const CollapseWrapper = styled(animated.div)`
background: #2d3436;
position: fixed;
left: 0;
right: 0;
width: 200px;
z-index: 100;
`;
I'm trying to create a React Component that zooms an image and the zoomed image follows the mouse.
I've created a pen to show what I've done so far: React Zoom
And here is the code:
class ProductGallery extends React.Component {
constructor (props) {
super(props)
this.state = {
imgZoomed: false,
mouseX: undefined,
mouseY: undefined
}
this.zoomImage = this.zoomImage.bind(this)
this.unZoomImage = this.unZoomImage.bind(this)
this.moveMouseImg = this.moveMouseImg.bind(this)
}
zoomImage () {
this.setState({
imgZoomed: true
})
}
unZoomImage () {
this.setState({
imgZoomed: false
})
}
moveMouseImg (e) {
const {
top: offsetTop,
left: offsetLeft
} = e.target.getBoundingClientRect()
const x = ((e.pageX - offsetLeft) / e.target.width) * 100
const y = ((e.pageY - offsetTop) / e.target.height) * 100
this.setState({
mouseX: x,
mouseY: y
})
}
render () {
const transform = {
transformOrigin: `${this.state.mouseX}% ${this.state.mouseY}%`
}
const classes = Object.assign({}, transform, {
transform: this.state.imgZoomed ? 'scale(2.0)' : 'scale(1.0)',
maxHeight: '615px',
maxWidth: '100%',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
backgroundSize: 'cover',
transition: 'transform .1s ease-out'
})
const divStyles = {
height: '615px',
width: '615px',
float: 'left',
marginLeft: '10px',
overflow: 'hidden',
borderStyle: 'solid'
}
return (
<div style={divStyles}>
<img
src="http://www.planwallpaper.com/static/images/maxresdefault_8yZPhSS.jpg"
style={classes}
alt="dmsak"
onMouseOver={this.zoomImage}
onMouseOut={this.unZoomImage}
onMouseMove={this.moveMouseImg}
/>
</div>
)
}
}
React.render(<ProductGallery />, document.getElementById('app'));
The problem is that when I move the mouse, the component just starts to "shake"! I am using a scale of 2.0, but if I change it to 1.5 (transform: scale(1.5)), the problem only happens in the edges of the image.
How can I solve it?