canvas实现彩色立体波浪形背景效果代码
代码语言:html
所属分类:背景
代码描述:canvas实现彩色立体波浪形背景效果代码,点击可切换。
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <style> body { font-family: Arial, Helvetica, "Liberation Sans", FreeSans, sans-serif; background-color: #000; margin: 0; padding: 0; border-width: 0; overflow: hidden; } </style> </head> <body translate="no"> <script > "use strict"; let canv, ctx; // canvas and context let maxx, maxy; // canvas dimensions let radius; // hexagons radius (and side length) let grid; // array of hexagons let nbx, nby; // grid size (in elements, not pixels) let orgx, orgy; let perx, pery, pergrid; let colorMode = 0; let globalHue; // for animation let messages; // 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 mPIS3 = Math.PI / 3; const m2PI = Math.PI * 2; const m2PIS3 = Math.PI * 2 / 3; 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; //------------------------------------------------------------------------ function alea(mini, maxi) { // random number in given range if (typeof maxi == "undefined") return mini * mrandom(); // range 0..mini return mini + mrandom() * (maxi - mini); // range mini..maxi } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function intAlea(mini, maxi) { // random integer in given range (mini..maxi - 1 or 0..mini - 1) // if (typeof maxi == "undefined") return mfloor(mini * mrandom()); // range 0..mini - 1 return mini + mfloor(mrandom() * (maxi - mini)); // range mini .. maxi - 1 } function lerp(p0, p1, alpha) { return { x: p0.x * (1 - alpha) + p1.x * alpha, y: p0.y * (1 - alpha) + p1.y * alpha }; } //------------------------------------------------------------------------ class ExtremeFilter { /* tracks extreme points to build a linear gradient in direction given by constructor let D the oriented straight line passing by (0,0) in direction D makes projection of filtered points on D keeps track of points whose projection has min and max abscissa on D */ constructor(angle = 0) { this.min = Infinity; this.max = -Infinity; this.c = Math.cos(angle); this.s = Math.sin(angle); } filter(p) { let absc = p.x * this.c + p.y * this.s; if (absc < this.min) { this.min = absc; this.pmin = p; } if (absc > this.max) { this.max = absc; this.pmax = p; } } // filter filterArc(xc, yc, radius, starta, enda, ccw) { /* uses same signature as CanvasRenderingContext2D.arc does not accurately find extreme values, but filters a few points along the arc. Inaccuracy does not matter that much for a gradient */ let x, y, a; // make angles increasing along arc if (ccw) [starta, enda] = [enda, starta]; while (enda < starta) enda += m2PI; while (enda > starta + m2PI) enda -= m2PI; const ndiv = mceil((enda - starta) / 0.4); // vill divide arc in angles < 0.4 rad (0.4 : arbitrary value) if (ndiv == 0) ndiv = 1; // will do some extra work, but who cares ? for (let k = 0; k <= ndiv; ++k) { a = starta + k * (enda - starta) / ndiv; this.filter({ x: xc + radius * mcos(a), y: yc + radius * msin(a) }); } } // filterArc getLinearGradient() { /* creates a gradient without filling the stop points */ let delta = this.max - this.min; return ctx.createLinearGradient( this.pmin.x, this.pmin.y, this.pmin.x + delta * this.c, this.pmin.y + delta * this.s); }} // ExtremeFilter //------------------------------------------------------------------------ /* angles useful for the arcs */ const deltaAng0 = Math.acos(msqrt(2 / 3)); // const deltaAng1 = mPIS3 - deltaAng0; class Hexagon { constructor(kx, ky) { this.kx = kx; this.ky = ky; // this.rot = intAlea(6); // random orientation this.rot = pergrid[ky % pery][kx % perx].rot; this.arcTypes = []; this.exits = []; this.turn = []; for (let k = 0; k < 6; ++k) { this.exits[(k + this.rot) % 6] = ([5, 4, 1, 2, 3, 0][k] + this.rot) % 6; this.turn[(k + this.rot) % 6] = [2, 0, 2, 2, 2, -2][k]; // in 1/6th of turn this.arcTypes[(k + this.rot) % 6] = ["l", "b", "b", "b", "b", "l"][k]; /* encoding for arcTypes l/b = little/big */ } this.size(); this.lines = []; } // Hexagon.constructor // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - size() { // coordinates of centre this.xc = orgx + this.kx * 1.5 * radius; this.yc = orgy + this.ky * radius * rac3; if (this.kx & 1) this.yc -= radius * rac3s2; // odd columns, centre is a bit higher this.vertices = new Array(6).fill(0).map((v, k) => ({ x: this.xc + radius * mcos((k - 2) * mPI / 3), y: this.yc + radius * msin((k - 2) * mPI / 3) })); this.vertices[6] = this.vertices[0]; // makes things easier by avoiding many "% 6" in calculating other calculations this.middle = new Array(6). fill(0). map((p, k) => lerp(this.vertices[k], this.vertices[k + 1], 0.5)); this.extCenters = new Array(6).fill(0).map((v, k) => ({ x: this.xc + rac3 * radius * mcos(k * mPI / 3 - mPIS2), y: this.yc + rac3 * radius * msin(k * mPI / 3 - mPIS2) })); } // Hexagon.prototype.size // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - drawLittleArc(kCenter) { ctx.beginPath(); ctx.arc( this.vertices[kCenter].x, this.vertices[kCenter].y, radius / 2, kCenter * mPI / 3, (kCenter + 2) * mPIS3); ctx.stroke(); } drawBigArc(kCenter) { ctx.beginPath(); ctx.arc( this.extCenters[kCenter].x, this.extCenters[kCenter].y, 1.5 * radius, (kCenter + 1) * mPI / 3, (kCenter + 2) * mPIS3); ctx.stroke(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - drawArcs() { let c, radius, a0, a1, ccw; ctx.lineWidth = 2; ctx.strokeStyle = "#fff"; for (let k = 0; k < 6; ++k) { if ((k - this.rot + 6) % 6 == 5) continue; ({ c, radius, a0, a1, ccw } = this.getArcElements(k)); ctx.beginPath(); ctx.arc(c.x, c.y, radius, a0, a1, ccw); ctx.stroke(); } } //Hexagon.prototype.drawArcs // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - getArcElements(kEntry) { // arcs are defined starting from kEntry // returns (center, radius, a0,a1, ccw) switch ((kEntry + 6 - this.rot) % 6) { case 0: return { c: this.vertices[this.rot], radius: 0.5 * radius, a0: this.rot * mPIS3, a1: (this.rot + 2) * mPIS3, ccw: false }; case 1: return { c: this.extCenters[(this.rot + 2) % 6], radius: 1.5 * radius, a0: (this.rot + 4) % 6 * mPIS3, a1: (this.rot + 4) % 6 * mPIS3 - deltaAng0, ccw: true }; case 2: return { c: this.extCenters[(this.rot + 3) % 6], radius: 1.5 * radius, a0: (this.rot + 5) % 6 * mPIS3, a1: (this.rot + 4) % 6 * mPIS3 + deltaAng0, ccw: true }; case 3: return { c: this.extCenters[(this.rot + 2) % 6], radius: 1.5 * radius, a0: (this.rot + 3) % 6 * mPIS3, a1: (this.rot + 4) % 6 * mPIS3 - deltaAng0, ccw: false }; case 4: return { c: this.extCenters[(this.rot + 3) % 6], radius: 1.5 * radius, a0: (this.rot + 4) % 6 * mPIS3, a1: (this.rot + 4) % 6 * mPIS3 + deltaAng0, ccw: false }; case 5: return { c: this.vertices[this.rot], radius: 0.5 * radius, a0: (this.rot + 2) * mPIS3, a1: this.rot * mPIS3, ccw: true };} } getReverseArcElements(kExit) { const el = this.getArcElements(kExit); return { c: el.c, radius: el.radius, a0: el.a1, a1: el.a0, ccw: !el.ccw }; } // getReverseArcElements getCrossingElements(kEntry, kExit) { /* returns array of 1 or 2 arcElements, suitably oriented */ if (this.arcTypes[kEntry] == "l") // little arc: only one return [this.getArcElements(kEntry)]; return [this.getArcElements(kEntry), this.getReverseArcElements(kExit)]; } // getCrossingElements drawHexagon() { ctx.beginPath(); this.vertices.forEach((p, k) => { if (k == 0) ctx.moveTo(p.x, p.y);else ctx.lineTo(p.x, p.y); }); ctx.lineWidth = 0.25; ctx.strokeStyle = "#fff"; ctx.stroke(); } // Hexagon.prototype.drawHexagon // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - getNeighbor(edge) { const kx = this.kx + [0, 1, 1, 0, -1, -1][edge]; const ky = this.ky + [ [-1, 0, 1, 1, 1, 0], [-1, -1, 0, 1, 0, -1]][ this.kx & 1][edge]; if (kx < 0 || kx >= nbx || ky < 0 || ky >= nby) return false; return grid[ky][kx]; }} //class Hexagon //----------------------------------------------------------------------------- class Hierarchy { constructor(item) { this.item = item; this.children = []; } isDeeper(other) { // "other" is deeper than "this" ? /* "deeper" means "inside" in the context of nesting loops */ /* to build hierarchy : returns true if other must be in this's chidren (or grandchildren) returning false DOES NOT imply that other.isDeeper(this) returns true : they may be siblings or not directly related */ if (!this.item) return true; // "this" is top of hierarchy, all others are lower return ctx.isPointInPath( this.item.path, other.item.refP.x, other.item.refP.y); } insert(other) { /* must be called only if isDeeper(other) returs true (actually checked, or this is top) */ for (let k = 0; k < this.children.length; ++k) { if (this.children[k].isDeeper(other)) { // belongs to a child this.children[k].insert(other); return; } } // see if other is any of my children's ancestor for (let k = this.children.length - 1.........完整代码请登录后点击上方下载按钮下载查看
网友评论0