Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import React, { useState, useEffect } from 'react'
- import { useSpring, animated, interpolate } from 'react-spring'
- import { useDrag } from 'react-use-gesture'
- // let's handle gestures within our pager component
- const PAGE_SIZE = 375
- // arbitrary value that will determine if we should transition after dragging
- const DRAG_THRESHOLD = Math.floor(PAGE_SIZE * 0.3)
- function Pager({ children, activeIndex, onChange }) {
- const offset = activeIndex * 100 * -1
- // dragX will represent the current drag value to animate
- const [{ translateX, dragX }, set] = useSpring(() => ({
- translateX: offset,
- dragX: 0,
- }))
- // this might look a bit strange but it's part of the api for useDrag
- // bind() is a function we'll add to our container div that gives us a bunch of gesture state data
- // think of this as an event listener for gestures
- const bind = useDrag(({ delta, last, vxvy }) => {
- // this is the drag value
- const [x] = delta
- // the velocity of the drag -- important to track to prevent jank after user releases
- const [vx] = vxvy
- // we want the value to immediate update w/ a user drag event, not spring to the value
- set({ dragX: x, immediate: true })
- // last is true when the user releases from dragging
- if (last) {
- // user has dragged beyond our threshold to transition (either left or right)
- const shouldTransition = Math.abs(x) >= DRAG_THRESHOLD
- if (!shouldTransition) {
- // restore to initial position when user started dragging:
- set({ dragX: 0, immediate: false })
- } else {
- // determine the next position based on the drag value (left or right)
- let nextPosition
- if (x > DRAG_THRESHOLD) {
- // transition to previous page
- nextPosition = offset + 100
- // update our controller component w/ the previous index
- onChange(activeIndex - 1)
- }
- if (x < DRAG_THRESHOLD) {
- // transition to next page
- nextPosition = offset - 100
- // update our controller component w/ the next index
- onChange(activeIndex + 1)
- }
- // start spring transition to next position
- // we want to spring the drag value back to 0 as we translate to the next position
- set({
- dragX: 0,
- translateX: nextPosition,
- immediate: false,
- config: {
- velocity: vx,
- },
- })
- }
- }
- })
- useEffect(() => {
- set({ translateX: offset })
- }, [offset, set])
- // attach the bind() function to our container div to listen for gesture events
- // we will now include our drag values in the interpolate() function below
- return (
- <animated.div
- {...bind()}
- style={{
- border: 'thin solid blue',
- position: 'relative',
- height: '100%',
- width: '100%',
- transform: interpolate(
- [translateX, dragX],
- (translateX, dragX) => `translateX(calc(${translateX}% + ${dragX}px))`,
- ),
- }}>
- {React.Children.map(children, (element, index) => {
- return (
- <div
- style={{
- ...absoluteFill,
- transform: `translateX(${index * 100}%)`,
- }}>
- {element}
- </div>
- )
- })}
- </animated.div>
- )
- }
- const absoluteFill = {
- position: 'absolute',
- left: 0,
- right: 0,
- bottom: 0,
- top: 0,
- }
- // this will represent a consumer component or any part of your application
- function App() {
- // all we need to pass are children and an activeIndex prop to our pager component
- const [activeIndex, setActiveIndex] = useState(3)
- function handleChange(index) {
- setActiveIndex(index)
- }
- const children = Array.from({ length: 10 }).map((c, i) => (
- <h1 key={i} style={{ textAlign: 'center' }}>
- Index {i}
- </h1>
- ))
- return (
- <div style={{ overflow: 'hidden' }}>
- <div
- style={{
- width: PAGE_SIZE,
- height: PAGE_SIZE,
- display: 'flex',
- margin: 'auto',
- padding: '5px',
- border: 'thin solid red',
- }}>
- <Pager activeIndex={activeIndex} onChange={handleChange}>{children}</Pager>
- </div>
- <div
- style={{
- display: 'flex',
- flexDirection: 'column',
- alignItems: 'center',
- justifyContent: 'space-around',
- }}>
- <strong style={{ margin: '5px 0' }}>activeIndex: {activeIndex}</strong>
- <button
- style={{ margin: '5px 0' }}
- onClick={() => setActiveIndex(activeIndex + 1)}>
- Increment
- </button>
- <button
- style={{ margin: '5px 0' }}
- onClick={() => setActiveIndex(activeIndex - 1)}>
- Decrement
- </button>
- </div>
- </div>
- )
- }
- export default App
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement