Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add autoPlay #70

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,12 @@ And you're all set. You can also use props for better control of how the carouse

## Props

| Name | Default value | Description |
| --------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| slides | --- | An array containing elements of the form `{ key, content, onClick? }` where key holds any unique value and content holds a reference to a JSX `<img />` to be displayed. onClick is an optional property that may be used to pass a callback method which will be invoked when the slide is clicked. |
| goToSlide | `null` | Setting this prop will cause the carousel to animate towards the provided index in the slides array. If `showNavigation` is set to false this prop is how you're expected to control the carousel. |
| goToSlideDelay | `200` | The amount of milliseconds to delay each slide before moving to the next one while animating towards the index provided in `goToSlide`. |
| offsetRadius | `2` | Number of carousel elements to display to the sides of the current slide, this value is clamped between `1` and `Math.floor(slides.length/2)`, and defaults to 2 when it's possible (if there are enough slide elements). |
| animationConfig | `{ tension: 120, friction: 14 }` | A config object passed to the slides' Spring element to control the nature of their animation, for more information check the [react-spring docs](http://react-spring.surge.sh/#/api#configs). |
| offsetFn | `undefined` | An optional callback function invoked with `(offsetFromRadius: number, index: number)` by each slide per animation to override its style in relation to its current offset (negative numbers are to the left of the center, positive are to the right, with 0 being the center) and index. Should return an object `{ transform?: string; left?: string \| number; opacity?: number }`. Any missing key within the object would trigger the default style behaviours. e.g. `(offsetFromCenter) => ({ opacity: 1 - Math.abs(offsetFromCenter) / 10 })` would change the way the opacity is rendered but not affect `transform` or `left`.
| Name | Default value | Description |
| --------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| slides | --- | An array containing elements of the form `{ key, content, onClick? }` where key holds any unique value and content holds a reference to a JSX `<img />` to be displayed. onClick is an optional property that may be used to pass a callback method which will be invoked when the slide is clicked. |
| goToSlide | `null` | Setting this prop will cause the carousel to animate towards the provided index in the slides array. If `showNavigation` is set to false this prop is how you're expected to control the carousel. |
| goToSlideDelay | `200` | The amount of milliseconds to delay each slide before moving to the next one while animating towards the index provided in `goToSlide`. |
| offsetRadius | `2` | Number of carousel elements to display to the sides of the current slide, this value is clamped between `1` and `Math.floor(slides.length/2)`, and defaults to 2 when it's possible (if there are enough slide elements). |
| animationConfig | `{ tension: 120, friction: 14 }` | A config object passed to the slides' Spring element to control the nature of their animation, for more information check the [react-spring docs](http://react-spring.surge.sh/#/api#configs). |
| offsetFn | `undefined` | An optional callback function invoked with `(offsetFromRadius: number, index: number)` by each slide per animation to override its style in relation to its current offset (negative numbers are to the left of the center, positive are to the right, with 0 being the center) and index. Should return an object `{ transform?: string; left?: string \| number; opacity?: number }`. Any missing key within the object would trigger the default style behaviours. e.g. `(offsetFromCenter) => ({ opacity: 1 - Math.abs(offsetFromCenter) / 10 })` would change the way the opacity is rendered but not affect `transform` or `left`. |
| autoPlay | `undefined` | The amount of milliseconds to delay each slide before moving to the next one automatically. |
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-spring-3d-carousel",
"version": "1.3.4",
"version": "1.3.5",
"repository": {
"type": "git",
"url": "git+https://github.com/gutiguy/react-spring-3d-carousel"
Expand Down
39 changes: 32 additions & 7 deletions src/components/Carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,16 @@ const NavigationButtons = styled.div`
const DEFAULT_GO_TO_SLIDE_DELAY = 200;

export type OffsetFn = (
offsetFromCenter: number,
index: number
) => { transform?: string; left?: string | number; opacity?: number };
offsetFromCenter: number,
index: number
) => { transform?: string; left?: string | number; opacity?: number };

interface IState {
index: number;
goToSlide: number | null;
prevPropsGoToSlide: number;
newSlide: boolean;
mouseIn: boolean;
}

interface IProps {
Expand All @@ -46,7 +47,8 @@ interface IProps {
offsetRadius: number;
animationConfig: object;
goToSlideDelay: number;
offsetFn?: OffsetFn
autoPlay?: number;
offsetFn?: OffsetFn;
}

function mod(a: number, b: number): number {
Expand All @@ -59,9 +61,11 @@ class Carousel extends Component<IProps, IState> {
goToSlide: null,
prevPropsGoToSlide: 0,
newSlide: false,
mouseIn: false,
};

goToIn?: number;
autoInterval?: any;

static propTypes = {
slides: PropTypes.arrayOf(
Expand All @@ -76,6 +80,7 @@ class Carousel extends Component<IProps, IState> {
animationConfig: PropTypes.object,
goToSlideDelay: PropTypes.number,
offsetFn: PropTypes.func,
autoPlay: PropTypes.number,
};

static defaultProps = {
Expand All @@ -95,7 +100,7 @@ class Carousel extends Component<IProps, IState> {
}

componentDidUpdate() {
const { goToSlideDelay } = this.props;
const { goToSlideDelay, autoPlay } = this.props;
const { index, goToSlide, newSlide } = this.state;
if (typeof goToSlide === "number") {
if (newSlide) {
Expand All @@ -107,12 +112,28 @@ class Carousel extends Component<IProps, IState> {
window.clearTimeout(this.goToIn);
}
}
if (autoPlay && !this.state.mouseIn) {
if (!this.autoInterval)
this.autoInterval = setInterval(() => this.moveSlide(1), autoPlay);
} else {
clearInterval(this.autoInterval);
this.autoInterval = null;
}
}

componentDidMount(): void {
const { autoPlay } = this.props;
if (autoPlay && !this.state.mouseIn && !this.autoInterval) {
this.autoInterval = setInterval(() => this.moveSlide(1), autoPlay);
}
}

componentWillUnmount() {
if (typeof window !== "undefined") {
window.clearTimeout(this.goToIn);
}
clearInterval(this.autoInterval);
this.autoInterval = null;
}

modBySlidesLength = (index: number): number => {
Expand Down Expand Up @@ -190,7 +211,8 @@ class Carousel extends Component<IProps, IState> {
}

render() {
const { animationConfig, offsetRadius, showNavigation, offsetFn } = this.props;
const { animationConfig, offsetRadius, showNavigation, offsetFn } =
this.props;

let navigationButtons = null;
if (showNavigation) {
Expand All @@ -212,7 +234,10 @@ class Carousel extends Component<IProps, IState> {
}
return (
<React.Fragment>
<Wrapper>
<Wrapper
onMouseEnter={() => this.setState((s) => ({ ...s, mouseIn: true }))}
onMouseLeave={() => this.setState((s) => ({ ...s, mouseIn: false }))}
>
{this.getPresentableSlides().map(
(slide: Slide, presentableIndex: number) => (
<Slide
Expand Down
28 changes: 17 additions & 11 deletions src/dev/Playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,48 +10,49 @@ export default class Example extends Component {
showNavigation: true,
config: config.gentle,
goToSlideDelay: 200,
autoPlay: 0,
};

slides: Slide[] = [
{
key: uuidv4(),
content: <img src="https://picsum.photos/800/801/?random" alt="1" />
content: <img src="https://picsum.photos/800/801/?random" alt="1" />,
},
{
key: uuidv4(),
content: <img src="https://picsum.photos/800/802/?random" alt="2" />
content: <img src="https://picsum.photos/800/802/?random" alt="2" />,
},
{
key: uuidv4(),
content: <img src="https://picsum.photos/600/803/?random" alt="3" />
content: <img src="https://picsum.photos/600/803/?random" alt="3" />,
},
{
key: uuidv4(),
content: <img src="https://picsum.photos/800/500/?random" alt="4" />
content: <img src="https://picsum.photos/800/500/?random" alt="4" />,
},
{
key: uuidv4(),
content: <img src="https://picsum.photos/800/804/?random" alt="6" />
content: <img src="https://picsum.photos/800/804/?random" alt="6" />,
},
{
key: uuidv4(),
content: <img src="https://picsum.photos/500/800/?random" alt="7" />
content: <img src="https://picsum.photos/500/800/?random" alt="7" />,
},
{
key: uuidv4(),
content: <img src="https://picsum.photos/800/600/?random" alt="8" />
content: <img src="https://picsum.photos/800/600/?random" alt="8" />,
},
{
key: uuidv4(),
content: <img src="https://picsum.photos/805/800/?random" alt="9" />
}
content: <img src="https://picsum.photos/805/800/?random" alt="9" />,
},
].map((slide, index) => {
return { ...slide, onClick: () => this.setState({ goToSlide: index }) };
});

onChangeInput = (e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({
[e.target.name]: parseInt(e.target.value, 10) || 0
[e.target.name]: parseInt(e.target.value, 10) || 0,
});
};

Expand All @@ -65,14 +66,15 @@ export default class Example extends Component {
offsetRadius={this.state.offsetRadius}
showNavigation={this.state.showNavigation}
animationConfig={this.state.config}
autoPlay={this.state.autoPlay}
/>
<div
style={{
margin: "0 auto",
marginTop: "2rem",
width: "50%",
display: "flex",
justifyContent: "space-around"
justifyContent: "space-around",
}}
>
<div>
Expand All @@ -87,6 +89,10 @@ export default class Example extends Component {
<label>Offset Radius: </label>
<input name="offsetRadius" onChange={this.onChangeInput} />
</div>
<div>
<label>Auto Play: </label>
<input name="autoPlay" onChange={this.onChangeInput} />
</div>
<div>
<label>Show navigation: </label>
<input
Expand Down