react: fade-in/slide-in animation when render component - reactjs

I'm new in React. I have made a small app with a button and a list of image urls. When button is clicked, an image url is added to the list. I render the list of image urls with standard .map function.
I would like to make a fast ui animation effect when the image is displayed: a combination of fade-in and slide-in from left. I tried Velocity.js and found the velocity-react wrapper. But I can not understand how to use it. The same goes for the 'standard' velocity-animate library.
What is best? velocity-react, velocity-animate or something else?
And how do I do it?
JSX
<div className="row">
{
this.state.images.map( (image, index) => { return this.renderThumb(image, index); } )
}
</div>
renderThumb function
renderThumb(image, index) {
return (
<div ref="tweetImage" key={`image-${index}`} className="col-xs-3 tweetImage">
<img className="img-thumbnail" src={image} alt="my pic"/>
</div>
);
}
velocity-react
I tried to wrap <img> animation opacity from 0 to 1 like this (copied from docs):
<VelocityComponent animation={{ opacity: 1 }} duration={ 500 }>
<img className="img-thumbnail" src={image} alt="my pic"/>
</VelocityComponent
I keep getting this error:
Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object
No luck with ReactCSSTransitionGroup either (like suggestions below). Images are shown but without animation:
renderThumb(image, index) {
return (
<div ref="tweetImage" key={`image-${index}`} className="col-xs-3">
<ReactCSSTransitionGroup
transitionName="example">
<img className="img-thumbnail" src={image} alt="Ole Frank Jensen"/>
</ReactCSSTransitionGroup>
</div>
);
}
SOLVED:
I moved <ReactCSSTransitionGroup transitionName="example"> outside of the fading component and voilá :-)
render()
<div className="row">
<ReactCSSTransitionGroup transitionName="example">
{
this.state.images.map( (image, index) => { return this.renderThumb(image, index); } )
}
</ReactCSSTransitionGroup>
</div>
renderThumb()
renderThumb(image, index) {
return (
<div key={`image-${index}`} className="col-xs-3">
<img className="img-thumbnail" src={image} alt="Ole Frank Jensen"/>
</div>
);
}

You can use the CSSTransitionGroup provided by react.
https://facebook.github.io/react/docs/animation.html
a simple todo exammple from the docs
class TodoList extends React.Component {
constructor(props) {
super(props);
this.state = {items: ['hello', 'world', 'click', 'me']};
this.handleAdd = this.handleAdd.bind(this);
}
handleAdd() {
const newItems = this.state.items.concat([
prompt('Enter some text')
]);
this.setState({items: newItems});
}
handleRemove(i) {
let newItems = this.state.items.slice();
newItems.splice(i, 1);
this.setState({items: newItems});
}
render() {
const items = this.state.items.map((item, i) => (
<div key={item} onClick={() => this.handleRemove(i)}>
{item}
</div>
));
return (
<div>
<button onClick={this.handleAdd}>Add Item</button>
<ReactCSSTransitionGroup
transitionName="example"
transitionEnterTimeout={500}
transitionLeaveTimeout={300}>
{items}
</ReactCSSTransitionGroup>
</div>
);
}
}

i'd recommend going with React CSS Transistion Group module. It provides an high level animation wrapper for simple animations.
EDIT:
The link will permanently redirect to Animation Add-Ons
ReactTransitionGroup and ReactCSSTransitionGroup have been moved to the react-transition-group package that is maintained by the community.
From the docs
.example-enter {
opacity: 0.01;
}
.example-enter.example-enter-active {
opacity: 1;
transition: opacity 500ms ease-in;
}
.example-leave {
opacity: 1;
}
.example-leave.example-leave-active {
opacity: 0.01;
transition: opacity 300ms ease-in;
}
Where example will be the transistionName for the component that you wish to paint. and -enter,enter-active,-leave,-leave-active stand for the corresponding ticks of the animation cycle. These will be added by React internally as class names to the items.
You can use them to achieve the desired effect in question. A small demo here.
P.S: not sure if this outperforms Velocity.js, havent used that.

Related

Why is my SVG not appearing when passed as prop?

I've looked through some of the other, similar questions on here, but none of the solutions seem to work for me.
I have a class component that represents a cell on a grid, and for each cell, I want there to be a specific background image that is revealed with the cell is hovered. The default component looks like this:
const Home = () => {
return (
<div id="main">
<Box title='About' svg={ BG1 } />
<Box title='Work' svg={ BG2 } />
<Box title='Portfolio' svg={ BG3 } />
<Box title='Resume' svg={ BG4 } />
</div>
);
};
I want to pass SVG components as background images, but can't seem to figure it out. I've imported each background image like this:
import { ReactComponent as BG1 } from './bg1.svg';
In the Box component, I've added state to handle some of the child elements, and the title prop seems to work out well, but the svg prop doesn't seem to work for me:
class Box extends React.Component {
constructor(props) {
super(props);
this.state = {
hovered: false,
};
}
hoverOn = e => {
this.setState({ hovered: true });
};
hoverOff = e => {
this.setState({ hovered: false });
};
render() {
const { hovered } = this.state;
const style = hovered ? { transform: 'translateY(-20%)', opacity: '.05' } : {};
return(
<section onMouseEnter={this.hoverOn} onMouseLeave={this.hoverOff}>
<a>
<div className="overlay" style={ style }></div>
<div className="bg" style={{ backgroundImage: `url(${this.props.svg})` }} ></div>
<div className="innerContent">
<h2>
{this.props.title}
</h2>
</div>
</a>
</section>
);
};
};
Whenever I inspect my developmental site, I just see <div class='bg'></div>. Any suggestions?
It's not working because BG1 is being set to the SVG file contents; not a link to the SVG file. I think you can just reference the filename in url("./bg1.svg") and it will expand it at build time. However, that probably won't work when you just pass in the filename as a prop. So - you could put the SVGs into the public folder and do something like this: {process.env.PUBLIC_URL + '/img/logo.png'}. Escape hatch doc.
With Vite you can do this: import modelGLB from './static/model.glb?url' to get the URL, but I forget the create-react-app equivalent.

React accordion with correlating image outside accordion section

I can't find any examples of accordions where the active class is related to an element outside of the accordion. I'm trying to get an image to change on the side of the accordion, where each image is related to a specific object. I managed to get something working using absolute positioning, but I'm looking for a more elegant solution so I can manipulate styling better.
I can get it to work while the image is inside the accordion under the info text, but can't figure out the styling issue. I think I need to do some refactoring or do away with the array mapping to get it to work but I'm not sure.
Here is a codesandbox of more or less what I want to achieve but without the restriction of absolute positioning - https://codesandbox.io/s/ecstatic-taussig-f084t?file=/src/App.js
You can remove your img tag from your renderedItems and do something like this:
import React, { useState } from "react";
const Accordion = ({ items }) => {
const [activeIndex, setActiveIndex] = useState(0);
const onTitleClick = (index) => {
setActiveIndex(index);
};
const renderedItems = items.map((item, index) => {
const active = index === activeIndex ? "active" : "";
return (
<div key={item.title}>
<div className={`title ${active}`} onClick={() => onTitleClick(index)}>
<i className="dropdown icon"></i>
{item.title}
</div>
<div className={`content ${active}`}>
<p>{item.content}</p>
</div>
</div>
);
});
return (
<div className="container-gallery">
<div className="ui styled accordion">{renderedItems}</div>
<img
className={`content `}
src={`${items[activeIndex].image}`}
style={{
height: "200px",
width: "200px"
}}
alt="img"
/>
</div>
);
};
export default Accordion;
And for the style I don't know what you are using so I made css for the example:
.container-gallery{
display:flex;
flex-wrap:no-wrap;
justify-content: space-between;
}
here a sandBox link

React CSSTransition "Type {number} not assignable to type boolean"

I am new to the React CSS Transition Group, but I thought I would give it a little try, just working with an example from their own website. So basically what I have is this:
REACT:
import React, { memo, useState } from "react";
import "./style.styles.scss";
import { CSSTransition } from 'react-transition-group'
const App= ( ) => {
const initialCount = 0
const [count, setCount] = useState(initialCount)
const components = [
<CSSTransition in={count} timeout={200} classNames="my-node">
<div className="box">
<div>DIV1</div>
<div className="button" onClick={() => setCount(count + 1)}>
Next</div>
</div>
</div>
</CSSTransition>,
<CSSTransition in={count} timeout={2000} classNames="my-node">
<div className="box">
<div>DIV2</div>
<div className="button" onClick={() => setCount(count - 1)}>
Prev</div>
</div>
<div className="button" onClick={() => setCount(count + 1)}>
Next</div>
</div>
</div>
</CSSTransition>
]
return (
<div>
{components[count]}
</div>
);
};
export default memo(App);
CSS:
.my-node-enter {
opacity: 0;
transition: opacity 2000ms;
}
.my-node-enter-active {
opacity: 1;
transition: opacity 2000ms;
}
.my-node-exit {
opacity: 1;
transition: opacity 2000ms;
}
.my-node-exit-active {
opacity: 0;
transition: opacity 2000ms;
}
So I am trying to have two divs, with different content, and the initial state is to open/show the first div DIV1, and then when "Next" is clicked I go to the next div DIV2, which I can switch between through the count state. Now, that is working, but they just show up, and disappear with no animation. I then saw this CSSTransition for React, which seemed to be able to do this animation stuff quite easily. But I am kind of stuck on my example above (which is taken from their website). It's obvious that the problem is with the in expression in the CSSTransition, which says "Type {number} not assignable to type boolean". So that's probably because it expected a true/false statement. So my question is: How do I convert this into a "count". I am thinking I might need some kind of if-statement saying something like: "if count == 0 then". But as I am new to React, I am not quite sure how this works in this case ?

React not able to render an array(state) in a single row

The state
state = {
posts: [],
}
The posts gets an API value of
{
"authors": [
{
"authorName": "Sideney Sheldon",
"authorImageURL": "http..."
},
{
"authorName": "Sideney Sheldon",
"authorImageURL": "http..."
},
....
]
}
code
render(){
const postList = posts.length ? (
posts.map(post => {
return (
<div className="" key={this.state.key}>
<div className="containerimager">
<img style={imgb} src={post.authorImageURL} />
</div>
<h6 style={{ marginTop:"23px"}} >{post.authorName}</h6>
</div>
)
})
) :
(
<div className="center">No posts to show</div>
);
return(
{postList}
)
}
The {postlist} returns objects but it returns each object in a new row in the webpage
I want all the results(images) in the same row
can anyone please help?
If you want multiple elements in the same row, you could achieve that with css. Use display: flex(by default flex-direction is row which is what you need) on the wrapper to postList. You can either pass it as a style though JSX or add a class that specifies this property
render(){
const postList = posts.length ? (
posts.map(post => {
return (
<div className="" key={this.state.key}>
<div className="containerimager">
<img style={imgb} src={post.authorImageURL} />
</div>
<h6 style={{ marginTop:"23px"}} >{post.authorName}</h6>
</div>
)
})
) : (
<div className="center">No posts to show</div>
);
return(
<div style={{display: 'flex'}}>{postList}</div>
)
}
Here's a CodeSandbox sample I created using your code.
If you're trying to line up the users horizontally (from left to right), CSS is the way to go. In particular, the <div> that is being mapped has, by default, the display: block style which makes it occupy its own row in the page.
To line up divs side by side, you need to somehow apply the display: inline-block style. In my example I used a style prop, but you will likely want to do it in CSS directly.
This is a CSS problem, you should use display: flex together with flex-direction: row on the parent div which will provide the desired result.
posts.map(post,index => {
return (
<div className="" key={this.state.key}>
<div className="containerimager">
<img style={imgb} src={post.authors[index].authorImageURL} />
</div>
<h6 style={{ marginTop:"23px"}} >{post.authors[index].authorName}</h6>
</div>
)
})
) : (
<div className="center">No posts to show</div>
);
Try this

React is removing dom before ReactCSSTransitionGroup animation finishes

I a component that is supposed to swap out a line of text with a list of icons. I am trying to animate the departure of the text and the entry of the icons using ReactCSSTransitionGroup. The entry of the icons works fine but react kills the dom of the text before the animation can run. here's the render state of the component:
render () {
return (
<section className="SocialBlock" onMouseOver={this.showIcons} onMouseLeave={this.hideIcons}>
{(() => {
if (this.state.iconsAreVisible) {
return (
<div className={`socialAccounts`}>
<ReactCSSTransitionGroup
transitionName="socialIcons"
transitionEnterTimeout={500}
transitionLeaveTimeout={300}
transitionAppear={true} >
{socials.map((icon, index) => {
return <div className={`icon icon-${index+1}`} key={index}><InlineSVG src={icon} /></div>
})}
</ReactCSSTransitionGroup>
</div>
)
} else {
return (
<ReactCSSTransitionGroup
transitionName="socialText"
transitionEnterTimeout={500}
transitionLeaveTimeout={500}
transitionAppear={true} >
<div key="12313"><h3>Check out the social stuff!</h3></div>
</ReactCSSTransitionGroup>)
}
})()}
</section>
);
}
I'm not sure why it's not working. I can switch my animation classes to appear instead of leave and it works for the entry - but the departure is still abrupt.
You are getting this behavior because of the structure of your component.
When (this.state.iconsvisible == false), then your entire second <ReactCSSTransitionGroup> will be unmounted, and will not be rendered.
And then it does not get the chance to do its leave transition.
Anything that needs to animate on leaving, should be inside the <ReactCSSTransitionGroup>.
To fix, you could do this:
<section>
{
<ReactCSSTransitionGroup>
{ if (this.state.iconsarevisible) {
socials.map(...)
}
}
</ReactCSSTransitionGroup>
<ReactCSSTransitionGroup>
{ if (!this.state.iconsarevisible) {
<div>Check out social stuff</div>
}
}
</ReactCSSTransitionGroup>
}
</section>

Resources