Skip to content

Instantly share code, notes, and snippets.

@djD-REK
Created August 23, 2019 03:14
Show Gist options
  • Select an option

  • Save djD-REK/cf55c952315460a7e9b13bd1ec4c011b to your computer and use it in GitHub Desktop.

Select an option

Save djD-REK/cf55c952315460a7e9b13bd1ec4c011b to your computer and use it in GitHub Desktop.
// 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