Skip to content

Instantly share code, notes, and snippets.

@andykent
Last active August 16, 2020 20:47
Show Gist options
  • Save andykent/2574cf9e50a014a7a64ea0ee4b88ab3b to your computer and use it in GitHub Desktop.
Save andykent/2574cf9e50a014a7a64ea0ee4b88ab3b to your computer and use it in GitHub Desktop.
Render Medium style blurred preview images using react-imgix
.aspectWrapper {
position: absolute;
width: 100%;
height: 100%;
}
.wrapper {
position: absolute;
width: 100%;
height: 100%;
}
.container {
position: relative;
overflow: hidden;
width: 100%;
height: 100%;
& img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}
.preview {
z-index: 1;
opacity: 1;
width: 100%;
height: 100%;
transition: opacity 500ms;
background-position: center center;
}
.previewLoaded {
z-index: 1;
opacity: 1;
width: 100%;
height: 100%;
transition: opacity 500ms;
}
.loaded {
& .preview {
opacity: 0;
}
& .full {
opacity: 1;
}
}
.full {
opacity: 1;
z-index: 2;
transition: opacity 300ms;
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
background-position: center center;
}
import React, { Component, PropTypes } from 'react'
import classes from './FastImg.css'
import classNames from 'classnames'
import Imgix from 'react-imgix'
import FluidImgix from 'FluidImgix'
export class FastImgix extends Component {
static propTypes = {
src: PropTypes.string.isRequired,
className: PropTypes.string,
aspect: PropTypes.number,
useFluid: PropTypes.bool,
previewSrc: PropTypes.string,
onLoad: PropTypes.func,
bg: PropTypes.bool
};
constructor (props) {
super(props)
let preloaded = props.previewSrc != null
this.state = {loaded: false, preloaded, mounted: false}
}
render () {
const { className, aspect } = this.props
const loadStateClasses = {
[classes.loaded]: this.state.loaded,
[classes.preloaded]: this.state.preloaded
}
let styles = {}
if (aspect) styles = { position: 'relative', height: 0, paddingBottom: `${aspect * 100}%` }
return (
<div className={classNames(classes.aspectWrapper, className)} style={styles}>
<div className={classes.wrapper}>
<div className={classNames(classes.container, loadStateClasses)}>
{this.renderPreviewImage()}
{this.renderFullImage()}
</div>
</div>
</div>
)
}
componentDidMount () {
this.setState({mounted: true})
}
renderPreviewImage () {
const { src, previewSrc, bg } = this.props
const previewImageProps = { onLoad: this.onPreviewLoad }
if (previewSrc) {
return <img className={classes.previewLoaded} {...previewImageProps} src={previewSrc} />
} else {
return <Imgix src={src} className={classes.preview} bg={bg} width={10} height={10} fit='clip' faces={false} generateSrcSet={false} customParams={{q: 10, blur: 2000, bri: 25, sat: 25}} imgProps={previewImageProps} aggressiveLoad />
}
}
renderFullImage () {
if (!this.state.mounted) return null
const fullImageProps = { onLoad: this.onFullLoad }
const { src, aspect, useFluid } = this.props
const fit = aspect ? 'clip' : 'crop'
const auto = 'compress'
if (useFluid) {
return <FluidImgix src={src} fit='clip' height={1} className={classes.full} faces={false} {...this.options} imgProps={fullImageProps} customParams={{auto}} />
} else {
return <Imgix src={src} fit={fit} className={classes.full} onLoad={this.onFullLoad} faces={false} {...this.options} imgProps={fullImageProps} customParams={{auto}} />
}
}
get options () {
let o = {...this.props}
delete o.src
delete o.className
delete o.aspect
delete o.useFluid
return o
}
onPreviewLoad = () => this.setState({preloaded: true})
onFullLoad = (e) => {
const { onLoad } = this.props
this.setState({loaded: true, preloaded: false})
if (onLoad) onLoad(e.target)
}
}
import Imgix from 'react-imgix'
import ReactDOM from 'react-dom'
// sizes image based on width of parent container
export default class FluidImgix extends Imgix {
forceLayout = () => {
const node = ReactDOM.findDOMNode(this)
const parent = node.parentNode
this.setState({
width: parent.scrollWidth,
height: 1,
mounted: true
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment