Created
January 21, 2016 07:08
-
-
Save alehlopeh/467cf5cfe77a5e4d27f0 to your computer and use it in GitHub Desktop.
Canvas Grid lines animation
This file contains 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
<canvas class="canvas-bg js-grid-canvas"> |
This file contains 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
/* GLOBAL */ | |
const requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; | |
/* Robert Penners easing algorithms | |
* http://www.gizma.com/easing/ | |
* | |
* t: current time | |
* b: start value | |
* c: total change in value | |
* d: duration | |
*/ | |
window.easing = { | |
easeInSine: function (t, b, c, d) { | |
'use strict'; | |
return -c * Math.cos(t/d * (Math.PI/2)) + c + b; | |
}, | |
easeOutSine: function (t, b, c, d) { | |
'use strict'; | |
return c * Math.sin(t/d * (Math.PI/2)) + b; | |
}, | |
easeInOutSine: function (t, b, c, d) { | |
'use strict'; | |
return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; | |
}, | |
easeInQuad: function (t, b, c, d) { | |
'use strict'; | |
t /= d; | |
return c*t*t + b; | |
}, | |
easeOutQuad: function (t, b, c, d) { | |
'use strict'; | |
t /= d; | |
return -c * t*(t-2) + b; | |
}, | |
easeInOutQuad: function (t, b, c, d) { | |
'use strict'; | |
t /= d/2; | |
if (t < 1) { return c/2*t*t + b; } | |
t--; | |
return -c/2 * (t*(t-2) - 1) + b; | |
}, | |
easeInOutQuart: function (t, b, c, d) { | |
'use strict'; | |
t /= d/2; | |
if (t < 1) { return c/2*t*t*t*t + b; } | |
t -= 2; | |
return -c/2 * (t*t*t*t - 2) + b; | |
}, | |
easeInQuart: function (t, b, c, d) { | |
t /= d; | |
return c*t*t*t*t + b; | |
}, | |
easeOutQuart: function (t, b, c, d) { | |
t /= d; | |
t--; | |
return -c * (t*t*t*t - 1) + b; | |
}, | |
easeOutQuint: function (t, b, c, d) { | |
t /= d; | |
t--; | |
return c*(t*t*t*t*t + 1) + b; | |
}, | |
easeInOutQuint: function (t, b, c, d) { | |
t /= d/2; | |
if (t < 1) return c/2*t*t*t*t*t + b; | |
t -= 2; | |
return c/2*(t*t*t*t*t + 2) + b; | |
} | |
}; | |
class Animatable { | |
animateTo(properties, duration, delay, easing) { | |
this.animation = { | |
target: properties, | |
duration: duration, | |
delay: delay, | |
easing: easing | |
}; | |
} | |
_getAnimatedPropery(property, progress) { | |
if (this.animation && typeof this.animation.target[property] !== 'undefined') { | |
const delta = this.animation.target[property] - this[property]; | |
const duration = this.animation.duration + this.animation.delay; | |
let time = Math.max(0, duration * progress - this.animation.delay); | |
return this.animation.easing(time, this[property], delta, this.animation.duration); | |
} | |
return this[property]; | |
} | |
} | |
class GridLine extends Animatable { | |
constructor(ctx, canvas, x, y) { | |
super(); | |
this.ctx = ctx; | |
this.canvas = canvas; | |
this.x = x; | |
this.y = y; | |
} | |
draw(progress) { | |
this.ctx.beginPath(); | |
this.ctx.moveTo(this.x, this.y); | |
const x = this._getAnimatedPropery('x', progress); | |
const y = this._getAnimatedPropery('y', progress); | |
this.ctx.lineTo(x, y); | |
this.ctx.stroke(); | |
} | |
} | |
class GridDot extends Animatable { | |
constructor(ctx, canvas, x, y, size) { | |
super(); | |
this.ctx = ctx; | |
this.canvas = canvas; | |
this.x = x; | |
this.y = y; | |
this.size = size; | |
} | |
draw(progress) { | |
this.ctx.beginPath(); | |
this.ctx.moveTo(this.x, this.y); | |
const size = this._getAnimatedPropery('size', progress); | |
this.ctx.arc(this.x, this.y, size, 0, 2 * Math.PI); | |
this.ctx.fill(); | |
} | |
} | |
// Main animation class | |
class GridAnimation { | |
constructor(el, duration = 2500, size = 64) { | |
this.canvas = el; | |
this.ratio = 1; | |
this.size = size; | |
this.duration = duration; | |
this.dotSize = 1; | |
this.lineColor = 'rgba(50,80,150, .5)'; | |
this.dotColor = 'rgba(0, 173, 255, 1)'; | |
this.lineEasing = easing.easeInOutQuint; //easing.easeOutQuart; | |
this.dotEasing = easing.easeInOutQuad; | |
this.lines = []; | |
this.dots = []; | |
this.numberOfHorizontalLines = undefined; | |
this.numberOfVerticalLines = undefined; | |
this.global_width = undefined; | |
this.global_height = undefined; | |
this.ctx = this.canvas.getContext('2d'); | |
this._update = this._update.bind(this); | |
this._rescale = this._rescale.bind(this); | |
this._init(); | |
} | |
//////////////////////////////////////////////// | |
// PUBLIC API | |
//////////////////////////////////////////////// | |
// Play animation from beginning | |
play() { | |
this.canvas.classList.add('js-animate'); | |
if (this.ctx) { | |
console.log('GridAnimation.play()'); | |
this.startTime = new Date(); | |
this._draw(); | |
this._update(); | |
} | |
} | |
// Reset animation | |
reset() { | |
this._clearCanvas(); | |
this.canvas.classList.remove('js-animate'); | |
} | |
// Skip to end of animation and draw last frame | |
skipToEnd() { | |
this.canvas.classList.add('js-animate'); | |
if (this.ctx) { | |
this._draw(1); | |
} | |
} | |
//////////////////////////////////////////////// | |
// PRIVATE METHODS | |
//////////////////////////////////////////////// | |
_init() { | |
if (this.ctx) { | |
window.addEventListener('resize', this._rescale); | |
this._rescale(); | |
} | |
} | |
_clearCanvas() { | |
if (this.ctx) { | |
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
} | |
} | |
// return animation progress as value between 0 and 1 | |
_animationProgress() { | |
const elapsed = new Date() - this.startTime; | |
return Math.min(1, Math.max(0, elapsed / this.duration)); | |
} | |
/* Returns a random integer between min (inclusive) and max (inclusive) | |
* Using Math.round() will give you a non-uniform distribution! | |
*/ | |
_getRandomInt(min, max) { | |
return Math.floor(Math.random() * (max - min + 1)) + min; | |
} | |
_buildAnimation() { | |
this._buildLines(); | |
this._buildDots(); | |
} | |
_buildLines() { | |
// create Horizontal lines | |
const delayPerHLine = this.duration / this.numberOfHorizontalLines / 2; | |
for (let i=0; i < this.numberOfHorizontalLines; i++) { | |
let lineDelay = delayPerHLine * i; | |
let line = new GridLine(this.ctx, this.canvas, 0, this.size*i); | |
if (i % 2) { | |
line.animateTo({x: this.global_width}, this.duration - lineDelay, lineDelay, this.lineEasing); | |
} | |
else { | |
line.x = this.global_width; | |
line.animateTo({x: 0}, this.duration - lineDelay, lineDelay, this.lineEasing); | |
} | |
this.lines.push(line); | |
} | |
// create Vertical Lines | |
const delayPerVLine = this.duration / this.numberOfVerticalLines / 2; | |
for (let j=0; j < this.numberOfVerticalLines; j++) { | |
let lineDelay = delayPerVLine * j; | |
let line = new GridLine(this.ctx, this.canvas, this.size*j, 0); | |
if (j % 2) { | |
line.animateTo({y: this.global_height}, this.duration - lineDelay, lineDelay, this.lineEasing); | |
} | |
else { | |
line.y = this.global_height; | |
line.animateTo({y: 0}, this.duration - lineDelay, lineDelay, this.lineEasing); | |
} | |
this.lines.push(line); | |
} | |
} | |
_buildDots() { | |
const numberOfDots = this.numberOfHorizontalLines * this.numberOfVerticalLines; | |
const dotDuration = this.duration*0.3; | |
const dotMinDelay = this.duration*0.2; | |
const dotMaxDelay = this.duration*0.6; | |
for (let k=0; k < numberOfDots; k++) { | |
const dotDelay = this._getRandomInt(dotMinDelay, dotMaxDelay); | |
const x = this.size * (k % this.numberOfVerticalLines); | |
const y = this.size * Math.floor(k / this.numberOfVerticalLines); | |
const dot = new GridDot(this.ctx, this.canvas, x, y, 0); | |
dot.animateTo({size: this.dotSize}, dotDuration, dotDelay, this.dotEasing); | |
this.dots.push(dot); | |
} | |
} | |
_rescale() { | |
this.global_width = Math.min(window.innerWidth, window.screen.width); | |
this.global_height = Math.min(window.innerHeight, window.screen.height); | |
if (this.ctx.webkitBackingStorePixelRatio < 2) { | |
this.ratio = window.devicePixelRatio || 1; | |
} | |
this.canvas.setAttribute('width', this.global_width * this.ratio); | |
this.canvas.setAttribute('height', this.global_height * this.ratio); | |
this.lines = []; | |
this.dots = []; | |
this.numberOfHorizontalLines = Math.ceil(this.global_height / this.size); | |
this.numberOfVerticalLines = Math.ceil(this.global_width / this.size); | |
this._buildAnimation(); | |
this._draw(); | |
} | |
// Draw current frame of animation | |
_draw(progress = this._animationProgress()) { | |
this.ctx.save(); | |
// clear previous frame | |
this._clearCanvas(); | |
// scale up for retina | |
this.ctx.scale(this.ratio, this.ratio); | |
// straddle our lines to make them sharp: http://diveintohtml5.info/canvas.html#pixel-madness | |
this.ctx.translate(0.5, 0.5); | |
///////////////////////////////////// | |
// high-dpi drawing start | |
///////////////////////////////////// | |
// Draw lines | |
this.ctx.lineWidth = 1; | |
this.ctx.strokeStyle = this.lineColor; | |
for (let line of this.lines) { | |
line.draw(progress); | |
} | |
// Draw dots | |
this.ctx.fillStyle = this.dotColor; | |
for (let dot of this.dots) { | |
dot.draw(progress); | |
} | |
///////////////////////////////////// | |
// high-dpi drawing end | |
///////////////////////////////////// | |
this.ctx.restore(); | |
} | |
// Request animation Frame callback | |
_update() { | |
// only draw until animation has completed | |
if (this._animationProgress() < 1) { | |
requestAnimationFrame(this._update); | |
} | |
this._draw(); | |
} | |
} | |
const $el = $('.js-grid-canvas'); | |
const gridAnimation = new GridAnimation( $el[0] ); | |
gridAnimation.play(); | |
//gridAnimation.skipToEnd(); | |
This file contains 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
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> |
This file contains 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
// Quad | |
$easeInQuad = cubic-bezier(0.550, 0.085, 0.680, 0.530); | |
$easeOutQuad = cubic-bezier(0.250, 0.460, 0.450, 0.940); | |
$easeInOutQuad = cubic-bezier(0.455, 0.030, 0.515, 0.955); | |
* | |
box-sizing border-box | |
html | |
background #000 | |
color #fff | |
body | |
margin 0 | |
.canvas-bg | |
position: absolute; | |
top: 0; | |
left: 0; | |
opacity: 0; | |
transition: opacity 1s $easeInQuad; | |
&.js-animate | |
opacity: 1; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment