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; | |
| } |