Skip to content

Instantly share code, notes, and snippets.

@smhutch
Created August 25, 2017 20:09
Show Gist options
  • Save smhutch/1f6eb72ed4f1574f7de92f837aa60918 to your computer and use it in GitHub Desktop.
Save smhutch/1f6eb72ed4f1574f7de92f837aa60918 to your computer and use it in GitHub Desktop.
Animated Background Effect
// React Component to render animated background on https://www.smhutch.co.uk
import React from 'react'
import Chance from 'chance'
export default class extends React.Component {
constructor(props) {
super(props);
this.state = {
showCanvas: true,
playAnimation: true
}
this.createBackgroundPattern = this.createBackgroundPattern.bind(this);
this.handleResize = this.handleResize.bind(this);
}
componentDidMount() {
this.createBackgroundPattern();
window.addEventListener('resize', this.handleResize);
}
componentWillUnmount() {
window.removeEventListener('resize', this.handleResize);
}
handleResize = () => {
if (this.state.showCanvas === true) {
this.setState({showCanvas:false});
setTimeout(() => {
this.setState({showCanvas:true});
this.createBackgroundPattern();
}, 400);
}
}
createBackgroundPattern = () => {
// get height of canvas
const w = window.innerWidth;
const h = window.innerHeight;
// get reference to canvas
const canvas = this.refs.canvas;
// If canvas reference is set
if (canvas) {
let ctx = canvas.getContext('2d');
// scale up canvas for retina screens
ctx.scale(2,2);
// canvas varaibles;
canvas.width=w;
canvas.height=h;
canvas.style.width= (w) + "px";
canvas.style.height= (h) + "px";
// colours to be used for elements
const colors = [
"#7f9eb2",
"#77919d",
"#dae9f4",
"#274c5e"
]
// Set initial values for elements randomly
let initialElements = [];
for(let i = 0; i < w/40; i++) {
const size = chance.integer({min:4, max:8});
const x = chance.integer({min:size * 2, max:w});
const y = chance.integer({min:size * 2, max:h});
const shape = {
x,
y,
size,
col: colors[chance.integer({
min:0,
max:(colors.length-1)
})],
opacity: chance.floating({min: 0.2, max: 0.6, fixed:2}),
rotation: chance.floating({min: 0.1, max:0.9}),
moveX: chance.floating({min:-0.5, max:0.5}),
moveY: chance.floating({min:-0.5, max:0.5}),
}
initialElements.push(shape);
}
let elements = [];
let lastAnimationTime = 0;
const animate = () => {
if (this.state.playAnimation) {
ctx.clearRect(0, 0, w, h);
let currAnimationTime = new Date();
let delta = 0;
if (lastAnimationTime === 0) {
elements = initialElements;
delta = currAnimationTime - new Date();
} else {
delta = currAnimationTime - lastAnimationTime;
}
lastAnimationTime = currAnimationTime;
elements.forEach(e => {
let center = e.size/2;
e.rotation = e.rotation + (delta * 0.2);
e.x += e.moveX;
e.y += e.moveY;
// If new position overflows container
if (e.x <= center) {e.moveX = e.moveX + 0.5;}
if (e.y <= center) {e.moveY = e.moveY + 0.5;}
if (e.x > w-e.size) {e.moveX = e.moveX - 0.5;}
if (e.y >= h-e.size) {e.moveY = e.moveY - 0.5;}
let translateX = e.x + center;
let translateY = e.y + center;
let undoX = 0 - translateX;
let undoY = 0 - translateY;
let radian = (Math.PI / 180) * e.rotation;
let reverseRadian = (Math.PI / 180) * (0 - e.rotation);
// set attributes
ctx.globalAlpha = e.opacity;
ctx.strokeStyle = e.col;
// translate to center of rectangle
ctx.translate(translateX,translateY);
// rotate to correct angle
ctx.rotate(radian);
// translate back to corner
ctx.translate(undoX,undoY);
// draw the rectangle
ctx.strokeRect(e.x,e.y,e.size,e.size);
// Undo angle
ctx.translate(translateX,translateY);
ctx.rotate(reverseRadian);
ctx.translate(undoX,undoY);
})
window.requestAnimationFrame(animate);
}
}
animate(this);
}
}
render () {
return <div>
<canvas
ref="canvas"
height="100vh"
width="100vw"
className={this.state.showCanvas ? '' : 'hide'}/>
<style jsx>{`
canvas {
position:absolute;
top:0;
left:0;
opacity:1;
z-index:-2;
}
.hide {opacity:0;}
`}</style>
</div>
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment