Created
February 7, 2020 18:35
-
-
Save lostintangent/c81960f3c3521209efb14436d71acf76 to your computer and use it in GitHub Desktop.
lines
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
"use strict"; | |
window.addEventListener("load",function() { | |
let canv, ctx; | |
let lines; | |
let maxx, maxy; | |
let yLines; | |
let restart; | |
/******************************************************** | |
parameters - play with them | |
********************************************************/ | |
const bgColor = '#004'; // background color | |
const nbLines = 6; // number of lines | |
const pixelsPerFrame = 3; // sliding speed | |
const interLine = 50; // distance between lines - in pixels | |
const lineWidth = 5; // line thickness | |
const transitionCoeff = 3 ; // coeff for transition | |
const coeffBezier = 0.5; // coeff for Bezier curve | |
//******************************************************** | |
// shortcuts for Math | |
const mrandom = Math.random; | |
const mfloor = Math.floor; | |
const mceil = Math.ceil; | |
const mround = Math.round; | |
const mabs = Math.abs; | |
const mmin = Math.min; | |
const mmax = Math.max; | |
const mPI = Math.PI; | |
const mPIS2 = Math.PI / 2; | |
const m2PI = Math.PI * 2; | |
const msin = Math.sin; | |
const mcos = Math.cos; | |
const matan2 = Math.atan2; | |
const mhypot = Math.hypot; | |
const msqrt = Math.sqrt; | |
//------------------------------------------------------------------------ | |
function Line(index) { | |
/* creates a new, flat line locate on the indexth line | |
Adds this line to the lines array | |
*/ | |
this.index = index; // location in lines | |
lines[index] = this; // adds new line to lines | |
this.description = [{kl: index}]; | |
/* kl alone : horizontal line throughout the window, at the given height | |
{kg, kd, xg, dx} : transition from line kg to line kd, beginning at pixel | |
x = xg and ending at pixel xg + dx | |
*/ | |
this.color = `hsl(${alea(360)}, 100%, 50%)`; | |
} // Line | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
Line.prototype.lD = function() { | |
/* tells on which display line we are on the right side of the window | |
return a line number | |
*/ | |
let elemD = this.description[this.description.length - 1]; // last element of this line | |
return (typeof elemD.kl == 'undefined') ? elemD.kd : elemD.kl; | |
} // Line.prototype.lD | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
Line.prototype.addTransition = function(kd, xg, dx) { | |
/* adds a transition towards line kd, beginning at pixel xg and lasting a duration of dx | |
*/ | |
let tra = { kg: this.lD(), kd: kd, xg: xg, dx: dx }; | |
if (this.description.length == 1 && typeof this.description[0].kl != 'undefined') { | |
this.description.pop(); // There was a single line : destroy it | |
} | |
this.description.push(tra); // add a new transition | |
} // Line.prototype.addTransition | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
Line.prototype.draw = function() { | |
let x0, x1, x2, x3, y0, y3; | |
ctx.beginPath(); | |
ctx.lineWidth = lineWidth; | |
ctx.strokeStyle = this.color; | |
if (this.description.length == 1 && typeof this.description[0].kl != 'undefined') { | |
y0 = yLines[this.description[0].kl]; | |
ctx.moveTo (0, y0); | |
ctx.lineTo (maxx, y0); | |
ctx.stroke(); | |
return; // that's enough | |
} // case of simple line | |
/* compound line | |
begin by straight line if required */ | |
y0 = yLines[this.description[0].kg]; | |
x0 = this.description[0].xg; | |
if (x0 > 0) { | |
ctx.moveTo (0, y0); | |
ctx.lineTo (x0, y0); | |
} else { | |
ctx.moveTo (x0, y0); | |
} | |
/* draw Bézier curve + following horizontal piece of line if required */ | |
this.description.forEach ((descr, kdesc) => { | |
x1 = x0 + descr.dx * coeffBezier; | |
x3 = x0 + descr.dx; | |
x2 = x3 - descr.dx * coeffBezier; | |
y3 = yLines[descr.kd]; | |
ctx.bezierCurveTo (x1, y0, x2, y3, x3, y3); | |
/* overflow to the right, no need go further */ | |
if (x3 >= maxx) { | |
ctx.stroke(); | |
return; | |
} | |
// If it was the last piece, draw horizontal line till the right side | |
if (kdesc == this.description.length - 1) { | |
ctx.lineTo (maxx, y3); | |
ctx.stroke(); | |
return; | |
} // if end of line | |
// else, draw horizontal till beginning of next Bezier | |
y0 = y3; // next line for future | |
x0 = this.description [kdesc + 1].xg; | |
ctx.lineTo (x0, y0); // horizontal till beginning of next Bezier | |
}); // this.description.forEach | |
} // Line.prototype.draw | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
Line.prototype.slide = function(npix) { | |
// returns rightmost pixel | |
if (typeof this.description[0].kl != 'undefined') return 0; // simple line, nothing to do | |
this.description.forEach(descr => { | |
descr.xg -= npix; | |
}) // this.description.forEach | |
let desc0 = this.description[0]; | |
if (desc0.xg + desc0.dx <= 0) { // bézier disappears at the left side, delete it | |
this.description.shift(); | |
if (this.description.length == 0) { // nothing left, add simple line | |
this.description[0] = {kl: desc0.kd}; | |
return 0; | |
} | |
} | |
desc0 = this.description[this.description.length - 1]; | |
return desc0.xg + desc0.dx; | |
} // Line.prototype.slide | |
//------------------------------------------------------------------------ | |
// end of class Line | |
//------------------------------------------------------------------------ | |
function newCrossing() { | |
let nOrder, ge; | |
do { | |
nOrder = shuffle(); // picks a new random order of lines | |
/* calculates highest jump */ | |
ge = lines.reduce ( | |
(maxJump, line) => { | |
let jump = mabs(line.lD() - nOrder[line.index]); | |
return (jump > maxJump) ? jump : maxJump; | |
}, 0); | |
} while (ge == 0); // we want at least one transition ! | |
let dxMax = (ge + 1) / 2 * transitionCoeff * interLine; | |
lines.forEach ((line, index) => { | |
let jump = mabs(line.lD() - nOrder[line.index]); | |
if (jump > 0) { // if this line actually moves | |
let dx = (jump + 1) / 2 * transitionCoeff * interLine; | |
let orgx = (dxMax - dx) / 2; // where the transition will begin for this line | |
line.addTransition(nOrder[line.index], maxx + orgx, dx); | |
} // if this line actually moves | |
}); // lines.forEach | |
} // newCrossing | |
//------------------------------------------------------------------------ | |
function size() { | |
let orgy; | |
maxx = window.innerWidth - 8; // -8 for a 4 pixels margin | |
maxy = window.innerHeight - 8; | |
canv.style.left = (window.innerWidth - maxx) / 2 + 'px'; | |
canv.style.top = (window.innerHeight - maxy) / 2 + 'px'; | |
canv.width = maxx; | |
canv.height = maxy; | |
orgy = (maxy - (nbLines - 1) * interLine) / 2 | |
yLines = []; | |
for (let k = 0; k < nbLines; ++k) yLines[k] = orgy + k * interLine; | |
} // size | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
function drawLines() { | |
ctx.clearRect(0, 0, maxx, maxy); | |
lines.forEach(line => line.draw()); | |
} // function drawLines() | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
let slideLines = (function() { | |
/* makes lines slide | |
if there is nothing left on the right of the window, creates a new crossing */ | |
let posnul = maxx * alea (0.8, 0.95); | |
return function (npix) { | |
let posd; | |
let maxd = 0; | |
lines.forEach(line => { | |
posd = line.slide(npix); | |
if (posd > maxd) maxd = posd; | |
}); // lines.forEach | |
let dist = posnul - maxd; | |
if (dist < 0) return; // not finished yet | |
newCrossing(); | |
posnul = maxx * alea (0.99, 0.999); | |
} | |
})(); // function slideLines() | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
function startOver() { | |
size(); // resize | |
lines = []; | |
for (let k = 0; k < nbLines ; ++k) { | |
new Line(k); | |
} | |
} // startOver | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
function alea (min, max) { | |
if (typeof max == 'undefined') return min * mrandom(); | |
return min + (max - min) * mrandom(); | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
function intAlea (min, max) { | |
if (typeof max == 'undefined') { | |
max = min; min = 0; | |
} | |
return mfloor(min + (max - min) * mrandom()); | |
} | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
function shuffle() { | |
let tb = []; | |
let k, r; | |
for (k = 0; k < nbLines; ++k) tb[k] = k; | |
for (k = nbLines - 1; k > 0; --k) { | |
r = intAlea(0, k + 1); | |
[tb[r], tb[k]] = [tb[k], tb[r]] | |
} // for k | |
return tb | |
} // function shuffle | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
function clickCanvas() { | |
restart = true; | |
} // clickCanvas | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
function resizeWindow() { | |
restart = true; | |
} // clickCanvas | |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
function animate(chrono) { | |
if (restart) { | |
startOver(); | |
} | |
if (maxx > 10) { | |
restart = false; | |
slideLines(pixelsPerFrame); | |
drawLines(); | |
} | |
window.requestAnimationFrame(animate); | |
} // animate | |
//------------------------------------------------------------------------ | |
//------------------------------------------------------------------------ | |
// beginning of execution | |
{ | |
document.body.style.backgroundColor = bgColor; | |
canv = document.createElement('canvas'); | |
canv.style.position = "absolute"; | |
canv.addEventListener('click',clickCanvas); | |
document.body.appendChild(canv); | |
ctx = canv.getContext('2d'); | |
} // CANVAS creation | |
startOver(); | |
window.addEventListener('resize',resizeWindow); | |
animate(); | |
}); // window load listener |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment