Amazing work from tsmallfield. See more work from this incredible developer here: http://jsdo.it/tsmallfield And check out his equally impressive UI designs here: http://dribbble.com/tsmallfield
A Pen by Justice Conder on CodePen.
Amazing work from tsmallfield. See more work from this incredible developer here: http://jsdo.it/tsmallfield And check out his equally impressive UI designs here: http://dribbble.com/tsmallfield
A Pen by Justice Conder on CodePen.
<div id="spacer"></div> | |
<div id="stage"> | |
<canvas></canvas> | |
</div> |
!function(win, doc) { | |
'use strict'; | |
///////////////////////////////////////////////// | |
var raf = win.requestAnimationFrame || | |
win.webkitRequestAnimationFrame || | |
win.mozRequestAnimationFrame || | |
win.msRequestAnimationFrame || | |
win.oRequestAnimationFrame || | |
function(func) { setTimeout(func, 1000 / 60); }; | |
///////////////////////////////////////////////// | |
var WIDTH = 465, | |
HEIGHT = 465, | |
ERROR = .03, | |
HEXAGON_SIZE = 15, | |
cvs = doc.querySelector('canvas'), | |
ctx = cvs.getContext('2d'), | |
hexagons = [], | |
curScrollRate = 0, | |
destScrollRate = 0, | |
frame = 0; | |
///////////////////////////////////////////////// | |
win.addEventListener('DOMContentLoaded', main, false); | |
///////////////////////////////////////////////// | |
/** | |
* | |
*/ | |
function main() { | |
hexagons = createHexagons(); | |
cvs.width = WIDTH; | |
cvs.height = HEIGHT; | |
win.addEventListener('scroll', handleScroll, false); | |
handleEnterFrame(); | |
setTimeout(function() { | |
win.scrollTo(0, 2800); | |
}, 500); | |
} | |
///////////////////////////////////////////////// | |
/** | |
* | |
*/ | |
function handleEnterFrame() { | |
var MIN = 0.0001, | |
deltaScrollRate; | |
if (++frame > 3) { | |
frame = 0; | |
deltaScrollRate = (destScrollRate - curScrollRate) / 5; | |
if (deltaScrollRate < -MIN || MIN < deltaScrollRate) { | |
curScrollRate = curScrollRate + deltaScrollRate; | |
render(curScrollRate); | |
} | |
} | |
raf(handleEnterFrame, cvs); | |
} | |
///////////////////////////////////////////////// | |
/** | |
* | |
*/ | |
function handleScroll() { | |
var scrollTop = doc.body.scrollTop || doc.documentElement.scrollTop, | |
scrollHeight = doc.body.scrollHeight || doc.documentElement.scrollHeight, | |
scrollRate = scrollTop / (scrollHeight - win.innerHeight); | |
scrollRate = scrollRate < 0 ? 0 : | |
scrollRate > 1 ? 1 : | |
scrollRate; | |
destScrollRate = scrollRate; | |
} | |
///////////////////////////////////////////////// | |
/** | |
* @class | |
* @constructor | |
* @param {number} x | |
* @param {number} y | |
*/ | |
function Point(x, y) { | |
this.x = x; | |
this.y = y; | |
} | |
///////////////////////////////////////////////// | |
/** | |
* @class | |
* @constructor | |
* @param {Point} a | |
* @param {Point} b | |
* @param {Point} c | |
* @param {Point} d | |
* @param {Point} e | |
* @param {Point} f | |
* @param {Point} center | |
* @param {number} delay | |
*/ | |
function Hexagon(a, b, c, d, e, f, center, delay) { | |
this.a = a; | |
this.b = b; | |
this.c = c; | |
this.d = d; | |
this.e = e; | |
this.f = f; | |
this.center = center; | |
this.delay = delay; | |
} | |
///////////////////////////////////////////////// | |
/** | |
* @param {number} x | |
* @param {number} y | |
* @param {number} delay | |
* @return {Hexagon} | |
*/ | |
function createHexagon(x, y, delay) { | |
var deg30 = 30 * Math.PI / 180, | |
sin30 = Math.sin(deg30), | |
cos30 = Math.cos(deg30), | |
center = new Point(x, y), | |
a, b, c, d, e, f; | |
/** | |
* | |
* a b | |
* | |
* | |
* | | |
* f -[x,y]- c | |
* | | |
* | |
* | |
* e d | |
* | |
*/ | |
a = new Point( | |
-sin30 * HEXAGON_SIZE, | |
-cos30 * HEXAGON_SIZE | |
); | |
b = new Point( | |
sin30 * HEXAGON_SIZE, | |
-cos30 * HEXAGON_SIZE | |
); | |
c = new Point( | |
HEXAGON_SIZE, | |
0 | |
); | |
d = new Point( | |
sin30 * HEXAGON_SIZE, | |
cos30 * HEXAGON_SIZE | |
); | |
e = new Point( | |
-sin30 * HEXAGON_SIZE, | |
cos30 * HEXAGON_SIZE | |
); | |
f = new Point( | |
-HEXAGON_SIZE, | |
0 | |
); | |
return new Hexagon(a, b, c, d, e, f, center, delay); | |
} | |
///////////////////////////////////////////////// | |
/** | |
* @return {Array.<Hexagon>} | |
*/ | |
function createHexagons() { | |
var deg30 = 30 * Math.PI / 180, | |
sin30 = Math.sin(deg30), | |
cos30 = Math.cos(deg30), | |
COLS = Math.ceil(WIDTH / (HEXAGON_SIZE * 3 )) + 1, | |
ROWS = Math.ceil(HEIGHT / (HEXAGON_SIZE * cos30)) + 1, | |
radius = Math.sqrt(WIDTH * WIDTH + HEIGHT * HEIGHT), | |
DELTA_X = HEXAGON_SIZE * sin30 + HEXAGON_SIZE, | |
arr = [], | |
x, y, r, c, delay; | |
for (r = ROWS; r--;) { | |
for (c = COLS; c--;) { | |
x = -(HEXAGON_SIZE / 2) + (HEXAGON_SIZE * 3 ) * c + HEXAGON_SIZE; | |
y = (HEXAGON_SIZE * cos30) * r; | |
delay = Math.sqrt(x * x + y * y) * (1 - ERROR) / radius + ERROR * Math.random(); | |
if (r & 1) { | |
arr.push(createHexagon(x + DELTA_X, y, delay)); | |
} else { | |
arr.push(createHexagon(x, y, delay)); | |
} | |
} | |
} | |
return arr; | |
} | |
///////////////////////////////////////////////// | |
/** | |
* @param {number} scrollRate | |
*/ | |
function render(scrollRate) { | |
var SPEED = .01, | |
arr = hexagons, | |
i = arr.length, | |
hex, rate; | |
ctx.clearRect(0, 0, WIDTH, HEIGHT); | |
ctx.save(); | |
ctx.beginPath(); | |
while (i--) { | |
hex = arr[i]; | |
rate = (scrollRate - hex.delay) / SPEED; | |
rate = rate < 0 ? 0 : | |
rate > 1 ? 1 : | |
rate; | |
ctx.save(); | |
ctx.translate(hex.center.x, hex.center.y); | |
ctx.moveTo(hex.a.x * rate, hex.a.y * rate); | |
ctx.lineTo(hex.b.x * rate, hex.b.y * rate); | |
ctx.lineTo(hex.c.x * rate, hex.c.y * rate); | |
ctx.lineTo(hex.d.x * rate, hex.d.y * rate); | |
ctx.lineTo(hex.e.x * rate, hex.e.y * rate); | |
ctx.lineTo(hex.f.x * rate, hex.f.y * rate); | |
ctx.lineTo(hex.a.x * rate, hex.a.y * rate); | |
ctx.restore(); | |
} | |
ctx.fillStyle = '#222'; | |
ctx.fill(); | |
ctx.beginPath(); | |
i = arr.length; | |
while (i--) { | |
hex = arr[i]; | |
rate = (scrollRate - hex.delay + .02) / SPEED; | |
rate = rate < 0 ? 0 : | |
rate > 1 ? 1 : | |
rate; | |
ctx.save(); | |
ctx.translate(hex.center.x, hex.center.y); | |
ctx.moveTo(hex.a.x * rate, hex.a.y * rate); | |
ctx.lineTo(hex.b.x * rate, hex.b.y * rate); | |
ctx.lineTo(hex.c.x * rate, hex.c.y * rate); | |
ctx.lineTo(hex.d.x * rate, hex.d.y * rate); | |
ctx.lineTo(hex.e.x * rate, hex.e.y * rate); | |
ctx.lineTo(hex.f.x * rate, hex.f.y * rate); | |
ctx.lineTo(hex.a.x * rate, hex.a.y * rate); | |
ctx.restore(); | |
} | |
ctx.globalCompositeOperation = 'destination-over'; | |
ctx.fillStyle = '#09f'; | |
ctx.fill(); | |
ctx.restore(); | |
ctx.strokeStyle = '#000'; | |
ctx.stroke(); | |
} | |
///////////////////////////////////////////////// | |
}(this, document); |
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> |
* { | |
margin: 0; padding: 0; | |
} | |
body { | |
background-color: #000; | |
overflow-x: hidden; | |
} | |
#spacer { | |
height: 10000px; | |
} | |
#stage { | |
position: fixed; | |
top: 0; left: 0; | |
width: 465px; height: 465px; | |
background: url(http://jsrun.it/assets/z/3/q/s/z3qsK.gif) no-repeat scroll 123px 212px; | |
} | |
canvas { | |
opacity: .9; | |
} |