I am trying to achieve an image/video carousel using https://jossmac.github.io/react-images/
and it should be like this including modal :
I following the code snippet given there but it's not working and I don't see any step by step guide to making that carousel.
class Gall extends Component {
state = { modalIsOpen: false }
toggleModal = () => {
this.setState(state => ({ modalIsOpen: !state.modalIsOpen }));
}
render() {
const { modalIsOpen } = this.state;
return (
<ModalGateway>
{modalIsOpen ? (
<Modal onClose={this.toggleModal}>
<Carousel views={images} />
</Modal>
) : null}
</ModalGateway>
);
}
}
export default Gall;
can anyone please help with a codesandbox?
Also is it possible to trigger the modal with the current active image?
Thanks in advance.
There is a link in their docs to the source
// #flow
// #jsx glam
import glam from 'glam';
import React, { Component, Fragment } from 'react';
import { type ProviderProps } from '../../ImageProvider';
import Carousel, { Modal, ModalGateway } from '../../../src/components';
import { FooterCaption } from '../components';
import { getAltText } from '../formatters';
type State = {
selectedIndex?: number,
lightboxIsOpen: boolean,
};
export default class Home extends Component<ProviderProps, State> {
state = {
selectedIndex: 0,
lightboxIsOpen: false,
};
toggleLightbox = (selectedIndex: number) => {
this.setState(state => ({
lightboxIsOpen: !state.lightboxIsOpen,
selectedIndex,
}));
};
render() {
const { images, isLoading } = this.props;
const { selectedIndex, lightboxIsOpen } = this.state;
return (
<Fragment>
{!isLoading ? (
<Gallery>
{images.map(({ author, caption, source }, j) => (
<Image onClick={() => this.toggleLightbox(j)} key={source.thumbnail}>
<img
alt={caption}
src={source.thumbnail}
css={{
cursor: 'pointer',
position: 'absolute',
maxWidth: '100%',
}}
/>
</Image>
))}
</Gallery>
) : null}
<ModalGateway>
{lightboxIsOpen && !isLoading ? (
<Modal onClose={this.toggleLightbox}>
<Carousel
components={{ FooterCaption }}
currentIndex={selectedIndex}
formatters={{ getAltText }}
frameProps={{ autoSize: 'height' }}
views={images}
/>
</Modal>
) : null}
</ModalGateway>
</Fragment>
);
}
}
const gutter = 2;
const Gallery = (props: any) => (
<div
css={{
overflow: 'hidden',
marginLeft: -gutter,
marginRight: -gutter,
}}
{...props}
/>
);
const Image = (props: any) => (
<div
css={{
backgroundColor: '#eee',
boxSizing: 'border-box',
float: 'left',
margin: gutter,
overflow: 'hidden',
paddingBottom: '16%',
position: 'relative',
width: `calc(25% - ${gutter * 2}px)`,
':hover': {
opacity: 0.9,
},
}}
{...props}
/>
);
Related
As you can see from the image I have a page where,
the part of the drawer and the dark mode change is found on the parent page.
Where the word Page1 and the input field appear, in the child page.
When the theme is changed, then switched to dark mode, a prop with the darkState state is passed from parent to child.
As you can see from the image if I have an input field in which I am writing, so with some text, then I switch to dark mode or I open the drawer.
The component updates everything, losing all its internal state.
I thought about using useMemo, but I don't know where I should use it.
Can you give me a hand?
Link: https://codesandbox.io/s/competent-sara-dru7w?file=/src/page/Page1.js
App.js
import React from "react";
import PropTypes from "prop-types";
import { Switch, Route, Link, useLocation } from "react-router-dom";
import {
AppBar,
CssBaseline,
Drawer,
Hidden,
IconButton,
List,
ListItem,
ListItemIcon,
ListItemText,
Toolbar,
Chip
} from "#material-ui/core";
import { GTranslate, Menu } from "#material-ui/icons";
import {
makeStyles,
useTheme,
createMuiTheme,
ThemeProvider
} from "#material-ui/core/styles";
import { blue, grey } from "#material-ui/core/colors";
import DarkModeToggle from "react-dark-mode-toggle";
import { Page1, Page2, Error } from "./page";
import "./styles/main.css";
import "./App.css";
const drawerWidth = 240;
function App(props) {
const { wind } = props;
const container = wind !== undefined ? () => wind().document.body : undefined;
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
const localDark = localStorage.getItem("dark");
const isDark = localDark === null ? prefersDark : localDark === "true";
let location = useLocation();
let pathname = location.pathname.replace("/", "");
if (pathname === "") pathname = "page1";
const [state, setState] = React.useState({
mobileOpen: false,
darkState: isDark,
repo: []
});
const { mobileOpen, darkState } = state;
const useStyles = makeStyles((theme) => ({
root: {
display: "flex"
},
drawer: {
[theme.breakpoints.up("sm")]: {
width: drawerWidth,
flexShrink: 0
}
},
appBar: {
[theme.breakpoints.up("sm")]: {
width: `calc(100% - ${drawerWidth}px)`,
marginLeft: drawerWidth
}
},
menuButton: {
marginRight: theme.spacing(2),
[theme.breakpoints.up("sm")]: {
display: "none"
},
backgroundColor: darkState ? grey[900] : blue[500]
},
// necessary for content to be below app bar
toolbar: theme.mixins.toolbar,
drawerPaper: {
width: drawerWidth,
color: "#ffffff",
backgroundColor: darkState ? grey[900] : blue[500]
},
content: {
flexGrow: 1,
padding: theme.spacing(3)
}
}));
const palletType = darkState ? "dark" : "light";
const mainPrimaryColor = darkState ? grey[900] : blue[500];
const mainSecondaryColor = darkState ? grey[800] : blue[300];
const darkTheme = createMuiTheme({
palette: {
type: palletType,
primary: {
main: mainPrimaryColor
},
secondary: {
main: mainSecondaryColor
}
}
});
const classes = useStyles();
const theme = useTheme();
const handleDrawerToggle = () =>
setState((prev) => ({ ...prev, mobileOpen: !mobileOpen }));
const changePage = (page) => setState((prev) => ({ ...prev, page }));
const handleThemeChange = React.useCallback(() => {
localStorage.setItem("dark", !darkState);
setState((prev) => ({ ...prev, darkState: !prev.darkState }));
}, []);
const menu = [
{ title: "Page1", path: "page1", icon: <GTranslate /> },
{ title: "Page2", path: "page2", icon: <GTranslate /> }
];
const routeObj = [
{ path: "/", obj: <Page1 darkState={darkState} /> },
{ path: "page1", obj: <Page1 darkState={darkState} /> },
{ path: "page2", obj: <Page2 darkState={darkState} /> }
];
const drawer = (
<div className="mt-32">
<div className={classes.toolbar} />
<List>
{menu.map(({ title, path, icon, badge }, index) => (
<Link to={`/${path}`} key={title}>
<ListItem button key={title} onClick={() => changePage(path)}>
<ListItemIcon
style={{ color: path === pathname ? "#ffffff" : "#ffffff80" }}
>
{icon}
</ListItemIcon>
<ListItemText
primary={<span className="font-bold">{title}</span>}
style={{ color: path === pathname ? "#ffffff" : "#ffffff80" }}
/>
{badge && (
<Chip
label={badge}
size="small"
color="secondary"
className="font-bold"
style={{ color: "#ffffff" }}
/>
)}
</ListItem>
</Link>
))}
</List>
</div>
);
return (
<ThemeProvider theme={darkTheme}>
<div className={classes.root}>
<CssBaseline />
<AppBar
position="fixed"
className={classes.appBar}
style={{
backgroundColor: darkState ? "#303030" : grey[50],
boxShadow: "none"
}}
>
<Toolbar className={"shadow-none"}>
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={handleDrawerToggle}
className={classes.menuButton}
>
<Menu />
</IconButton>
<div className="ml-auto text-right flex">
<DarkModeToggle
onChange={handleThemeChange}
checked={darkState}
size={60}
/>
</div>
</Toolbar>
</AppBar>
<nav className={classes.drawer} aria-label="mailbox folders">
{/* The implementation can be swapped with js to avoid SEO duplication of links. */}
<Hidden smUp implementation="css">
<Drawer
container={container}
variant="temporary"
anchor={theme.direction === "rtl" ? "right" : "left"}
open={mobileOpen}
onClose={handleDrawerToggle}
classes={{
paper: classes.drawerPaper
}}
ModalProps={{
keepMounted: true // Better open performance on mobile.
}}
>
{drawer}
</Drawer>
</Hidden>
<Hidden xsDown implementation="css">
<Drawer
classes={{
paper: classes.drawerPaper
}}
variant="permanent"
open
>
{drawer}
</Drawer>
</Hidden>
</nav>
<main className={classes.content}>
<div className={classes.toolbar} />
<Switch>
{routeObj.map(({ path, obj }, key) => (
<Route exact path={`/${path}`} component={() => obj} key={key} />
))}
<Route component={() => <Error darkState={darkState} />} />
</Switch>
</main>
</div>
</ThemeProvider>
);
}
App.propTypes = {
/**
* Injected by the documentation to work in an iframe.
* You won't need it on your project.
*/
wind: PropTypes.func
};
export default App;
Page1.js
import React, { useState, useEffect } from "react";
import { TextField, makeStyles } from "#material-ui/core";
import { className } from "../function";
import "../styles/main.css";
export default function Page1({ darkState }) {
const useStyles = makeStyles((theme) => ({
title: {
color: darkState ? "#ffffff" : "#343a40",
textShadow: `3px 3px 2px ${
darkState ? "rgba(0, 0, 0, 1)" : "rgba(150, 150, 150, 1)"
}`
},
button: {
margin: theme.spacing(1)
}
}));
const classes = useStyles();
const [state, setState] = useState({
name: ""
});
const { name } = state;
useEffect(() => {
console.log(darkState, state);
}, []);
useEffect(() => {
console.log("darkState", darkState, state);
}, [darkState]);
const onChange = ({ target: { value } }, name) => {
setState((prev) => ({ ...prev, [name]: value }));
};
console.log(state);
return (
<>
<h1 className={className(classes.title, "text-6xl font-bold hp")}>
Page
<span className="text-primary">1</span>
</h1>
<div
style={{
width: "50%",
minHeight: "600px"
}}
>
<div style={{ paddingBottom: 15 }}>
<TextField
fullWidth
id="outlined-basic"
label={"Name"}
variant="outlined"
size="small"
value={name}
onChange={(value) => onChange(value, "name")}
/>
</div>
</div>
</>
);
}
Goal:
When you press on the button 'ok', the id element named test2 should be display non and id element named test1 should be display block with support of css code.
And also please take account to the color of the text that is located in the css code.
Problem:
I don't know how to solve it.
What is needed to be changed in the source code in order to achieve the goal?
Stackblitz:
https://stackblitz.com/edit/react-modal-gdh4hp?
Info:
*I'm newbie in Reactjs
Thank you!
index.js
import React, { Component } from 'react';
import { render } from 'react-dom';
import { Modal } from './modal';
import './style.css';
class App extends Component {
constructor() {
super();
this.state = {
modal: true
};
}
handleCloseModal = () => {
alert('ddd');
};
render() {
const { modal } = this.state;
const non = {
display: 'none',
color: 'yellow'
};
const block = {
display: 'block',
color: 'yellow'
};
return (
<div>
{modal ? (
<Modal
onClose={() => {
this.setState({ modal: false });
}}
>
<div id="test1" style={non}>Awesome1</div>
<div id="test2">Awesome2</div>
<button onClick={() => this.handleCloseModal()}>ok</button>
</Modal>
) : (
<button
onClick={() => {
this.setState({ modal: true });
}}
>
Show modal
</button>
)}
</div>
);
}
}
render(<App />, document.getElementById('root'));
modal.js
import React from 'react';
export class Modal extends React.Component {
render() {
const { children, onClose } = this.props;
return (
<div style={{position: "absolute", top: 0, left: 0, width: "100%", height: "100%", background: "gray"}} onClick={ev => onClose()}>
<div
style={{margin: "auto", background: "white", border: "red", width: "500px", height: "300px"}}
onClick={ev => ev.stopPropagation()}>
{ children }
</div>
</div>
);
}
}
You can simply do that :
ok() => {
document.getElementById('test1').style.display = 'block'
document.getElementById('test2').style.display = 'none'
}
You can use state
class App extends Component {
constructor() {
super();
this.state = {
modal: true,
showBlock: "",
showNon: "",
color: ""
};
}
handleCloseModal = () => {
this.setState({showBlock: "block"});
this.setState({showNon: "none"});
this.setState({color: "yellow"});
alert('ddd');
};
render() {
const { modal } = this.state;
return (
<div>
{modal ? (
<Modal
onClose={() => {
this.setState({ modal: false });
}}
>
<div id="test1" style={{display: this.state.showBlock, color: this.state.color}}>Awesome1</div>
<div id="test2" style={{display: this.state.showNon, color: this.state.color}}>Awesome2</div>
<button onClick={() => this.handleCloseModal()}>ok</button>
</Modal>
) : (
<button
onClick={() => {
this.setState({ modal: true });
}}
>
Show modal
</button>
)}
</div>
);
}
}
render(<App />, document.getElementById('root'));
React way to achieve that is to use useRef hook (or createRef for class approach):
Class approach:
constructor(props) {
this.testRef = React.createRef()
}
const toggleBlock = () => {
testRef.current.style.display = 'block'
testRef.current.style.color = 'yellow'
}
render() {
return (
<>
<div id="test1" ref={testRef}>Awesome1</div>
<button onclick={this.toggleBlock}>Ok</button>
</>
)
}
Hooks approach:
const testRef = useRef(null)
const toggleBlock = () => {
testRef.current.style.display = 'block'
testRef.current.style.color = 'yellow'
}
return (
<>
<div id="test1" ref={testRef}>Awesome1</div>
<button onclick={this.toggleBlock}>Ok</button>
</>
)
I'm building an application, where there is a form presented with different steps. In all the steps but one, I manage to provide the necessary functions as props to make some operations such as 'handleNext', 'handleBack' or 'handleChange'.
Nevertheless, in the last step, represented in the class SuccessForm, when I try to execute the 'handleDownload' function, I get the following error:
TypeError: this.props.handleDownload is not a function
Here it is the SuccessForm.js class:
export class SuccessForm extends Component {
constructor() {
super();
}
download = e => {
e.preventDefault();
this.props.handleDownload();
}
render() {
return (
<React.Fragment>
<Grid container>
<Grid item xs={12} sm={2}>
<DirectionsWalkIcon fontSize="large" style={{
fill: "orange", width: 65,
height: 65
}} />
</Grid>
<Grid>
<Grid item xs={12} sm={6}>
<Typography variant="h5" gutterBottom>
Route created
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="subtitle1">
Your new track was succesfully created and saved
</Typography>
</Grid>
</Grid>
<Tooltip title="Download" arrow>
<IconButton
variant="contained"
color="primary"
style={{
marginLeft: 'auto',
// marginRight: '2vh'
}}
onClick={this.download}
>
<GetAppIcon fontSize="large" style={{ fill: "orange" }} />
</IconButton>
</Tooltip>
</Grid>
</React.Fragment>
)
}
}
The entire NewRouteForm.js:
import React, { Component } from 'react'
import { makeStyles, MuiThemeProvider } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import Stepper from '#material-ui/core/Stepper';
import Step from '#material-ui/core/Step';
import StepLabel from '#material-ui/core/StepLabel';
import Button from '#material-ui/core/Button';
import Typography from '#material-ui/core/Typography';
import DataForm from '../stepper/dataform/DataForm';
import ReviewForm from '../stepper/reviewform/ReviewForm';
import MapForm from '../stepper/mapform/MapForm';
import NavBar from '../../graphic interface/NavBar';
import DirectionsWalkIcon from '#material-ui/icons/DirectionsWalk';
import Avatar from '#material-ui/core/Avatar';
import CheckCircleOutlineOutlinedIcon from '#material- ui/icons/CheckCircleOutlineOutlined';
import FilterHdrIcon from '#material-ui/icons/FilterHdr';
import Grid from '#material-ui/core/Grid';
import SuccessForm from '../stepper/success/SuccessForm';
import { withStyles } from '#material-ui/styles';
export class NewRouteForm extends Component {
state = {
activeStep: 0,
name: '',
description: '',
date: new Date(),
photos: [],
videos: [],
points: []
};
handleNext = () => {
const { activeStep } = this.state;
this.setState({ activeStep: activeStep + 1 });
};
handleBack = () => {
const { activeStep } = this.state;
this.setState({ activeStep: activeStep - 1 });
};
handleChange = input => e => {
this.setState({ [input]: e.target.value });
}
handleDateChange = date => {
this.setState({ date: date });
}
handleMediaChange = (selectorFiles: FileList, code) => { // this is not an error, is TypeScript
switch (code) {
case 0: // photos
this.setState({ photos: selectorFiles });
break;
case 1: // videos
this.setState({ videos: selectorFiles });
break;
default:
alert('Invalid media code!!');
console.log(code)
break;
}
}
handleMapPoints = points => {
this.setState({ points: points })
}
// ###########################
// Download and Upload methods
// ###########################
handleDownload = () => {
// download route
console.log("DOWNLOAD")
alert("DOWNLOAD");
}
upload = () => {
// upload route
}
render() {
const { activeStep } = this.state;
const { name, description, date, photos, videos, points } = this.state;
const values = { activeStep, name, description, date, photos, videos, points };
const { classes } = this.props;
return (
<MuiThemeProvider>
<React.Fragment>
<NavBar />
<main className={classes.layout}>
<Paper className={classes.paper}>
<Avatar className={classes.avatar}>
<FilterHdrIcon fontSize="large" />
</Avatar>
<Typography component="h1" variant="h4" align="center">
Create your own route
</Typography>
<Stepper activeStep={activeStep} className={classes.stepper}>
{steps.map((label) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
</Step>
))}
</Stepper>
<React.Fragment>
{activeStep === steps.length ? (
<SuccessForm />
) : (
<React.Fragment>
{getStepContent(activeStep,
values,
this.handleNext,
this.handleBack,
this.handleChange,
this.handleDateChange,
this.handleMediaChange,
this.handleMapPoints,
this.handleDownload
)}
</React.Fragment>
)}
</React.Fragment>
</Paper>
</main>
</React.Fragment>
</MuiThemeProvider>
)
}
}
const steps = ['Basic data', 'Map', 'Review your route'];
function getStepContent(step,
values,
handleNext,
handleBack,
handleChange,
handleDateChange,
handleMediaChange,
handleMapPoints,
handleDownload) {
switch (step) {
case 0:
return <DataForm
handleNext={handleNext}
handleChange={handleChange}
handleDateChange={handleDateChange}
handleMediaChange={handleMediaChange}
values={values}
/>;
case 1:
return <MapForm
handleNext={handleNext}
handleBack={handleBack}
handleMapPoints={handleMapPoints}
values={values}
/>;
case 2:
return <ReviewForm
handleNext={handleNext}
handleBack={handleBack}
values={values}
/>;
case 3:
return <SuccessForm
handleDownload={handleDownload}
/>;
default:
throw new Error('Unknown step');
}
}
const useStyles = theme => ({
layout: {
width: 'auto',
marginLeft: theme.spacing(2),
marginRight: theme.spacing(2),
[theme.breakpoints.up(600 + theme.spacing(2) * 2)]: {
width: 600,
marginLeft: 'auto',
marginRight: 'auto',
},
},
paper: {
marginTop: theme.spacing(3),
marginBottom: theme.spacing(3),
padding: theme.spacing(2),
[theme.breakpoints.up(600 + theme.spacing(3) * 2)]: {
marginTop: theme.spacing(6),
marginBottom: theme.spacing(6),
padding: theme.spacing(3),
},
},
stepper: {
padding: theme.spacing(3, 0, 5),
},
buttons: {
display: 'flex',
justifyContent: 'flex-end',
},
button: {
marginTop: theme.spacing(3),
marginLeft: theme.spacing(1),
},
avatar: {
marginLeft: 'auto',
marginRight: 'auto',
backgroundColor: theme.palette.warning.main,
},
icon: {
width: 65,
height: 65,
},
grid: {
marginLeft: theme.spacing(2),
}
});
export default withStyles(useStyles)(NewRouteForm);
Try calling super(props) in the constructor:
constructor(props) {
super(props);
}
and passing function with this instance (this.handleDownload) as it is a class property:
<SuccessForm handleDownload={this.handleDownload} />
Update:
You have a bug on the last step when you not passing a property:
activeStep === steps.length ? <SuccessForm handleDownload={this.handleDownload}/>
Assuming that you have a class in your parent Component, what you're missing is the this keyword in the function reference...
case 3:
return <SuccessForm
handleDownload={this.handleDownload}
/>;
I want to animate this image carousel in reactNative, but have no idea how to start. Read the documentation about animations but still really stuck, have no idea how to incorporate it in. I tried it this way but keep getting a big fat error. Help!
import React from 'react';
import {StyleSheet, View, ScrollView, Dimensions, Image, Animated} from 'react-native'
const DEVICE_WIDTH = Dimensions.get('window').width;
class BackgroundCarousel extends React.Component {
scrollRef = React.createRef();
constructor(props) {
super(props);
this.state = {
selectedIndex: 0,
opacity: new Animated.Value(0)
};
}
componentDidMount = () => {
Animated.timing(this.state.opacity , {
toValue: 1,
duration: 500,
useNativeDriver: true,
}).start();
setInterval(() => {
this.setState(
prev => ({ selectedIndex: prev.selectedIndex ===
this.props.images.length - 1 ? 0 : prev.selectedIndex +1 }),
() => {
this.scrollRef.current.scrollTo({
animated: true,
y: 0,
x: DEVICE_WIDTH * this.state.selectedIndex
});
}
);
}, 6000);
};
componentWillUnmount() {
clearInterval(this.setState);
}
render() {
const {images} = this.props
const {selectedIndex} = this.state
return (
<Animated.Image
onLoad={this.onLoad}
{...this.props}
style={[
{
opacity: this.state.opacity,
},
this.props.style,
]}
/>
<View style= {{height: "100%", width: "100%"}}>
{this.props.children}
<ScrollView
horizontal
pagingEnabled
scrollEnabled={false}
ref={this.scrollRef}
>
{images.map(image => (
<Image
key={image}
source={image}
style={styles.backgroundImage}
/>
))}
</ScrollView>
</View>
)
}
}
const styles = StyleSheet.create ({
backgroundImage: {
height: '100%',
width: DEVICE_WIDTH,
}
});
export default BackgroundCarousel;
Any help would be appreciated. Don't know where I'm going wrong. Basically trying to add a fade effect when my background carousel changes from image to image.
I have fixed your code and removed all errors, copy-paste it in https://snack.expo.io/ and give it some time to load.
Note: I have removed this.props.images for website demo, please change in your real project.
Working fade carousal: https://snack.expo.io/#rajrohityadav/fade-carosal
But I have not implemented this using React Animation.
import React from 'react';
import {StyleSheet, View, ScrollView, Dimensions, Image, Animated} from 'react-native'
const DEVICE_WIDTH = Dimensions.get('window').width;
export default class BackgroundCarousel extends React.Component {
scrollRef = React.createRef();
constructor(props) {
super(props);
this.state = {
selectedIndex: 0,
opacity: new Animated.Value(0)
};
}
componentDidMount = () => {
Animated.timing(this.state.opacity , {
toValue: 1,
duration: 500,
useNativeDriver: true,
}).start();
setInterval(() => {
this.setState(
prev => ({ selectedIndex: prev.selectedIndex ===
3 - 1 ? 0 : prev.selectedIndex +1 }),
() => {
this.scrollRef.current.scrollTo({
animated: true,
y: 0,
x: DEVICE_WIDTH * this.state.selectedIndex
});
}
);
}, 6000);
};
componentWillUnmount() {
clearInterval(this.setState);
}
render() {
const images =[
'https://image.shutterstock.com/image-vector/dragon-scream-vector-illustration-tshirt-260nw-1410107855.jpg','https://image.shutterstock.com/image-vector/dragon-head-vector-illustration-mascot-600w-1201914655.jpg',
'https://i.pinimg.com/474x/b7/1a/bb/b71abb6dd7678bbd14a1f56be5291747--dragon-illustration-samurai-tattoo.jpg']//this.props
const {selectedIndex} = this.state
return (
<>
<Animated.Image
onLoad={this.onLoad}
{...this.props}
style={[
{
opacity: this.state.opacity,
},
this.props.style,
]}
/>
<View style= {{height: "100%", width: "100%"}}>
{this.props.children}
<ScrollView
horizontal
pagingEnabled
scrollEnabled={false}
ref={this.scrollRef}
>
{images.map(image => (
<Image
key={image}
source={image}
style={styles.backgroundImage}
/>
))}
</ScrollView>
</View>
</>
)
}
}
const styles = StyleSheet.create ({
backgroundImage: {
height: '100%',
width: DEVICE_WIDTH,
}
});
You can also use a simple & optimized library react-native-fadecarousel and use it like this:
import React from 'react'
import { View, StyleSheet, Image } from 'react-native';
import FadeCarousel from 'react-native-fadecarousel';
const FadeCarouselScreen = () => {
const images = [
'https://image.shutterstock.com/image-vector/dragon-scream-vector-illustration-tshirt-260nw-1410107855.jpg',
'https://image.shutterstock.com/image-vector/dragon-head-vector-illustration-mascot-600w-1201914655.jpg',
'https://i.pinimg.com/474x/b7/1a/bb/b71abb6dd7678bbd14a1f56be5291747--dragon-illustration-samurai-tattoo.jpg'
];
return <FadeCarousel
loop
fadeAnimationDuration={1000}
autoPlay={{enable: true , delay: 1000 }}>
{
images.map((image, index) => {
return <View key={`slide ${index}`} style={styles.slideItem}>
<Image style={styles.image} resizeMethod="resize" resizeMode="cover" source={{ uri: image }}/>
</View>
})
}
</FadeCarousel>
}
const styles = StyleSheet.create({
image: {
width: "100%",
height: 300
},
slideItem: {
width: "100%",
height: 300,
justifyContent: "center",
alignContent: "center"
}
})
export default FadeCarouselScreen
As per Material Design guidelines:
Upon scrolling, the top app bar can […] transform in the following ways:
- Scrolling upward hides the top app bar
- Scrolling downward reveals the top app bar
When the top app bar scrolls, its elevation above other elements becomes apparent.
Is there any built-in approach to do this in material-ui-next or should it be considered as a new feature? Can you give a hint on how to achieve the animation of the AppBar component as described in the guidelines?
To my knowledge, there's no out-of-the-box solution for this at the moment. It's quite easy to implement though. Here is a snippet that subscribes to scroll events and hides or shows the AppBar accordingly:
const styles = {
root: {
flexGrow: 1,
},
show: {
transform: 'translateY(0)',
transition: 'transform .5s',
},
hide: {
transform: 'translateY(-110%)',
transition: 'transform .5s',
},
};
class CollapsibleAppBar extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
shouldShow: null,
};
this.lastScroll = null;
this.handleScroll = this.handleScroll.bind(this);
// Alternatively, you can throttle scroll events to avoid
// updating the state too often. Here using lodash.
// this.handleScroll = _.throttle(this.handleScroll.bind(this), 100);
}
componentDidMount() {
window.addEventListener('scroll', this.handleScroll, { passive: true });
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
handleScroll(evt) {
const lastScroll = window.scrollY;
if (lastScroll === this.lastScroll) {
return;
}
const shouldShow = (this.lastScroll !== null) ? (lastScroll < this.lastScroll) : null;
if (shouldShow !== this.state.shouldShow) {
this.setState((prevState, props) => ({
...prevState,
shouldShow,
}));
}
this.lastScroll = lastScroll;
}
render() {
const { classes } = this.props;
return (
<AppBar
position="fixed"
color="default"
className={
`${classes.root} ${
this.state.shouldShow === null ? '' : (
this.state.shouldShow ? classes.show : classes.hide
)
}`
}
>
<Toolbar>
<Typography variant="title" color="inherit">
Title
</Typography>
</Toolbar>
</AppBar>
);
}
}
CollapsibleAppBar.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(CollapsibleAppBar);
For those who are still looking for built-in feature, Hide appbar on scroll is available in material-ui.
in the current version of Material-ui, you can simply use the following
import clsx from "clsx";
import useScrollTrigger from "#material-ui/core/useScrollTrigger";
const trigger = useScrollTrigger();
<AppBar className={trigger ? classes.show : classes.hide}>
</AppBar>
https://material-ui.com/components/app-bar/#usescrolltrigger-options-trigger
this seem to work for me
import {
useScrollTrigger,
Fab,
Zoom,
} from '#mui/material';
...
function ElevationScroll(props) {
const { children } = props;
const theme = useTheme();
const trigger = useScrollTrigger({
disableHysteresis: true,
threshold: 0,
});
return React.cloneElement(children, {
sx: trigger
? {
bgcolor: theme.palette.primary.dark,
'transition-duration': '500ms',
'transition-property':
'padding-top, padding-bottom, background-color',
'transition-timing-function': 'ease-in-out',
}
: {
pt: 2,
pb: 2,
bgcolor: theme.palette.primary.main,
},
elevation: trigger ? 5 : 0,
});
}
function ScrollTop(props) {
const { children } = props;
const trigger = useScrollTrigger({
disableHysteresis: true,
threshold: 200,
});
const handleClick = (event) => {
const anchor = (event.target.ownerDocument || document).querySelector(
'#back-to-top-anchor'
);
if (anchor) {
anchor.scrollIntoView({
behavior: 'smooth',
block: 'center',
});
}
};
return (
<Zoom in={trigger}>
<Box
onClick={handleClick}
role="presentation"
sx={{ position: 'fixed', bottom: 16, right: 16, zIndex: 1 }}
>
{children}
</Box>
</Zoom>
);
}
...
return (
<React.Fragment>
<ElevationScroll {...props}>
<AppBar position="sticky">
...
</AppBar>
</ElevationScroll>
<Toolbar
id="back-to-top-anchor"
className="_Toolbar"
sx={{
minHeight: '0 !important',
}}
/>
<ScrollTop {...props}>
<Fab color="secondary" size="small" aria-label="scroll back to top">
<KeyboardArrowUpIcon />
</Fab>
</ScrollTop>
</React.Fragment>
this seem to work for me
import {
useScrollTrigger,
Fab,
Zoom,
} from '#mui/material';
...
function ElevationScroll(props) {
const { children } = props;
const theme = useTheme();
const trigger = useScrollTrigger({
disableHysteresis: true,
threshold: 0,
});
return React.cloneElement(children, {
sx: trigger
? {
bgcolor: theme.palette.primary.dark,
'transition-duration': '500ms',
'transition-property':
'padding-top, padding-bottom, background-color',
'transition-timing-function': 'ease-in-out',
}
: {
pt: 2,
pb: 2,
bgcolor: theme.palette.primary.main,
},
elevation: trigger ? 5 : 0,
});
}
function ScrollTop(props) {
const { children } = props;
const trigger = useScrollTrigger({
disableHysteresis: true,
threshold: 200,
});
const handleClick = (event) => {
const anchor = (event.target.ownerDocument || document).querySelector(
'#back-to-top-anchor'
);
if (anchor) {
anchor.scrollIntoView({
behavior: 'smooth',
block: 'center',
});
}
};
return (
<Zoom in={trigger}>
<Box
onClick={handleClick}
role="presentation"
sx={{ position: 'fixed', bottom: 16, right: 16, zIndex: 1 }}
>
{children}
</Box>
</Zoom>
);
}
...
return (
<React.Fragment>
<ElevationScroll {...props}>
<AppBar position="sticky">
...
</AppBar>
</ElevationScroll>
<Toolbar
id="back-to-top-anchor"
className="_Toolbar"
sx={{
minHeight: '0 !important',
}}
/>
<ScrollTop {...props}>
<Fab color="secondary" size="small" aria-label="scroll back to top">
<KeyboardArrowUpIcon />
</Fab>
</ScrollTop>
</React.Fragment>
https://mui.com/components/app-bar/#usescrolltrigger-options-trigger