Skip to content

Commit

Permalink
feat: dots
Browse files Browse the repository at this point in the history
  • Loading branch information
jiangbo11 committed Oct 20, 2021
1 parent a9f6864 commit caaba92
Show file tree
Hide file tree
Showing 9 changed files with 203 additions and 100 deletions.
Binary file added .DS_Store
Binary file not shown.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Change Log

## 1.0.5

support custom dots

## 1.0.4

add a live demo

## 1.0.3

fix export default from syntax
Expand Down
51 changes: 30 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# framer-motion-carousel

A simple framer-motion-carousel

A simple `framer-motion-carousel`, used for `framer-motion`, `chakra-ui`
support `click` and `swipe`, support custom `arrows`, `dots`, easy to use.
## Basic Usage

```jsx
Expand Down Expand Up @@ -29,34 +29,43 @@ const App = () => (
export default App;
```

## images carousel

`img` element should add `draggable=false`

```jsx
<div style={{ width: 600, height: 400, margin: "0 auto" }}>
<Carousel>
{[1, 2, 3, 4].map((item, i) => (
<img
draggable="false"
src={`./${item}.jpg`}
key={i}
width="100%"
alt=""
/>
))}
</Carousel>
</div>
```

## Example

[Live Demo](https://carousel-app-772051431.vercel.app)

![example](https://raw.githubusercontent.com/jiangbo2015/framer-motion-carousel/main/img.jpg)

## Use your arrows

```
renderArrowLeft: ({handlePrev, activeIndex}) => React.ReactNode
```

```
renderArrowRight: ({handleNext, activeIndex}) => React.ReactNode
```

```jsx
const App = () => (
<Carousel renderArrowLeft={({handlePrev, activeIndex}) => <div />}>
...
</Carousel>
)
```
## props

## Todo
| props | type | default | description |
|------------------|--------------------------------------------------------------------------------------|---------|--------------------|
| autoPlay | boolean | true | auto play |
| interval | number | 2000 | auto play interval |
| renderArrowLeft | ({handlePrev: () => void, activeIndex: number}) => React.ReactNode | null | custom your arrows |
| renderArrowRight | ({handleNext: () => void, activeIndex: number}) => React.ReactNode | null | custom your arrows |
| renderDots | ({activeIndex: number, setActiveIndex: (index: number) => void;}) => React.ReactNode | null | custom your dots |

- dots
- custom dots


## Development
Expand Down
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
{
"name": "framer-motion-carousel",
"version": "1.0.3",
"version": "1.0.5",
"description": "A simple framer motion carousel component for React",
"keywords": [
"react",
"component",
"carousel",
"motion",
"framer",
"collapse",
"framer motion"
"swipe",
"image gallary",
"framer motion",
"chakra ui"
],
"author": "jiangbo2015 <reactbo@gmail.com>",
"homepage": "https://github.com/jiangbo2015/framer-motion-carousel.git#readme",
Expand Down
33 changes: 33 additions & 0 deletions src/arrow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react'
import {ArrowProps} from './types'

const baseArrowStyle: React.CSSProperties = {
position: "absolute",
width: "50px",
height: "50px",
backgroundColor: "rgba(0,0,0,0.5)",
top: "50%",
transform: "translateY(-50%)",
borderRadius: "50%",
color: "#fff",
fontSize: "20px",
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
}

const Arrow = ({ left = false, children, onClick }: ArrowProps) => (
<div
onClick={onClick}
style={{
...baseArrowStyle,
left: left ? "20px" : "initial",
right: !left ? "10px" : "initial",
}}
>
{children}
</div>
)

export default Arrow
101 changes: 25 additions & 76 deletions src/carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ import {
AnimationOptions,
motion,
MotionStyle,
MotionValue,
PanInfo,
useMotionValue,
} from "framer-motion"

import { CarouselProps } from "./types"
import Arrow from "./arrow"
import Slider from "./slider"
import Dots from "./dots"

const containerStyle: React.CSSProperties = {
position: "relative",
width: "100%",
Expand All @@ -17,13 +21,6 @@ const containerStyle: React.CSSProperties = {
display: "flex",
}

const pageStyle: MotionStyle = {
width: "100%",
height: "100%",
display: "inline-block",
flex: "none",
}

const transition: AnimationOptions<any> = {
type: "spring",
bounce: 0,
Expand All @@ -38,78 +35,13 @@ const Contaier = React.forwardRef<
</div>
))

type SliderProps = {
x: MotionValue<number>
i: number
children: React.ReactNode
onDragEnd: (e: Event, dragProps: PanInfo) => void
}
const Slider = ({ x, i, onDragEnd, children }: SliderProps) => (
<motion.div
style={{
...pageStyle,
x,
left: `${i * 100}%`,
right: `${i * 100}%`,
}}
drag="x"
dragElastic={0.3}
onDragEnd={onDragEnd}
>
{children}
</motion.div>
)

const baseArrowStyle: React.CSSProperties = {
position: "absolute",
width: "50px",
height: "50px",
backgroundColor: "rgba(0,0,0,0.5)",
top: "50%",
transform: "translateY(-50%)",
borderRadius: "50%",
color: "#fff",
fontSize: "20px",
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
}

type ArrowProps = {
onClick: () => void
left?: boolean
children: React.ReactNode
}

const Arrow = ({ left = false, children, onClick }: ArrowProps) => (
<div
onClick={onClick}
style={{
...baseArrowStyle,
left: left ? "20px" : "initial",
right: !left ? "10px" : "initial",
}}
>
{children}
</div>
)

type CarouselProps = {
children: React.ReactNode
renderArrowLeft?: (args: {
handlePrev: () => void
activeIndex: number
}) => void
renderArrowRight?: (args: {
handleNext: () => void
activeIndex: number
}) => void
}
export const Carousel = ({
children,
renderArrowLeft,
renderArrowRight,
renderDots,
autoPlay = true,
interval = 2000,
}: CarouselProps) => {
const x = useMotionValue(0)
const containerRef = React.useRef<HTMLDivElement>(null)
Expand Down Expand Up @@ -147,13 +79,22 @@ export const Carousel = ({
return controls.stop
}, [index])

React.useEffect(() => {
if(!autoPlay) {
return;
}
const timer = setInterval(() => handleNext(), interval)
return clearInterval(timer)
}, [handleNext, interval])

return (
<Contaier ref={containerRef}>
{childrens.map((child, i) => (
<Slider onDragEnd={handleEndDrag} x={x} i={i}>
{child}
</Slider>
))}
{/* left arrow */}
{renderArrowLeft ? (
renderArrowLeft({ handlePrev, activeIndex: index })
) : (
Expand All @@ -162,11 +103,19 @@ export const Carousel = ({
</Arrow>
)}

{/* right arrow */}
{renderArrowRight ? (
renderArrowRight({ handleNext, activeIndex: index })
) : (
<Arrow onClick={handleNext}>&rarr;</Arrow>
)}

{/* dots */}
{renderDots ? (
renderDots({ setActiveIndex: setIndex, activeIndex: index })
) : (
<Dots length={childrens.length} setActiveIndex={setIndex} activeIndex={index} />
)}
</Contaier>
)
}
38 changes: 38 additions & 0 deletions src/dots.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from "react"
import { DotProps } from "./types"

const dotWrapStyle: React.CSSProperties = {
position: "absolute",
bottom: "10px",
left: "50%",
transform: "translateX(-50%)",
}

const dotItemStyle: React.CSSProperties = {
width: "20px",
height: "20px",
borderRadius: "50%",
margin: "0 10px",
display: "inline-block",
cursor: "pointer",
}

const Dots = ({ length, activeIndex, setActiveIndex }: DotProps) => {
return (
<div style={dotWrapStyle}>
{new Array(length).fill("").map((_, i) => (
<span
onClick={() => setActiveIndex(i)}
key={i}
style={{
...dotItemStyle,
background: i === activeIndex ? "#000" : "#999",
transform: `scale(${i === activeIndex ? 1.3 : 1})`,
}}
></span>
))}
</div>
)
}

export default Dots
29 changes: 29 additions & 0 deletions src/slider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

import React from 'react'
import { motion, MotionStyle } from 'framer-motion'
import { SliderProps } from './types'

const pageStyle: MotionStyle = {
width: "100%",
height: "100%",
display: "inline-block",
flex: "none",
}

const Slider = ({ x, i, onDragEnd, children }: SliderProps) => (
<motion.div
style={{
...pageStyle,
x,
left: `${i * 100}%`,
right: `${i * 100}%`,
}}
drag="x"
dragElastic={0.3}
onDragEnd={onDragEnd}
>
{children}
</motion.div>
)

export default Slider
Loading

0 comments on commit caaba92

Please sign in to comment.