Last active
December 19, 2017 14:44
-
-
Save jaredpalmer/92adbbd499713d016994547a2e023e4e to your computer and use it in GitHub Desktop.
Vjeux's 2013 Viewport Image Resizer as a Render Prop
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 * as React from 'react'; | |
import { throttle } from 'common/utils/throttle'; | |
export interface ScaleToViewportProps { | |
height: number; | |
width: number; | |
verticalPadding: number; | |
horizontalPadding: number; | |
throttle: number; | |
render: (props: ScaleToViewportState) => React.ReactNode; | |
} | |
export interface ScaleToViewportState { | |
height?: number; | |
width?: number; | |
} | |
export class ScaleToViewport extends React.Component< | |
ScaleToViewportProps, | |
ScaleToViewportState | |
> { | |
state: ScaleToViewportState = {}; | |
componentDidMount() { | |
window.addEventListener('resize', this.throttledResize); | |
this.sizePhoto(null, this.props); | |
} | |
componentWillUnmount() { | |
window.removeEventListener('resize', this.throttledResize); | |
} | |
componentWillReceiveProps( | |
nextProps: ScaleToViewportProps, | |
nextState: ScaleToViewportState | |
) { | |
if ( | |
nextProps.height !== this.props.height || | |
nextProps.width !== this.props.width | |
) { | |
this.setState({ height: undefined, width: undefined }); | |
this.sizePhoto(null, nextProps); | |
} | |
} | |
/** | |
* Resize photo using @Vjeux's / Facebook's 2013 technique | |
* @see http://blog.vjeux.com/2013/image/css-container-and-cover.html | |
*/ | |
sizePhoto = (e: any, props?: ScaleToViewportProps) => { | |
const { height, width, verticalPadding, horizontalPadding } = | |
props || this.props; | |
// We want to offset by the height of the Navbar / Appbar | |
const viewportHeight = window.innerHeight - verticalPadding; | |
const viewportWidth = window.innerWidth - horizontalPadding; | |
// Aspect ratio of the viewport | |
const viewportRatio = viewportWidth / viewportHeight; | |
// Aspect ratio of the image | |
const imageRatio = width / height; | |
let imageHeight, imageWidth; | |
// calculated sizes | |
if (imageRatio <= viewportRatio) { | |
imageWidth = Math.min(viewportHeight * imageRatio, width); | |
imageHeight = Math.min(viewportHeight, height); | |
} else { | |
imageWidth = Math.min(viewportWidth, width); | |
imageHeight = Math.min(viewportWidth / imageRatio, height); | |
} | |
this.setState({ width: imageWidth, height: imageHeight }); | |
}; | |
throttledResize = throttle(this.sizePhoto, this.props.throttle); | |
render() { | |
return this.state.height && this.state.width | |
? (this.props.render as any)(this.state) | |
: null; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment