Files
Iris/src/js/components/Parallax.js

200 lines
5.4 KiB
JavaScript
Raw Normal View History

2016-10-25 17:01:37 +13:00
import React, { PropTypes } from 'react'
export default class Parallax extends React.Component{
constructor(props) {
super(props);
2016-11-28 08:24:01 +13:00
this._loading = false
2016-11-28 09:07:18 +13:00
this._loaded = false
2016-11-28 08:24:01 +13:00
2016-10-25 17:01:37 +13:00
this.state = {
2016-10-25 21:36:33 +13:00
scrollTop: 0,
windowWidth: 0,
windowHeight: 0,
canvas: {
width: 0,
height: 0
},
2016-10-26 08:51:28 +13:00
image: {},
2016-11-28 08:24:01 +13:00
loading: false,
url: false
2016-10-25 17:01:37 +13:00
}
2016-10-26 16:55:47 +13:00
// we need to manually bind this as eventListener doesn't work with anonymous functions
this.handleResize = this.handleResize.bind(this);
this.handleScroll = this.handleScroll.bind(this);
2016-10-25 17:01:37 +13:00
}
componentDidMount(){
2016-11-28 08:24:01 +13:00
this._mounted = true
2016-10-26 16:55:47 +13:00
window.addEventListener("resize", this.handleResize);
window.addEventListener("scroll", this.handleScroll);
2017-07-24 09:09:13 +12:00
if (this.props.image){
this._loading = true
this.setState({ url: this.props.image, image: false, loading: true })
this.loadImage( this.props.image )
.then(
response => {
if( this._mounted ){
this._loading = false
this._loaded = true
this.setState({ url: this.props.image, image: response, loading: false })
this.updateCanvas( response )
}
}
)
2017-07-24 09:09:13 +12:00
} else {
this._loaded = true
this.state.image = false
this.updateCanvas()
}
2016-10-25 21:36:33 +13:00
}
componentWillUnmount(){
2016-11-28 08:24:01 +13:00
this._mounted = false
2016-10-26 16:55:47 +13:00
window.removeEventListener("resize", this.handleResize);
window.removeEventListener("scroll", this.handleScroll);
2016-10-26 16:55:47 +13:00
}
2016-11-10 08:08:03 +13:00
componentWillReceiveProps( nextProps ){
if( ( !this.state.url || nextProps.image != this.state.url ) && !this._loading && nextProps.image ){
2016-11-28 08:24:01 +13:00
this._loading = true
this.setState({ url: nextProps.image, image: false, loading: true })
this.loadImage( nextProps.image )
2016-11-23 13:17:07 +13:00
.then(
response => {
2016-11-28 08:24:01 +13:00
if( this._mounted ){
this._loading = false
2016-11-28 09:07:18 +13:00
this._loaded = true
2016-11-28 08:24:01 +13:00
this.setState({ url: nextProps.image, image: response, loading: false })
2017-07-24 09:09:13 +12:00
this.updateCanvas(response)
2016-11-28 08:24:01 +13:00
}
2016-11-23 13:17:07 +13:00
}
)
}
2016-11-10 08:08:03 +13:00
}
handleResize(e){
2017-07-24 09:09:13 +12:00
if (this._loaded){
this.updateCanvas(this.state.image)
2016-11-28 09:07:18 +13:00
}
2016-10-26 16:55:47 +13:00
}
handleScroll(e){
2016-11-06 15:47:37 +13:00
// this DOES work, but is in no way high-performing and only on Firefox
2017-07-24 09:09:13 +12:00
if (this._loaded){
2016-11-28 09:07:18 +13:00
this.setState(
{ scrollTop: window.scrollY },
2017-07-24 09:09:13 +12:00
this.updateCanvas(this.state.image)
2016-11-28 09:07:18 +13:00
)
}
}
2017-07-24 09:09:13 +12:00
loadImage(url){
2016-10-25 21:36:33 +13:00
return new Promise( (resolve, reject) => {
var imageObject = new Image()
imageObject.src = url
imageObject.crossOrigin = 'anonymous'
2016-10-25 21:36:33 +13:00
imageObject.onload = function(){
2016-10-26 08:51:28 +13:00
var image = {
width: imageObject.naturalWidth,
height: imageObject.naturalHeight,
original_width: imageObject.naturalWidth,
original_height: imageObject.naturalHeight,
object: imageObject
}
resolve( image )
2016-10-25 21:36:33 +13:00
}
2016-10-25 17:01:37 +13:00
})
2016-10-25 21:36:33 +13:00
}
2017-07-24 09:09:13 +12:00
updateCanvas(image = false){
console.log(image)
2016-10-25 21:36:33 +13:00
var canvasWidth = $('.parallax').outerWidth();
var canvasHeight = $('.parallax').outerHeight();
2017-07-24 09:09:13 +12:00
if (this.state.canvas.width != canvasWidth || this.state.canvas.height != canvasHeight){
2016-10-25 21:36:33 +13:00
this.setState({
canvas: {
width: canvasWidth,
height: canvasHeight
}
})
}
2016-10-26 08:51:28 +13:00
this.renderCanvas( image );
2016-10-25 17:01:37 +13:00
}
2017-07-24 09:09:13 +12:00
renderCanvas(image = false){
let self = this;
var canvasDOM = document.getElementById('parallax-canvas')
var context = canvasDOM.getContext('2d')
2017-07-24 09:09:13 +12:00
// Fill the background with mid-gret
context.beginPath()
context.rect(0, 0, this.state.canvas.width, this.state.canvas.height)
context.fillStyle = "#a3a19f"
context.fill()
2016-10-25 21:36:33 +13:00
2017-07-24 09:09:13 +12:00
if (image){
2016-10-25 21:36:33 +13:00
2017-07-24 09:09:13 +12:00
// zoom image to fill canvas, widthwise
if( image.width < this.state.canvas.width || image.width > this.state.canvas.width ){
var scale = this.state.canvas.width / image.width;
image.width = image.width * scale;
image.height = image.height * scale;
}
// now check for fill heightwise, and zoom in if necessary
if( image.height < this.state.canvas.height ){
var scale = this.state.canvas.height / image.height;
image.width = image.width * scale;
image.height = image.height * scale;
}
// figure out where we want the image to be, based on scroll position
var percent = Math.round( self.state.scrollTop / this.state.canvas.height * 100 );
var position = Math.round( (this.state.canvas.height / 2) * (percent/100) ) - 100;
image.x = ( this.state.canvas.width / 2 ) - ( image.width / 2 );
image.y = ( ( this.state.canvas.height / 2 ) - ( image.height / 2 ) ) + ( ( percent / 100 ) * 100);
// Actually draw the image on the canvas
context.drawImage(image.object, image.x, image.y, image.width, image.height)
// now update our component
self.setState({ image: image });
2016-10-25 21:36:33 +13:00
}
2017-07-24 09:09:13 +12:00
// Construct a gradient overlay
context.rect(0, 0, this.state.canvas.width, this.state.canvas.height)
let gradient = context.createLinearGradient(0, 0, 0, this.state.canvas.height)
gradient.addColorStop(0, 'rgba(38,39,40,0)')
gradient.addColorStop(0.9, 'rgba(38,39,40,1)')
context.fillStyle = gradient
2016-10-25 21:36:33 +13:00
2017-07-24 09:09:13 +12:00
// And now drop it into place
context.fill()
2016-10-25 17:01:37 +13:00
}
render(){
return (
<div className={this.props.blur ? "parallax blur" : "parallax"}>
2016-10-26 16:55:47 +13:00
<canvas
id="parallax-canvas"
className={!this.state.loading ? 'loaded' : null}
2016-10-26 16:55:47 +13:00
width={this.state.canvas.width}
height={this.state.canvas.height}>
</canvas>
2016-10-25 17:01:37 +13:00
</div>
);
}
}