Skip to content

Instantly share code, notes, and snippets.

@JonathonAshworth
Last active April 18, 2018 05:18
Show Gist options
  • Save JonathonAshworth/5805d8c63be621841514642e8191b8cd to your computer and use it in GitHub Desktop.
Save JonathonAshworth/5805d8c63be621841514642e8191b8cd to your computer and use it in GitHub Desktop.
Container element that automatically animates height changes when children change size
import React from 'react'
import { string, object } from 'prop-types'
import styles from './AnimatedHeightContainer.css'
class AnimatedHeightContainer extends React.Component {
static propTypes = {
className: string,
style: object,
}
constructor () {
super()
this.el = null
this.manualUpdate = this.manualUpdate.bind(this)
}
componentDidMount () {
this.manualUpdate()
window.addEventListener('resize', this.manualUpdate)
}
componentWillUnmount () {
window.removeEventListener('resize', this.manualUpdate)
}
componentDidUpdate () {
this.manualUpdate()
}
async manualUpdate () {
const animationFrame = async () => new Promise(res => requestAnimationFrame(() => res()))
// Compute the height of the content
const originalHeight = this.el.style.height
this.el.style.height = 'auto'
const computedHeight = this.el.getBoundingClientRect().height
await animationFrame() // [0]
this.el.style.height = originalHeight
// Manually set the height to the computed height to force a css transition
// (this is needed since css won't transition to 'auto')
await animationFrame()
this.el.style.height = computedHeight + 'px'
// [0] If we have multiple AnimatedHeightContainer's nested, the ancestor's height
// calculation (above) is dependent on all it's decendant's computed heights
// (i.e height: auto) So we need to pause somehow until they've all been
// calculated.
// All the browsers seem to handle this timing stuff differently. setTimeout
// doesn't work half the time on Chrome, but requestAnimationFrame results in
// a 1-frame flash of bad animation on Safari. I've picked the lesser of two
// evils for now.
}
render () {
return (
<div
className={`${styles.container} ${this.props.className}`}
style={this.props.style}
ref={el => this.el = el}
>
{this.props.children}
</div>
)
}
}
export default AnimatedHeightContainer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment