I'm working on a react website but new to the animation area.
I'm trying to accomplish the effect like the hero section of this page: https://stripe.com/us/customers, where there's an infinite loop of circles scrolling from right to left, each with different images and sizes.
How should I get started with this infinite loop of objects animation using React? Is there some library that I can use, or is there a react code snippet sample that I can learn from?
you may be able to accomplish this using purely CSS (check out these crazy pure CSS animation examples that you can fork https://envato.com/blog/pure-css-animation-snippets/)
But the ReactJS approach would be to create a component like.. lets say FloatingIcon
import React from 'react';
class FloatingIcon extends React.Component {
constructor(props) {
super(props);
this.state{
horizontalPosition: "0px",
verticalPosition: "0px",
imgRef: "http://blah.com/asdf",
backgroundColor: "#000000"
}
}
changePosition(horizontalPosition, verticalPosition) {
this.setState({
horizontalPosition,
verticalPosition
});
}
render() {
return (
<div>
<img
href={this.state.imgRef}
style={
{translate(this.state.horizontalPosition,
this.state.verticalPosition)},
backgroungColor:{this.state.backgroundColor}}>
</img>
</div>
);
}
}
export default FloatingIcon;
each floating icon has an image, background-color, and position in it's state. Create as many as you need for your page and store them in an array. You can change the position using the changePosition function that sets the state and updates causing the DOM to render again. Getting it to float all pretty will take some work, but if you calculate correctly and create a good position change. This will work in a React technical sense and this is a React like design for such a problem creating components to accomplish these tasks using single responsibility principles. Let me know how it goes. Hope this helps friend.
Cheers!
Related
(I am new to React and TypeScript so apologies ahead of time)
I am trying to build a bar that I can fill between 2 elements and I think the best way might be to figure out where those two elements are after the component is built and then do some logic in order to size the bar properly.
For reference here is what im trying to build:
How / is it possible to get the DOM using something like getBoundingClientRect() in order to know how big and where the bar should start and stop?
Here is the component set up so far:
import React from "react";
import './CardLineItem.css';
import CardLineItemImage from "./CardLineItemImage";
import { FaChevronRight } from 'react-icons/fa';
import CardLineItemProgressBar from "./CardLineItemProgressBar";
export default class CardLineItem extends React.Component {
render() {
return (
<div className="CardLineItem">
<CardLineItemImage cardUrl="platinum-card.png" altText="platinum-card"/>
<FaChevronRight className="CardLineItemChevron" size="25%"/>
<CardLineItemProgressBar />
</div>
)
}
}
I looked into componentDidMount and this.children.toArray but that never returns anything. Suggestions on how I could best do this? CardLineItemProgressBar is the element I need to be flexible depending on the browser size.
Thanks!
Rather than reading the size and position of the other items, and making adjustments before the DOM paints, it's much better for performance to rely on CSS to do something like this.
I would highly recommend seeing if flex or grid can fulfill what you're trying to do here.
By bringing the progress bar component between the two other components in the JSX tree like this ...
<div className="CardLineItem">
<CardLineItemImage cardUrl="platinum-card.png" altText="platinum-card"/>
<CardLineItemProgressBar className="CardLineItemProgress" />
<FaChevronRight className="CardLineItemChevron" size="25%"/>
</div>
... you can add the flex style to the .CardLineItem and a .CardLineItemProgress class to have your progress bar grow accordingly to fill the space.
.CardLineItem {
// ... your other styles
display: flex;
gap: 10px;
}
.CardLineItemProgress {
flex-grow: 1;
align-self: flex-end;
}
That said, if you really would prefer to add JS calculations to manually calculate and set the size, getSnapshotBeforeUpdate() should be what you're looking for.
And if you ever switch to React functional components with hooks (standardized after React 16.8), then the hook you'd be looking for is useLayoutEffect()
Say I've got a component with identical content, but presented two totally different markup structures depending on the device (desktop viewports or mobile viewports).
In this situation when the viewport is below or above certain width or breakpoint (for this example 768px), I want to show one over the other.
A common situation for something like this might be the Navigation, where at Desktop views you have a simple navigation bar in the header of the page, whilst at Mobile views you have a more complex navigation menu that slides in and out:
import React from 'react';
import './Navigation.scss';
const Navigation = () => {
return (
<div className="navigation">
<div className="mobile-navigation-container">
<MobileNavigation />
</div>
<div className="desktop-navigation-container">
<DesktopNavigation />
</div>
</div>
);
};
Solution 1:
A simple solution to achieve this is to use CSS:
.navigation {
.mobile-navigation-container {
#media (min-width: 768px){
display: none;
}
}
.desktop-navigation-container {
#media (max-width: 767px){
display: none;
}
}
}
However, the issue here is that I still have both views in the DOM, even though one is not visible.
Solution #2:
Alternatively, I can use a resize listener and piece of state in my JSX component to conditionally render the correct component depending on the viewport width I can calculate using window.innerWidth:
import React, { Component } from 'react';
const isClient = typeof window !== 'undefined';
class Navigation extends Component {
state = {
viewportWidth: 0,
}
componentDidMount() {
if (isClient) {
this.updateWindowDimensions();
window.addEventListener('resize', this.updateWindowDimensions);
}
}
componentWillUnmount() {
if (isClient) window.removeEventListener('resize', this.updateWindowDimensions);
}
updateWindowDimensions = () => {
this.setState({ viewportWidth: window.innerWidth });
}
render() {
const { viewportWidth } = this.state;
return (
<div className="navigation">
{viewportWidth <= 768 && (
<div className="mobile-navigation-container">
<MobileNavigation />
</div>
)}
{viewportWidth > 768 && (
<div className="desktop-navigation-container">
<DesktopNavigation />
</div>
)}
</div>
);
}
This solves the issue of having duplicate content on the DOM. Which I'd guess is better for Search Engine Crawlers.
However, this somewhat makes my JSX more complicated, and I have the feeling that the CSS breakpoint is cleaner, smoother implementation in terms of performance, rather than using a JavaScript resize listener (though I can't find solid sources to advise one over the other).
My question is which of these two implementations is better practice and why?
The second approach Solution #2 is very good as compared to Solution #1. Because #1 has unnecessary and unwanted elements in DOM Object which is also confusing for react. Although it is not a good practice in any other languages as well. But in #2 you are not rendering unwanted contents this will improve smooth running of your code and debugging and designing is also easy in this approach.
Definitely the 2nd one even though it involves more lines of code, the overall performance outcome is much better because you don’t have extra pieces of DOM elements laying unnecessarily here and there in the page.
What’s more important is the flexibility provided by solution 2, what if you have to change the underlying markup on different screens in the future? (ex. hide some columns in smaller screens)
I managed to made a snack in Expo to simulate the Facebook Navigation bar hiding effect on scroll event, thanks to this amazing post:
https://medium.com/appandflow/react-native-collapsible-navbar-e51a049b560a
Everything works like the Facebook main app.
Here you can find the snack: https://snack.expo.io/#univers3/barra-scomparsa
But now I need to put the scrollview inside a View and I was using this to modify the height of the ScrollView:
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: this.state.scrollAnim } } }],
{ useNativeDriver: true },
)}
The problem is this: if I will incapsulate the ScrollView into a normal View, this event (onScroll) is not available anymore for the parent View.
How I can achieve to modify the height of the parent View?
Edit
To let understand better my problem:
I'm not able to reproduce the same behavior with this environment =>
https://snack.expo.io/SkE2ORuH
Does anyone know how can we achieve this kind of view in React Native, or is there any available components out there that can help on this issue?
I've seen in F8 2016 app too, been searching on how to achieve the transition and the carousel-like view with horizontal scrolling.
I know that the question is old, but a co-worker and I recently had to create a component that answers this particular need. We ended up open-sourcing it, so it's all yours to try: react-native-snap-carousel.
The plugin is now built on top of FlatList (versions >= 3.0.0), which is great to handle huge numbers of items. It also provides previews (the effect you were after), snapping effect, parallax images, RTL support, and more.
You can take a look at the showcase to get a grasp of what can be achieved with it. Do not hesitate to share your experience with the plugin since we're always trying to improve it.
Edit : two new layouts have been introduced in version 3.6.0 (one with a stack of cards effect and the other with a tinder-like effect). Enjoy!
You can achieve this using ScrollView with paging enabled on iOS and ViewPagerAndroid on Android.
F8 being an open source app,
you can see that's what it's actually using:
https://github.com/fbsamples/f8app/blob/master/js/common/ViewPager.js
This component renders all pages.
If you only want to have the visible and left and right pages rendered to save memory, there's another component built on top of it that does it:
https://github.com/fbsamples/f8app/blob/master/js/common/Carousel.js
There are various other similar implementations available:
https://js.coach/react-native?search=carousel
https://js.coach/react-native?search=swiper
However I'm not recommending https://github.com/leecade/react-native-swiper as I've had several issues with it.
Speaking about the swiper-component claiming the best of the world, it still does not work out of the box (as of November 2018) as described in the official swiper-react-native documentation. The issue and a workaround is described in the swiper issue 444:
The error message (on Android) states console.error: "fontFamily 'Arial' is not a system font and has not been loaded through Exponent.Font.loadAsync.
Zach Dixon provided an elegant quick-fix which I repeat here for everybody's convenience. Simply use the following JSX-snippet inside your render()-function to avoid that a new font is required:
<Swiper style={styles.wrapper} showsButtons={true}
nextButton={<Text>></Text>} prevButton={<Text><</Text>}>
<View style={styles.slide1}><Text style>Slide 1</Text></View>
<View style={styles.slide2}><Text style>Slide 2</Text></View>
<View style={styles.slide3}><Text style>Slide 3</Text></View>
</Swiper>
For those interested in explanations on how to implement carousel with Scroll-View only, I recommend a tutorial on a simple image carousel with ScrollView. The tutorial is straight forward and elaborates on the things one has to take care of, but you cannot use it out of the box within or on top of other View-elements. In particular the snapping does not work to well (on Android).
You can create your own custom carousel. The Carousel end result looks like this-
goToNextPage = () => {
const childlenth = this.getCustomData().length;
selectedIndex = selectedIndex + 1;
this.clearTimer();
if (selectedIndex === childlenth) {
this.scrollRef.current.scrollTo({ offset: 0, animated: false, nofix: true });
selectedIndex = 1;
}
this.scrollRef.current.scrollTo({
animated: true,
x: this.props.childWidth * selectedIndex,
});
this.setUpTimer();
}
// pushing 1st element at last
getCustomData() {
const {data} = this.props;
const finaldata = [];
finaldata.push(...data);
finaldata.push(data[0]);
return finaldata;
}
This is the main logic used behind looped carousel.
Here we are pushing the first item at last in the list again and then when scroll reaches at last position we are making the scrollview to scroll to first position as first and last element are same now and we scroll to first position with animation like this
this.scrollRef.current.scrollTo({ offset: 0, animated: false, nofix: true });
For further reference go through the link provided.
https://goel-mohit56.medium.com/custom-horizontal-auto-scroll-looped-carousel-using-scrollview-42baa5262f95
I have been using React for a couple months now and I came from using a lot of jQuery for DOM animations/manipulation. The manipulations I can handle pretty well but animating things and doing shiny UI stuff seems tricky in React. Or at least, not as easy as jQuery. I don't want to pull jQuery in for animations.
Some examples I am trying to achieve:
User scrolls down to my skills section of my Personal Portfolio Page where there are some Bootstrap progress bars. How would I animate these bars to 'fill up' once the user scrolls to them. I imagine something with detecting position? I could also build my own progress bars so as to have more control.
When the user resizes my portfolio section that has a flexbox setup with images that represent my projects, I want the images to reposition themselves slowly, like a transition and not snap to their new spot.
Or something simple like have something slide in from the left/right?
I think jQuery spoiled me! I just feel like I don't have as much as control in React. I have looked at Transition Groups but that seems pretty limited.
Here is my code for the progress bar question...
import React from 'react';
export default (props) => {
const width = `${props.lvl}%`
// setup diff colors for bars
let backgroundColor = (function(lvl){
if (lvl <=35) {
return '#ffa64d'
}
else if (lvl >= 36 && lvl <= 49) {
return '#ffff66'
}
else {
return '#47d147'
}
}(props.lvl))
return (
<div>
<h4 style={{display: 'inline'}}>{props.name}</h4>
<div className="progress">
<div className='progress-bar' role="progressbar" aria-valuenow="70"
aria-valuemin="0" aria-valuemax="100" style={{width, backgroundColor}}>
</div>
</div>
</div>
)
}
I think most people have gotten their animations done through CSS transitions; it's by far the easiest way. However, I have written a custom module that provides functionality that animates elements through manipulation of inline styles stored as part of component state. You can find it here. You'll have to judge for yourself whether the hassle is worth the functionality.