Toggle navigation
在线编辑器
在线代码
文本比较
jQuery下载
前端库
在线手册
登录/注册
下载代码
html
css
js
分享到微信朋友圈
X
html
css
body { font-family: Arial, Helvetica, "Liberation Sans", FreeSans, sans-serif; background-color: #000; margin:0; padding:0; border-width:0; }
JavaScript
"use strict"; /**** parameters you should try to modify */ let angleV = 0.4; // angle of crossing (with perpendicular to side) when trans is "x" /* following numbers are given as fractions of sqSide */ let lenV = 0.2; // length of tangent const cdParal = 0.20; // distance between tangent and side when trans is "b" const clParal = 0.15; // length of tangent pointing towards "b" const clParal2 = 0.2; // length of tangent pointing towards "x" const cDotRadius = 0.05; const cLineWidth = 1.5 * cDotRadius // for animation const speed = 0.5; // pixel / ms const nbSimult = 2; // number of animations in parallel /**** modifications beyond this line at your own risk */ let canvbg, ctxbg; // canvas and context for background (not redrawn at each frame) let canvanim, ctxanim; let maxx, maxy; // canvas sizes (in pixels) let orgx, orgy; // ref position for squares let sth, cth; // sine and cosine of angleV let nbx, nby; // number of squares horiz. / vert. let symType; // type of symetry : // 0 : central // 1 : vertical axis // 2 : horizontal axis (not used) // 3 : two axis let grid; // array of squares let vertices; let sqSide; // length of side of one square let dParal, lParal, lParal2, dotRadius, lineWidth; let events = []; // list of messages for 'animate' let hue, color, bgColor; let tbLoops; let tbAnim; // table of queues of animations let nextToPlay; // index of next animation to play tbAnim let playing; // index of animation currently played let timeAnim; // timings for proper chaining of animations let tStartOne; // starting time for current animation let accel = 1; // for time acceleration let mouse = {}; // reproductible random function // shortcuts for Math.… const mrandom = Math.random; const mfloor = Math.floor; const mround = Math.round; const mceil = Math.ceil; 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; const rac3 = msqrt(3); const rac3s2 = rac3 / 2; const mPIS3 = Math.PI / 3; function Mash() { var n = 0xefc8249d; var mash = function(data) { if ( data ) { data = data.toString(); for (var i = 0; i < data.length; i++) { n += data.charCodeAt(i); var h = 0.02519603282416938 * n; n = h >>> 0; h -= n; h *= n; n = h >>> 0; h -= n; n += h * 0x100000000; // 2^32 } return (n >>> 0) * 2.3283064365386963e-10; // 2^-32 } else n = 0xefc8249d; }; return mash; } 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()); } // intAlea function arrayShuffle (array) { /* randomly changes the order of items in an array only the order is modified, not the elements */ let k1, temp; for (let k = array.length - 1; k >= 1; --k) { k1 = intAlea(0, k + 1); temp = array[k]; array[k] = array[k1]; array[k1] = temp; } // for k return array } // arrayShuffle function randomOrder (range) { /* returns an array with numbers 0..range-1 in random order */ let array = []; for ( let k = 0; k < range; ++k) array[k] = k; return arrayShuffle(array); } // randomOrder // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function randomElement (array) { return array[intAlea(array.length)]; } // randomElement // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* returns intermediate value between v0 and v1, alpha = 0 will return v0, alpha = 1 will return v1 values of alpha outside [0,1] may be used to compute points outside the v0-v1 range */ function lerp (v0, v1, alpha) { return (1 - alpha) * v0 + alpha * v1; } // function lerp; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* returns point based on : - an origin point - a distance - a direction given by two coordinates */ function dirPoint (porg, dist, dir) { let distDir = mhypot (dir[0], dir[1]); return [porg[0] + dist * dir[0] / distDir, porg[1] + dist * dir[1] / distDir]; } // function dirPoint; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* returns two coordinates representing move from porg to pend */ function diffPoints (porg, pend) { return [pend[0] - porg[0], pend[1] - porg[1]]; } // function diffPoints; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function sumPoints (pa, pb) { return [pa[0] + pb[0], pa[1] + pb[1]]; } // function sumPoints; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* returns intermediate point between p0 and p1, alpha = 0 will return p0, alpha = 1 will return p1 values of alpha outside [0,1] may be used to compute points outside the p0-p1 segment */ function intermediate (p0, p1, alpha) { return [(1 - alpha) * p0[0] + alpha * p1[0], (1 - alpha) * p0[1] + alpha * p1[1]]; } // function intermediate // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function length (p0, p1) { /* distance between points */ return mhypot (p0[0] - p1[0], p0[1] - p1[1]); } // function length // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* find first occurence of element in array returns index if found returns -1 if not found but safe == true throws error if not found and safe == false */ function findIndex (array, element, safe = false) { let idx = array.indexOf(element); if (idx != -1 || safe) return idx; throw ('not found element in array'); } // removeFromArray //------------------------------------------------------------------------ function drawBezier (points, first) { /* draws a cubic Bezier curve */ /* The 'first' parameter is just used to draw the very first line in each loop. it needs a 'ctx.moveTo' to define the starting position. Following parts of the loop just need a 'ctx.BezierCurveTo' Defines the path but does not actually draw it ( no 'beginPath' nor 'stroke or 'fill') */ let p0, p1, pa, pb; // control points of the Bézier curve [p0, pa, pb, p1] = points; if (first) ctx.moveTo(p0[0], p0[1]); ctx.bezierCurveTo( pa[0], pa[1], pb[0], pb[1], p1[0], p1[1]); } // drawBezier // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function drawPoint (p, color= '#F00') { // pour tests seulement /* for tests only */ ctx.lineWidth = 4; ctx.strokeStyle = color; ctx.beginPath(); ctx.moveTo (p[0]-8 ,p[1]); ctx.lineTo (p[0]+8 ,p[1]); ctx.moveTo (p[0] ,p[1]-8); ctx.lineTo (p[0] ,p[1]+8); ctx.stroke(); } // drawPoint //----------------------------------------------------------------------------- function Vertex(kx, ky) { this.kx = kx; this.ky = ky; this.p = [orgx + kx * sqSide, orgy + ky * sqSide]; } // Vertex //----------------------------------------------------------------------------- function returnVertex(kx, ky) { /* return vertex at (kx, ky) - creates new one if does not exist */ if (! vertices[ky]) vertices[ky] = []; if (! vertices[ky][kx]) vertices[ky][kx] = new Vertex(kx, ky); return vertices[ky][kx]; } // returnVertex //----------------------------------------------------------------------------- function Square(kx, ky) { this.kx = kx; this.ky = ky; this.vertices = [returnVertex(kx, ky), returnVertex(kx + 1, ky), returnVertex(kx + 1, ky + 1), returnVertex(kx, ky + 1)]; this.trans = []; this.blocked = false; } // Square Square.prototype.setPoints = function() { /* sets the points that will be useful to draw the curves there are 3 points for each side */ let points, middle; this.points = []; this.trans.forEach((trans, side) => { this.points[side] = points = {}; middle = intermediate (this.vertices[side].p, this.vertices[(side + 1) % 4].p, 0.5); switch (this.trans[side]) { case 'b': points.middle = sumPoints(middle, [[0, dParal], [-dParal, 0], [0, -dParal], [dParal, 0]][side]); // change the control point distance according type of trans we are connected to if (this.trans[(side + 3) % 4] == "b") points.ccw = sumPoints(points.middle, [[-lParal, 0], [0, -lParal], [lParal, 0], [0, lParal]][side]); else points.ccw = sumPoints(points.middle, [[-lParal2, 0], [0, -lParal2], [lParal2, 0], [0, lParal2]][side]); if (this.trans[(side + 1) % 4] == "b") points.cw = sumPoints(points.middle, [[lParal, 0], [0, lParal], [-lParal, 0], [0, -lParal]][side]); else points.cw = sumPoints(points.middle, [[lParal2, 0], [0, lParal2], [-lParal2, 0], [0, -lParal2]][side]); break; case'x' : points.middle = middle; points.ccw = sumPoints(middle, [[-sth, cth], [-cth, -sth], [sth, -cth], [cth, sth]][side]); points.cw = sumPoints(middle, [[sth, cth], [-cth, sth], [-sth, -cth], [cth, -sth]][side]); break; } // switch }); // this.trans.forEach } // Square.prototype.setPoints Square.prototype.draw = function() { let p, side, points; ctx.beginPath(); p = this.vertices[0].p; ctx.moveTo (p[0], p[1]); p = this.vertices[1].p; ctx.lineTo (p[0], p[1]); p = this.vertices[2].p; ctx.lineTo (p[0], p[1]); p = this.vertices[3].p; ctx.lineTo (p[0], p[1]); ctx.closePath(); ctx.strokeStyle = '#fff'; ctx.lineWidth = 0.5; ctx.stroke(); for (side = 0; side < 4; ++side) { points = this.points[side]; ctx.beginPath(); ctx.moveTo(points.ccw[0], points.ccw[1]); ctx.lineTo(points.middle[0], points.middle[1]); ctx.lineTo(points.cw[0], points.cw[1]); ctx.strokeStyle = '#0f0'; ctx.lineWidth = lineWidth; ctx.stroke(); } // side } // Square.prototype.draw Square.prototype.drawDot = function(ctx) { if (this.blocked) return; ctx.beginPath(); ctx.arc(orgx + (this.kx + 0.5) * sqSide, orgy + (this.ky + 0.5) * sqSide, dotRadius, 0, m2PI); ctx.fillStyle = color; ctx.fill(); } // Square.prototype.drawDot Square.prototype.drawLines = function(ctx) { let p, side, nextSide, points, nextPoints; if (this.blocked) return; for (side = 0; side < 4; ++side) { points = this.points[side]; nextSide = (side + 1) % 4; nextPoints = this.points[nextSide]; ctx.beginPath(); ctx.moveTo(points.middle[0], points.middle[1]); ctx.bezierCurveTo(points.cw[0], points.cw[1], nextPoints.ccw[0], nextPoints.ccw[1], nextPoints.middle[0], nextPoints.middle[1]); ctx.strokeStyle = color; ctx.lineWidth = lineWidth; ctx.stroke(); } // side } // Square.prototype.drawLines Square.prototype.setBlocked = function() { /* makes a cell unusable for drawing curve */ this.trans =["b","b","b","b"]; this.blocked = true; } Square.prototype.removebbbb = function() { let side, xneigh, yneigh; // special authorization for centre if (this.kx == (nbx - 1) / 2 && this.ky == (nby - 1) / 2) return; if (this.blocked) return; if (this.trans.some(value => value != "b")) return; // not "bbbb" : don't change let tb = randomOrder(4); // not always try in the same order for (let k = 0; k < 4 ; ++k) { side = tb[k]; if (this.trans[side] != "b") continue; // not "b", not to be changed xneigh = this.kx + [0, 1, 0, -1][side]; yneigh = this.ky + [-1, 0, 1, 0][side]; if (this.kx == 1 && this.ky == 3) { let zzzz = 0 } if (grid[yneigh][xneigh].blocked) continue; // this side can't be changed setGivenTrans(this, side, 'x'); setGivenTrans(grid[yneigh][xneigh], side ^ 2, 'x'); return; // done with this cell } // } // Square.prototype.removebbbb Square.prototype.removexxxx = function() { return; let side, xneigh, yneigh; // special authorization for centre if (this.kx == (nbx - 1) / 2 && this.ky == (nby - 1) / 2) return; if (this.blocked) return; if (this.trans.some(value => value != "x")) return; // not "xxxx" : don't change let tb = randomOrder(4); // not always try in the same order for (let k = 0; k < 4 ; ++k) { side = tb[k]; if (this.trans[side] != "x") continue; // not "b", not to be changed xneigh = this.kx + [0, 1, 0, -1][side]; yneigh = this.ky + [-1, 0, 1, 0][side]; // if (grid[yneigh][xneigh].blocked) continue; //can't be blocked setGivenTrans(this, side, 'b'); setGivenTrans(grid[yneigh][xneigh], side ^ 2, 'b'); return; // done with this cell } // } // Square.prototype.removexxxx function setPoints() { for (let ky = 1; ky <= nby - 2; ++ky) { for (let kx = 1; kx <= nbx - 2; ++kx) { grid[ky][kx].setPoints(); } // for kx } // for ky } // setPoints function setGivenTrans(cell, side, value) { let xsym, ysym; let {kx, ky} = cell; if (value) cell.trans[side] = value; else value = cell.trans[side]; switch (symType) { case 0 : // central symetry xsym = nbx - 1 - kx; ysym = nby - 1 - ky; grid[ysym][xsym].trans[side ^ 2] = value; break; case 1 : // symetry around vertical axis xsym = nbx - 1 - kx; grid[ky][xsym].trans[(4 - side) & 3] = value; break; case 2 : // symetry around horizontal axis ysym = nby - 1 - ky; grid[ysym][kx].trans[(6 - side) & 3] = value; break; case 3 : // two symetry axis xsym = nbx - 1 - kx; ysym = nby - 1 - ky; grid[ysym][xsym].trans[side ^ 2] = value; grid[ky][xsym].trans[(4 - side) & 3] = value; grid[ysym][kx].trans[(6 - side) & 3] = value; break; } // switch } // setGivenTrans function setTrans() { let xneigh, yneigh; // position of neighbour let cell, celln, transn; // fill external fictional row with edges that constraint the curve to stay in the innermost cells grid[0].forEach(cell => cell.setBlocked()); // top grid.forEach(row => { row[0].setBlocked(); // left side row[nbx - 1].setBlocked(); // right side }); grid[nby - 1].forEach(cell => cell.setBlocked()); // bottom cropCorners(); for (let ky = 1; ky < nby - 1; ++ky) { for (let kx = 1; kx < nbx - 1; ++kx) { cell = grid[ky][kx]; nextSide: for (let side = 0; side < 4; ++side) { if (!cell.trans[side]) { // if transition not already defined on this side // test neighbour to see if trans already defined xneigh = kx + [0, 1, 0, -1][side]; yneigh = ky + [-1, 0, 1, 0][side]; celln = grid[yneigh][xneigh]; transn = celln.trans[side ^ 2]; // ^2 gives opposite side if (transn) { // trans constrained from neighbour cell.trans[side] = transn; } else { // no constraint : take random transition cell.trans[side] = randomElement(["b","x"]); // somthig more subtle should be tried } } // if trans not already defined for this side /* apply type of symetry (may be to the same cell) */ setGivenTrans(cell, side, false); // apply symetries } // for side } // for kx } // for ky /* try to fix some unsightly situations */ for (let ky = 1; ky < nby - 1; ++ky) { for (let kx = 1; kx < nbx - 1; ++kx) { cell = grid[ky][kx]; cell.removexxxx(); cell.removebbbb(); } // for kx; } // for ky } // setTrans function showGrid () { let line, cell for (let ky = 1; ky < nby - 1; ++ky) { line = grid[ky]; for (let kx = 1; kx < nbx - 1; ++kx) { cell = line[kx]; cell.draw(); }; // for kx }; // for ky } function drawLines (ctx) { let line, cell for (let ky = 1; ky < nby - 1; ++ky) { line = grid[ky]; for (let kx = 1; kx < nbx - 1; ++kx) { cell = line[kx]; cell.drawLines(ctx); }; // for kx }; // for ky } function drawDots (ctx) { let line, cell for (let ky = 1; ky < nby - 1; ++ky) { line = grid[ky]; for (let kx = 1; kx < nbx - 1; ++kx) { cell = line[kx]; cell.drawDot(ctx); }; // for kx }; // for ky } function cropCorners() { let kx, ky; let redx = (nbx - 3) / 2; // is integer, since nbx is odd let redy = (nby - 3) / 2; // is integer, since nbx is odd let max = mmax(redx, redy); let min = mmin(redx, redy); let swap = (redy > redx); let nToRem, step; let cropMethod = randomElement([0,3,3]); switch (cropMethod) { case 0 : return; // no cropping at all, the easyest case 3 : nToRem = min; step = 1; break; } // switch for (let k = 1; k <= min && (nToRem > 0); ++k) { for (let krem = 1; krem <= nToRem; ++krem) { [kx, ky] = swap ? [k, krem] : [krem, k]; grid[ky][kx].setBlocked(); grid[nby - 1 - ky][kx].setBlocked(); grid[ky][nbx - 1 - kx].setBlocked(); grid[nby - 1 - ky][nbx - 1 - kx].setBlocked(); } // for krem nToRem -= step; } // for k } // cropCorners function analyzeLoops() { let kx, ky, line, cell, side, dir; tbLoops = []; grid.forEach(line => line.forEach (cell => cell.loops = [[],[],[],[]])); // for loop analysis let kLoop = 0; // index of current loop for (ky = 1; ky < nby - 1; ++ky) { line = grid[ky]; for (kx = 1; kx < nbx - 1; ++kx) { cell = line[kx]; if (cell.blocked) continue; // ths cell out of the game for (side = 0; side < 4; ++side) { for (dir = 0; dir < 2; ++dir) { if (cell.loops[side][dir] === undefined) { analyzeOneLoop (cell, side, dir); // new Loop ++kLoop; // count loops / increm. loop index } // if unused trans } } // for side } // for kx } // for ky function analyzeOneLoop (cell, side, direction) { // inside analyzeLoops let s1; let cellStart = cell; let sideStart = side; let dirStart = direction; let loop = new PolyCubic([cell.points[side].middle]); // only the 1st point tbLoops[kLoop] = loop; do { cell.loops[side][direction] = kLoop; // memorize loop goes here if (direction == 0) { // cw s1 = (side + 1) % 4; // next side turning cw loop.addCubic([cell.points[side].cw, cell.points[s1].ccw, cell.points[s1].middle]); } else { // ccw s1 = (side + 3) % 4; // next side turning ccw loop.addCubic([cell.points[side].ccw, cell.points[s1].cw, cell.points[s1].middle]); } cell.loops[s1][1 - direction] = kLoop; // memorize loop goes here too if (cell.trans[s1] == 'b') { side = s1; } else { // if 'x' cell = grid[cell.ky + [-1,0,1,0][s1]][cell.kx + [0,1,0,-1][s1]] side = (s1 + 2) % 4; direction = 1 - direction; // changing of cell, change direction } } while (cell != cellStart || side != sideStart || direction != dirStart); } // analyzeOneLoop } // analyze loops function startOver() { // canvas dimensions maxx = window.innerWidth; maxy = window.innerHeight; canvbg.style.left = ((window.innerWidth ) - maxx) / 2 + 'px'; canvbg.style.top = ((window.innerHeight ) - maxy) / 2 + 'px'; canvbg.width = maxx; canvbg.height = maxy; canvanim.style.left = ((window.innerWidth ) - maxx) / 2 + 'px'; canvanim.style.top = ((window.innerHeight ) - maxy) / 2 + 'px'; canvanim.width = maxx; canvanim.height = maxy; if (maxx < 10) return false; // not yet ready ctxbg.lineCap = 'round'; ctxanim.lineCap = 'round'; // first, choose the type of symetry symType = randomElement([0,1,1,3,3,3,4]); // sym 2 axis is preferred // symType = 4; // may be used : there will be no symetry at all // number of columns / rows nbx = 1 + 2 * intAlea(2,5); nby = (symType == 0 || symType == 3) ? nbx : (1 + 2 * intAlea(2,5)); // sqSide = mround (maxx / (nbx + 1)); if (sqSide * (nby + 1) > maxy) sqSide = mround (maxy / (nby + 1)); nbx += 2; nby += 2; // position of top left corner of grid[0][0] orgx = (maxx - sqSide * nbx ) / 2; orgy = (maxy - sqSide * nby ) / 2; sth = msin(angleV) * lenV * sqSide; cth = mcos(angleV) * lenV * sqSide; lParal = clParal * sqSide; lParal2 = clParal2 * sqSide; dParal = cdParal * sqSide; dotRadius = cDotRadius * sqSide; lineWidth = cLineWidth * sqSide; // bgColor = `hsl(${(hue + 180) % 360},100%,10%)`; bgColor = '#222'; // create grid vertices = []; grid = new Array(nby).fill(0).map((line,ky)=> new Array(nbx).fill(0).map((square, kx)=> new Square(kx, ky))); setTrans(); setPoints(); // drawLines(ctxbg); // drawDots(ctxbg); analyzeLoops(); return true; } // startOver function clickCanvas() { events.push({event: 'click'}); mouse.x = event.clientX; mouse.y = event.clientY; } function resize() { events.push({event: 'resize'}); } function mouseMove() { mouse.x = event.clientX; mouse.y = event.clientY; mouse.moved = true; } function prepareAnimation() { /* after loop have been analyzed, prepare elements for animation */ let kLoop; arrayShuffle(tbLoops); // take loops in any order // reverse the direction of some loops tbAnim = new Array(nbSimult).fill(0).map(()=>[]); // not clear, you said ? nextToPlay = new Array(nbSimult).fill(0); playing = []; tStartOne = []; timeAnim = new Array(nbSimult).fill(0).map(()=>[0]); // time for beginning of every animation in each queue tbLoops.forEach(loop => { if (intAlea(2)) loop.inverse(); loop.rndOffset(mrandom()); loop.structure(); loop.atomize(2); loop.prepareWhere(0, speed); }); // tbLoops.forEach // simulate animations are running to queue them properly for (kLoop = tbLoops.length - 1; kLoop >= 0; --kLoop) { /* find queue which ends first */ let kQueue = timeAnim.reduce((reduced, timeQueue, kQueue) => { return ((timeQueue[timeQueue.length - 1] < timeAnim[reduced][timeAnim[reduced].length - 1]) ? kQueue : reduced); }, 0); tbAnim[kQueue].push(tbLoops[kLoop]); timeAnim[kQueue].push(timeAnim[kQueue][timeAnim[kQueue].length - 1] + tbLoops[kLoop].tout + intAlea(100,200)); // add time for this loop + short random delay } // for kLoop // clear background ctxbg.fillStyle = bgColor; ctxbg.fillRect(0, 0, maxx, maxy); drawDots(ctxbg); } // prepareAnimation let animate = (()=>{ // scope for animate let state; let relTime = 0; let tstampPre = 0; return function (tstamp) { let event; let dt = (tstamp - tstampPre) * accel; relTime += dt; // will be zeroed when starting animation tstampPre = tstamp; while (event = events.shift()) { switch (event.event) { case 'init' : case 'click' : case 'resize' : state = 1; break; } //switch (event.event) } // while events let ymouse; switch (state) { case 1: if (startOver()) ++state; break; case 2: // prepare for animation ymouse = (mouse.y == undefined) ? (maxy / 3) : mouse.y; hue = (360 + 2 * 30) * ymouse / maxy - 30; hue = mmax(0, mmin(360,hue)); // 0 - 360 color = `hsl(${hue},100%,50%)`; prepareAnimation(); relTime = 0; // start time for animation ++state; break; case 3 : ymouse = (mouse.y == undefined) ? (maxy / 3) : mouse.y; hue = (360 + 2 * 30) * ymouse / maxy - 30; hue = mmax(0, mmin(360,hue)); // 0 - 360 color = `hsl(${hue},100%,50%)`; if (mouse.x == undefined) accel = 1; else { accel = 0.1 + 10 / maxx * mouse.x; } ctxanim.clearRect(0, 0, maxx, maxy); let stop = true; tbAnim.forEach((queue, kQueue)=> { if (playing[kQueue] === undefined) { // nothing playing in this queue ? if (nextToPlay[kQueue] < queue.length) { stop = false; playing[kQueue] = nextToPlay[kQueue]++; tStartOne[kQueue] = timeAnim[kQueue][playing[kQueue]]; } // if queue not finished } // if nothing playing in this queue else { stop = false; } });// tbAnim.forEach if (stop) { ++state; break; } ctxanim.beginPath(); playing.forEach((kAnim, kQueue) => { if (kAnim === undefined) return; tbAnim[kQueue][kAnim].drawTo(relTime - tStartOne[kQueue],ctxanim); if (tbAnim[kQueue][kAnim].finished) { ctxbg.beginPath(); tbAnim[kQueue][kAnim].draw(ctxbg); ctxbg.strokeStyle = color; ctxbg.lineWidth = lineWidth; ctxbg.stroke(); playing[kQueue] = undefined; } }); ctxanim.strokeStyle = color; ctxanim.lineWidth = lineWidth; ctxanim.stroke(); } // switch (state) window.requestAnimationFrame(animate); } // animate })(); // scope for animate // beginning of execution window.addEventListener("load",function() { { canvbg = document.createElement('canvas'); canvbg.style.position="absolute"; document.body.appendChild(canvbg); ctxbg = canvbg.getContext('2d'); // canvbg.setAttribute('title','Click for new pattern'); canvanim = document.createElement('canvas'); canvanim.style.position="absolute"; document.body.appendChild(canvanim); ctxanim = canvanim.getContext('2d'); canvanim.setAttribute('title','Click for new pattern \nMove for hue and speed'); } // canvas creation window.addEventListener('click',clickCanvas); window.addEventListener('resize',resize); window.addEventListener('mousemove',mouseMove); /* launch animation */ events.push ({event: 'init'}); window.requestAnimationFrame(animate); // animate }); // window load listener (function() { function CubicBezier (points) { /* constructor - to be called with an array of 4 points */ this.points = points; } // CubicBezier constructor CubicBezier.prototype.split = function (alpha) { let pa = intermediate (this.points[0], this.points[1] , alpha); let pb = intermediate (this.points[1], this.points[2] , alpha); let pc = intermediate (this.points[2], this.points[3] , alpha); let pd = intermediate (pa, pb, alpha); let pe = intermediate (pb, pc, alpha); let pf = intermediate (pd, pe, alpha); return [new CubicBezier([this.points[0], pa, pd, pf]), new CubicBezier([pf, pe, pc, this.points[3]])]; } //split CubicBezier.prototype.draw = function(ctx, first) { let p0, p1, pa, pb; // control points of the Bézier curve [p0, pa, pb, p1] = this.points; if (first) ctx.moveTo(p0[0], p0[1]); ctx.bezierCurveTo( pa[0], pa[1], pb[0], pb[1], p1[0], p1[1]); } // draw CubicBezier.prototype.atomize = function(approx) { let lop = [this.points[0]]; // first point in list let dist = [0]; // distance with previous point let alphas = [0]; subDivide(this, 0, 1); for (let k = 1; k < dist.length; ++k) dist[k] += dist[k - 1]; this.atomPoints = lop; this.dist = dist; this.alphas = alphas; this.nb = lop.length; this.length = dist[lop.length -1]; this.approx = approx; function subDivide(subBezier, alpha0, alpha1) { let [p0,p1,p2,p3] = subBezier.points; let l1 = length(p0, p3); let l2 = length(p0, p1) + length(p1, p2) + length(p2, p3); if (l1 + approx > l2) { // if approximation reached lop.push(p3); dist.push(l1); alphas.push(alpha1); return; } // if approximation reached let [ba, bb] = subBezier.split(0.5); let mid = (alpha0 + alpha1) / 2; subDivide(ba, alpha0, mid); subDivide(bb, mid, alpha1); } // subDivide } // atomize CubicBezier.prototype.prepareWhere = function (tin, speed) { let atom, times; // prepares for future calls to 'where' // speed must be positive if (!this.atomized) this.atomize(1); // default approx of 1 this.tin = tin; // time in this.speed = speed; /* complete this.atom with times at which points are passed by */ this.times = times = []; this.atomPoints.forEach((point,k) => times[k] = this.dist[k] / speed + tin); this.tout = times[this.nb - 1]; // should get out at this moment } // prepareWhere CubicBezier.prototype.where = function (t) { if (t > this.tout) return false; // too late ! if (t <= this.tin) return this.atomPoints[0]; // too soon for (k = 1; k < this.nb; ++k) { if (t < this.times[k]) break; } let pa = this.atomPoints[k - 1]; let pb = this.atomPoints[k]; let ta = t - this.times[k - 1]; let tb = this.times[k] - t; let dt = tb + ta; return [(pa[0] * tb + pb[0] * ta) / dt, (pa[1] * tb + pb[1] * ta) / dt]; } // CubicBezier.prototype.where // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CubicBezier.prototype.drawTo = function (t, ctx, first) { /* draws path for Cubic from start up to the point reached at time t Defines the path but does not actually draw it ( no 'beginPath' nor 'stroke or 'fill') Begins with ctx.moveTo (first point) if first == true /* returns new value for 'first' (normally false, but may be true if t < t init and nothing at all is done) */ let k, alpha; if (t > this.tout) { // this cubic is finished this.draw(ctx,first); return false; } if (t <= this.tin) return first; // too soon : do nothing for (k = 1; k < this.nb; ++k) { if (t < this.times[k]) break; } let pa = this.alphas[k - 1]; let pb = this.alphas[k]; let ta = t - this.times[k - 1]; let tb = this.times[k] - t; alpha = (pa * tb + pb * ta) / (ta + tb); let[c1, c2] = this.split(alpha); c1.draw(ctx, first); return false; } // CubicBezier.prototype.drawTo // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - window.CubicBezier = CubicBezier; })(); /* the following code could have been embedded in the same anonymous function I kept it separated for the sake of clarity */ (function() { function PolyCubic (initial) { /* constructor for an object made of several consecutive Bezier curves (whether closed or not) */ /* if initial is given, it must be an array of 3n + 1 (n = 0 or more) points, the last (4th) point of every Bezier cubic being the 1st of the next Bezier cubic. Notice : the PolyCubic won't work if this.points.length as only 1 point */ this.points = []; if (initial) { this.points = initial; // (no check) } } // PolyCubic // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PolyCubic.prototype.addCubic = function(cubic) { /* adds a cubic to this PolyCubic parameter cubic is an array of 3 or 4 points : 4 points are required if this.points empty. The 1st of 4 points is ignored if this.points is not empty. The 3 remaining points are appended to this.points. */ if (!this.points.length) { this.points[0] = cubic[0]; } let k = (cubic.length == 3) ? 0 : 1; let l = this.points.length; this.points[l++] = cubic[k++]; // add 3 points to this.points this.points[l++] = cubic[k++]; this.points[l++] = cubic[k++]; } // PolyCubic.prototype.addCubic // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PolyCubic.prototype.inverse = function() { /* to be called when all points of the PolyCubic have been defined reorders the points backwards */ let pts = this.points; for (let ka = 0, kb = pts.length - 1; ka < kb; ++ka, --kb) { [pts[ka], pts[kb]] = [pts[kb], pts[ka]]; } // ka, kb } // PolyCubic.prototype.inverse // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PolyCubic.prototype.rndOffset = function(rnd) { /* to be called when all points of the PolyCubic have been defined moves some points to describe the same curve, beginning at some other point rnd must be a random number(not a function) in the range [0..1[ */ let pts = this.points; let ncut = (pts.length - 1) / 3; // number of Bezier; let cut = Math.floor(rnd * ncut); if (ncut == cut) --cut; // just in case rnd too close to 1 if (cut == 0) return; // nothing to do cut *= 3; pts.splice(-1, 1); // remove last point (== 1st) let s1 = pts.slice(cut, pts.length); // beginning of future array this.points = s1.concat(pts.slice(0, cut)); this.points.push(this.points[0]); // add 1st point to the end; } // PolyCubic.prototype.inverse // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PolyCubic.prototype.structure = function() { /* to be called when all points of the PolyCubic have been defined */ let cubics = []; let pts = this.points; for (let k = 0; k < pts.length - 1; k += 3) { cubics.push (new CubicBezier([pts[k], pts[k + 1], pts[k + 2], pts[k + 3]])); } // k; this.cubics = cubics; } // PolyCubic.prototype.structure // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PolyCubic.prototype.atomize = function(approx) { /* to be called when all points of the PolyCubic.structure has been called */ let length = 0; this.cubics.forEach(cubic => { cubic.atomize(approx); length += cubic.length; }); // this.cubics.forEach this.length = length; } // PolyCubic.prototype.atomize // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PolyCubic.prototype.prepareWhere = function (tin, speed) { /* speed should be > 0 */ this.tin = tin; this.cubics.forEach(cubic => { cubic.prepareWhere (tin, speed); tin = cubic.tout; // for next animation }); // this.cubics.forEach this.tout = tin; this.kCubic = 0; // begin with 1st cubic this.finished = false; } // PolyCubic.prototype.prepareWhere // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PolyCubic.prototype.where = function(t) { /* returns the current position at time t, based on the entry time and speed given in prepareWhere and the asumption that the value of t in increasing every time 'where' is called return entry point if t < this.tin, and false if t > this.tout */ while (this.kCubic < this.cubics.length) { if (t <= this.cubics[this.kCubic].tout) return this.cubics[this.kCubic].where(t); ++this.kCubic; } return false; // finished } // PolyCubic.prototype.where // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PolyCubic.prototype.draw = function(ctx) { /* call draw on every cubic, with the 'first' parameter appropriately set */ let first = true; this.cubics.forEach (cubic => {cubic.draw(ctx, first); first = false}); } // PolyCubic.prototype.draw // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PolyCubic.prototype.drawTo = function(t, ctx) { /* call drawTo on every cubic, with the 'first' parameter appropriately set */ let first = true; this.cubics.forEach (cubic => {first = cubic.drawTo(t, ctx, first);}); if (t >= this.tout) this.finished = true; } // PolyCubic.prototype.draw window.PolyCubic = PolyCubic; })();
粒子
时间
文字
hover
canvas
3d
游戏
音乐
火焰
水波
轮播图
鼠标跟随
动画
css
加载动画
导航
菜单
按钮
滑块
tab
弹出层
统计图
svg
×
Close
在线代码下载提示
开通在线代码永久免费下载,需支付20jQ币
开通后,在线代码模块中所有代码可终身免费下!
您已开通在线代码永久免费下载,关闭提示框后,点下载代码可直接下载!
您已经开通过在线代码永久免费下载
对不起,您的jQ币不足!可通过发布资源 或
直接充值获取jQ币
取消
开通下载
<!doctype html> <html> <head> <meta charset="utf-8"> <title>艺术线条-贝塞尔曲线-jq22.com</title> <script src="https://www.jq22.com/jquery/jquery-1.10.2.js"></script> <style>
</style> </head> <body>
<script>
</script>
</body> </html>
2012-2021 jQuery插件库版权所有
jquery插件
|
jq22工具库
|
网页技术
|
广告合作
|
在线反馈
|
版权声明
沪ICP备13043785号-1
浙公网安备 33041102000314号