Skip to content

Instantly share code, notes, and snippets.

@zeh
Created March 10, 2017 15:05
Show Gist options
  • Save zeh/5b3b7ca435b5de3babeebff359924d39 to your computer and use it in GitHub Desktop.
Save zeh/5b3b7ca435b5de3babeebff359924d39 to your computer and use it in GitHub Desktop.
import * as React from "react";
import * as ReactDOM from "react-dom";
interface IProps {
className?: string;
ratio: number;
style?: Object;
type?: ScaleType;
}
interface IState {
dimensionsKnown: boolean;
maxWidth: number;
maxHeight: number;
}
export enum ScaleType {
Inside,
Cover,
Width,
Height,
}
class AspectRatioDiv extends React.Component<IProps, IState> {
private _reset:(e:UIEvent) => void;
private _mainComponent:HTMLDivElement;
constructor(props:IProps) {
super(props);
this.state = this.getInitialStateInternal();
this._reset = this.reset.bind(this);
}
public componentDidMount() {
window.addEventListener("resize", this._reset);
this.requestReadWidth(null);
}
public componentWillUnmount() {
window.removeEventListener("resize", this._reset);
}
public render() {
let style = Object.assign({}, this.props.style || {});
if (!this.state.dimensionsKnown) {
// Parent dimensions not known yet
style = Object.assign(style, {
height: "100%",
width: "100%",
});
} else {
// Parent dimensions known
let type = !!this.props.type ? this.props.type : ScaleType.Inside;
let finalW = NaN;
let finalH = NaN;
let containerRatio = this.state.maxWidth / this.state.maxHeight;
if (type === ScaleType.Width ||
(type === ScaleType.Inside && containerRatio < this.props.ratio) ||
(type === ScaleType.Cover && containerRatio > this.props.ratio)) {
// Use width as basis
finalW = this.state.maxWidth;
finalH = Math.round(this.state.maxWidth / this.props.ratio);
} else {
// Use height as basis
finalW = Math.round(this.state.maxHeight * this.props.ratio);
finalH = this.state.maxHeight;
}
style = Object.assign(style, {
height: finalH + "px",
width: finalW + "px",
});
}
return (
<div
className={ this.props.className }
style={ style }
ref={ (r) => this._mainComponent = r } >
{ this.props.children }
</div>
);
}
private reset(e:UIEvent) {
this.setState(this.getInitialStateInternal());
this.readWidth(e);
}
private requestReadWidth(e:UIEvent|null) {
window.requestAnimationFrame(() => this.readWidth(e));
}
private readWidth(e:UIEvent|null) {
// Read the current element width, and if different, resize it
if (!this.state.dimensionsKnown && this._mainComponent) {
let element = ReactDOM.findDOMNode(this._mainComponent);
let w = element.clientWidth;
let h = element.clientHeight;
if (w !== this.state.maxWidth || h !== this.state.maxHeight) {
this.setState({
dimensionsKnown: true,
maxHeight: h,
maxWidth: w,
});
// Re-check if needed
this.requestReadWidth(e);
}
}
}
private getInitialStateInternal():IState {
return {
dimensionsKnown: false,
maxHeight: 0,
maxWidth: 0,
};
}
}
export default AspectRatioDiv;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment