Created
August 23, 2019 03:14
-
-
Save djD-REK/cf55c952315460a7e9b13bd1ec4c011b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Tarot Card Deck example by Dr. Derek Austin 🥳 (Github master) | |
| // Credit Paul Henckel for the original code | |
| // Source: https://codesandbox.io/s/j0y0vpz59?from-embed | |
| import { render } from 'react-dom' | |
| import React, { useState } from 'react' | |
| import { useSprings, animated, interpolate } from 'react-spring' | |
| import { useGesture } from 'react-use-gesture' | |
| import './styles.css' | |
| const tarotDeck = [ | |
| // Cards from https://en.wikipedia.org/wiki/Rider-Waite_tarot_deck | |
| // Authorship: Arthur Edward Waite, Pamela Coleman Smith was the artist and worked as an artist 'for hire.' | |
| // Waite was the copyright holder and he died in 1942. - These images scanned by Holly Voley | |
| ] | |
| // These two are just helpers, they curate spring data, values that are later being interpolated into css | |
| const to = i => ({ x: 0, y: i * -4, scale: 1, rot: -10 + Math.random() * 20, delay: i * 100 }) | |
| const from = i => ({ x: 0, rot: 0, scale: 1.5, y: -1000 }) | |
| // This is being used down there in the view, it interpolates rotation and scale into a css transform | |
| const trans = (r, s) => `perspective(1500px) rotateX(30deg) rotateY(${r / 10}deg) rotateZ(${r}deg) scale(${s})` | |
| /** | |
| * Randomize array element order in-place. | |
| * Using Durstenfeld shuffle algorithm. | |
| * Updated for EC6/ECMA2015 | |
| * source: https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array | |
| */ | |
| function shuffleArray(array) { | |
| for (let i = array.length - 1; i > 0; i--) { | |
| const j = Math.floor(Math.random() * (i + 1)) | |
| ;[array[i], array[j]] = [array[j], array[i]] | |
| } | |
| return array | |
| } | |
| function Deck() { | |
| const cards = shuffleArray(tarotDeck) | |
| const [gone] = useState(() => new Set()) // The set flags all the cards that are flicked out | |
| const [props, set] = useSprings(cards.length, i => ({ ...to(i), from: from(i) })) // Create a bunch of springs using the helpers above | |
| // Create a gesture, we're interested in down-state, delta (current-pos - click-pos), direction and velocity | |
| const bind = useGesture(({ args: [index], down, delta: [xDelta], distance, direction: [xDir], velocity }) => { | |
| const trigger = velocity > 0.2 // If you flick hard enough it should trigger the card to fly out | |
| const dir = xDir < 0 ? -1 : 1 // Direction should either point left or right | |
| if (!down && trigger) gone.add(index) // If button/finger's up and trigger velocity is reached, we flag the card ready to fly out | |
| set(i => { | |
| if (index !== i) return // We're only interested in changing spring-data for the current spring | |
| const isGone = gone.has(index) | |
| const x = isGone ? (200 + window.innerWidth) * dir : down ? xDelta : 0 // When a card is gone it flys out left or right, otherwise goes back to zero | |
| const rot = xDelta / 100 + (isGone ? dir * 10 * velocity : 0) // How much the card tilts, flicking it harder makes it rotate faster | |
| const scale = down ? 1.1 : 1 // Active cards lift up a bit | |
| return { x, rot, scale, delay: undefined, config: { friction: 50, tension: down ? 800 : isGone ? 200 : 500 } } | |
| }) | |
| if (!down && gone.size === cards.length) setTimeout(() => gone.clear() || set(i => to(i)), 600) | |
| }) | |
| // Now we're just mapping the animated values to our view, that's it. Btw, this component only renders once. :-) | |
| return props.map(({ x, y, rot, scale }, i) => ( | |
| <animated.div key={i} style={{ transform: interpolate([x, y], (x, y) => `translate3d(${x}px,${y}px,0)`) }}> | |
| { | |
| // This is the card itself, we're binding our gesture to it (and inject its index so we know which is which) | |
| } | |
| <animated.div {...bind(i)} style={{ transform: interpolate([rot, scale], trans), backgroundImage: `url(${cards[i]})` }} /> | |
| </animated.div> | |
| )) | |
| } | |
| render(<Deck />, document.getElementById('root')) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment