Last active
September 13, 2022 01:30
-
-
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.
This file contains 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
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