Created
September 25, 2017 18:47
-
-
Save bobvanderlinden/8422f92730afb8d6c29d635c6330ba53 to your computer and use it in GitHub Desktop.
FrameLayout React component: scale content while maintaining aspect ratio
This file contains hidden or 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
| .framelayout { | |
| margin: 0; | |
| padding: 0; | |
| border: 0; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .framelayout > * { | |
| position: absolute; | |
| } |
This file contains hidden or 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 ReactResizeDetector from 'react-resize-detector' | |
| import './FrameLayout.css' | |
| interface FrameLayoutProps { | |
| inheritParentSize?: boolean, | |
| aspectRatio?: number, | |
| children?: any, | |
| [key: string]: any | |
| } | |
| export class FrameLayout extends React.Component<FrameLayoutProps, {}> { | |
| ref: HTMLElement | |
| parentWidth: number | |
| parentHeight: number | |
| previousUpdate: { | |
| width: number, | |
| height: number, | |
| child: HTMLElement | |
| } | null = null | |
| constructor () { | |
| super() | |
| this.handleResize = this.handleResize.bind(this) | |
| this.handleRef = this.handleRef.bind(this) | |
| } | |
| get child (): HTMLElement { | |
| const ref = this.ref | |
| return ref && (ref.children[ref.children.length - 1] as HTMLElement) | |
| } | |
| handleRef (ref) { | |
| this.ref = ref | |
| this.updateSize() | |
| } | |
| handleResize (width, height) { | |
| this.parentWidth = width | |
| this.parentHeight = height | |
| this.updateSize() | |
| } | |
| updateSize () { | |
| const child = this.child | |
| const parentWidth = this.parentWidth | |
| const parentHeight = this.parentHeight | |
| const canUpdateSize = child && (parentWidth || parentHeight) | |
| if (!canUpdateSize) { | |
| return | |
| } | |
| const needsUpdate = !this.previousUpdate || ( | |
| this.previousUpdate.child !== child || | |
| this.previousUpdate.width !== parentWidth || | |
| this.previousUpdate.height !== parentHeight | |
| ) | |
| if (!needsUpdate) { | |
| return | |
| } | |
| this.previousUpdate = { | |
| child: child, | |
| width: parentWidth, | |
| height: parentHeight | |
| } | |
| // We can presume the parentNode is actually a <div> | |
| const desiredAspectRatio = this.props.aspectRatio || 1 | |
| const parentAspectRatio = parentWidth / parentHeight | |
| const isWiderThanParent = parentAspectRatio > desiredAspectRatio | |
| const newWidth = isWiderThanParent | |
| ? parentHeight * desiredAspectRatio | |
| : parentWidth | |
| const newHeight = isWiderThanParent | |
| ? parentHeight | |
| : parentWidth / desiredAspectRatio | |
| child.style.width = `${newWidth}px` | |
| child.style.height = `${newHeight}px` | |
| } | |
| componentDidUpdate () { | |
| this.updateSize() | |
| } | |
| render () { | |
| const { | |
| inheritParentSize = false, | |
| aspectRatio, | |
| children, | |
| ...attrs | |
| } = this.props | |
| return ( | |
| <div | |
| {...attrs} | |
| ref={this.handleRef} | |
| className={[ | |
| 'framelayout', | |
| inheritParentSize && 'inherit-parent-size', | |
| aspectRatio && 'fixed-aspect-ratio', | |
| attrs.className | |
| ].filter(Boolean).join(' ')}> | |
| <ReactResizeDetector handleWidth handleHeight onResize={this.handleResize} /> | |
| {children} | |
| </div> | |
| ) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment