Skip to content

Instantly share code, notes, and snippets.

@tkh44
Last active September 13, 2022 01:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tkh44/33031c4d8f29f7d5ec2c25430f82bc2a to your computer and use it in GitHub Desktop.
Save tkh44/33031c4d8f29f7d5ec2c25430f82bc2a to your computer and use it in GitHub Desktop.
Fake shared element transitions. The height/width on lines 97 & 98 need to be calculated, not static values.
import React from 'react'
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom'
import { Motion } from 'data-driven-motion'
const STIFF_SPRING = { stiffness: 420, damping: 42, precision: 0.1 }
const BOUNCY_SPRING = { stiffness: 180, damping: 21, precision: 0.1 }
const IMAGES = [
{ id: 0, title: 'Dark Orchid', color: 'DarkOrchid' },
{ id: 1, title: 'Lime Green', color: 'LimeGreen' },
{ id: 2, title: 'Tomato', color: 'Tomato' },
{ id: 3, title: 'Seven Ate Nine', color: '#789' },
{ id: 4, title: 'Crimson', color: 'Crimson' }
]
const Thumbnail = ({ color }) => <div style={{ width: 50, height: 50, background: color }} />
const Image = ({ color }) => <div style={{ width: '100%', height: 400, background: color }} />
const Home = () => (
<div>
<Link to='/gallery'>Visit the Gallery</Link>
<h2>Featured Images</h2>
<ul>
<li><Link to='/img/2'>Tomato</Link></li>
<li><Link to='/img/4'>Crimson</Link></li>
</ul>
</div>
)
const Gallery = ({ onLinkClick }) => (
<div>
{IMAGES.map(i => (
<Link
key={i.id}
to={{ pathname: `/img/${i.id}`, state: { modal: true } }}
onClick={onLinkClick}
>
<Thumbnail color={i.color} />
<p>{i.title}</p>
</Link>
))}
</div>
)
const ImageView = ({ match }) => {
const image = IMAGES[parseInt(match, 10)]
if (!image) {
return <div>Image not found</div>
}
return (
<div>
<h1>{image.title}</h1>
<Image color={image.color} />
</div>
)
}
const Modal = ({ match, history, start }) => {
const image = match ? IMAGES[parseInt(match.params.id, 10)] : null
console.log(start)
// if (!image) {
// return null
// }
const back = e => {
e.stopPropagation()
history.goBack()
}
return (
<Motion
data={image ? [image] : []}
component={
(
<div
onClick={back}
style={{
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
background: image ? 'rgba(0, 0, 0, 0.15)' : 'transparent',
pointerEvents: image ? 'initial' : 'none'
}}
/>
)
}
getKey={({ title }) => title}
onComponentMount={() => Object.assign({ opacity: 0 }, start)}
onRender={(data, i, spring) => ({
top: spring(25, STIFF_SPRING),
left: spring(25, STIFF_SPRING),
right: spring(25, STIFF_SPRING),
bottom: spring(25, BOUNCY_SPRING),
height: spring(660 - 50, STIFF_SPRING),
width: spring(400 - 50, STIFF_SPRING),
opacity: spring(1, STIFF_SPRING)
})}
onRemount={() => {
return Object.assign({ opacity: 0 }, start)
}}
onUnmount={(config, spring) => {
return {
top: spring(start.top, STIFF_SPRING),
left: spring(start.left, STIFF_SPRING),
right: spring(start.right, STIFF_SPRING),
bottom: spring(start.bottom, BOUNCY_SPRING),
height: spring(start.height, STIFF_SPRING),
width: spring(start.width, STIFF_SPRING),
opacity: spring(0, STIFF_SPRING)
}
}}
render={(key, img, style) => {
return (
<div
key={key}
className='modal'
style={{
position: 'absolute',
overflow: 'hidden',
display: 'flex',
justifyContent: 'space-around',
alignItems: 'center',
flexDirection: 'column',
background: img.color,
height: style.height,
width: style.width,
top: style.top,
left: style.left,
right: style.right,
opacity: style.opacity,
// bottom: style.bottom,
padding: 0,
border: '2px solid',
borderColor: img.color
}}
>
<h1
style={{
color: 'white',
fontWeight: 'bold'
}}
>{img.title}</h1>
<button type='button' onClick={back}>
Close
</button>
</div>
)
}}
/>
)
}
class ModalSwitch extends React.Component {
constructor (props, context) {
super(props, context)
this.state = {
clickedLinkPos: { top: 0, right: 0, left: 0, bottom: 0, height: 0, width: 0 }
}
}
componentWillMount () {
// set the initial previousLocation value on mount
this.previousLocation = this.props.location
}
componentWillUpdate (nextProps) {
const { location } = this.props
// set previousLocation if props.location is not modal
if (nextProps.history.action !== 'POP' && (!location.state || !location.state.modal)) {
this.previousLocation = this.props.location
}
}
render () {
const { location } = this.props
const isModal = !!(location.state &&
location.state.modal &&
this.previousLocation !== location)
return (
<div>
<Switch location={isModal ? this.previousLocation : location}>
<Route exact path='/' component={Home} />
<Route
path='/gallery'
render={props => {
return (
<Gallery
{...props}
onLinkClick={e => {
let target = e.target.tagName === 'A' ? e.target : e.target.parentNode
const top = target.offsetTop
const left = target.offsetLeft
const width = target.offsetWidth
const height = target.offsetHeight
this.setState({
clickedLinkPos: {
top: top,
right: left + width,
left: left,
bottom: 661 - top + height,
height: height,
width: width
}
})
}}
/>
)
}}
/>
<Route path='/img/:id' component={ImageView} />
</Switch>
<Route
path='/img/:id'
children={props => {
console.log(props)
return <Modal start={this.state.clickedLinkPos} {...props} />
}}
/>
</div>
)
}
}
const ModalGallery = () => (
<Router>
<Route component={ModalSwitch} />
</Router>
)
export default ModalGallery
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment