svg结合wtc-math生成春天的背景效果代码
代码语言:html
所属分类:背景
代码描述:svg结合wtc-math生成春天的背景效果代码
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <style> document, body { margin: 0; min-height: 100vh; } body { align-items: center; background: #F6FFFA; display: flex; font-family: "Montserrat", sans-serif; justify-content: center; } #container { align-items: center; display: flex; flex-direction: column; } #container > :first-child { cursor: pointer; } button { bottom: 30px; max-width: 200px; margin-top: 10px; position: fixed; } </style> </head> <body> <div id="container"> <div class="drawing"></div> </div> <script type="module"> import { SVG } from 'https://cdn.skypack.dev/@svgdotjs/svg.js' import { Vec2, Vec3 } from 'https://cdn.skypack.dev/wtc-math'; console.clear(); const config = { drawingType: 2, dimensions: new Vec2(window.innerWidth-20, window.innerHeight-20), r: 50, k: 32, }; const vars = { drawing: null } let resizeTimeout; window.addEventListener('resize', () => { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { document.querySelector('#container .drawing').innerHTML = ''; config.dimensions = new Vec2(window.innerWidth-20, window.innerHeight-20); vars.drawing = new Drawing(config.drawingType).addTo('#container .drawing').size(config.dimensions); setup(); }, 200); }) const setup = () => { vars.drawing.clear(); config.r = floatRandomBetween(40, 120); vars.bluenoise = new BlueNoise({ size: config.dimensions.addNew(new Vec2(config.r*2, config.r*2)), offset: new Vec2(config.r*-1, config.r*-1), r: config.r, k: config.k }); draw(); }; const drawStar = (pos, initialA = 0, ir = 10, spokes = 7, dims = new Vec2(4, 10)) => { for(let i = 0; i < spokes; i++) { const a = initialA + Math.PI * 2 / spokes * i; const oa = a - Math.PI * .5; const ip = new Vec2( Math.cos(a) * ir + pos.x, Math.sin(a) * ir + pos.y ); const u = ip.addNew( new Vec2( Math.cos(oa) * (dims.x * .5), Math.sin(oa) * (dims.x * .5) ) ); const v = u.addNew( new Vec2( Math.cos(a) * dims.y, Math.sin(a) * dims.y ) ); const x = v.addNew( new Vec2( Math.cos(oa) * (dims.x * -1), Math.sin(oa) * (dims.x * -1) ) ); const y = ip.addNew( new Vec2( Math.cos(oa) * (dims.x * -.5), Math.sin(oa) * (dims.x * -.5) ) ); vars.drawing.polygon( [u,v,x,y,u] ); } } const drawCurl = (pos, initialA = 0, ir = 20, points = 3, width = 5, revolutions = 3) => { const pointsDown = []; const pointsUp = []; const maxRev = (ir / width) - 1. / points; revolutions = Math.min(revolutions, maxRev); for(let i = 0; i < points*revolutions; i++) { const a = initialA + Math.PI * 2 / points * i; const oa = a - Math.PI * .5; const ip = new Vec2( Math.cos(a) * ir + pos.x, Math.sin(a) * ir + pos.y ); const u = ip.addNew( new Vec2( Math.cos(a) * (width * .5), Math.sin(a) * (width * .5) ) ); const v = ip.addNew( new Vec2( Math.cos(a) * (width * -.5), Math.sin(a) * (width * -.5) ) ); pointsDown.push(u); pointsUp.push(v); ir -= width / points; } vars.drawing.polyline( pointsDown.concat(pointsUp.reverse()).concat(pointsDown[0]) ); } const draw = () => { vars.drawing.rect(new Vec2(0,0), config.dimensions); while(vars.bluenoise.active.length > 0) { vars.bluenoise.step(); } vars.drawing.stroke = '#000000FF'; vars.drawing.lineWidth = 2; vars.bluenoise.news.forEach((n) => { const f = config.r/80; const annulus = new Vec2(floatRandomBetween(6*f, 12*f), floatRandomBetween(5*f,10*f)); vars.drawing.stroke = '#'+getColour(Math.random()); const which = Math.random(); if(which < .89) { drawStar( n.addNew(vars.bluenoise.offset), // position Math.random() * Math.PI, // initial angle annulus.x, // internal size Math.floor(floatRandomBetween(3, 12)), // number of spokes new Vec2(floatRandomBetween(2*f,4*f), annulus.y) // spoke size ); vars.drawing.circle(n.addNew(vars.bluenoise.offset), annulus.x*.25); } else if(which < .9) { drawCurl( n.addNew(vars.bluenoise.offset), // position, Math.random() * Math.PI, // initial angle annulus.x * 1.5, // internal size 20, // number of points floatRandomBetween(3*f,7*f), // width of stroke Math.floor(floatRandomBetween(1,6)) // The number of revolutions ); } else { drawCurl( n.addNew(vars.bluenoise.offset), // position, Math.random() * Math.PI, // initial angle annulus.x * 1.5, // internal size Math.floor(floatRandomBetween(3, 3)), // number of points floatRandomBetween(3*f,7*f), // width of stroke Math.floor(floatRandomBetween(3,6)) // The number of revolutions ); } }); const triangles = Delaunator.from(vars.bluenoise.news.map(n => n.array)); vars.drawing.stroke = '#00000011'; for(let i = 0; i < triangles.triangles.length; i += 3) { const t = [ vars.bluenoise.news[triangles.triangles[i+0]].addNew(vars.bluenoise.offset), vars.bluenoise.news[triangles.triangles[i+1]].addNew(vars.bluenoise.offset), vars.bluenoise.news[triangles.triangles[i+2]].addNew(vars.bluenoise.offset), vars.bluenoise.news[triangles.triangles[i+0]].addNew(vars.bluenoise.offset) ]; vars.drawing.polygon(t); } } const pal = ( t, a, b, c, d ) => { const mp = c.scaleNew(t).add(d).scale(6.28318); mp.x = Math.cos(mp.x); mp.y = Math.cos(mp.y); mp.z = Math.cos(mp.z); return a.addNew(b.multiplyNew(mp)); // return a + b*cos( 6.28318*(c*t+d) ); } const getColour = (d) => { d *= 100.; const col = pal( d/70+.65, new Vec3(0.5,0.5,0.5), new Vec3(0.5,0.1,0.2), new Vec3(1.0,1.0,1.0), new Vec3(0.6,0.1,0.2) ); const gc = (s) => { return (Math.floor(s * 255).toString(16)+'00').substring(0,2); } const colour = gc(col.x) + gc(col.y) + gc(col.z); // const a = Math.floor((1.-d/30)*255).toString(16); return colour; } setTimeout(() => { setup(); document.body.querySelector('#container>:first-child').addEventListener('click', () => { setup(); }); }, 500); class Drawing { static DT_CANVAS = 1; static DT_SVG = 2; #drawing; #ctx; #mode; #instructions = []; constructor(mode = Drawing.DT_CANVAS) { this.mode = mode; if(this.mode & Drawing.DT_CANVAS) { this.#drawing = document.createElement('canvas'); } else if(this.mode & Drawing.DT_SVG) { this.#drawing = SVG(); } this.stroke = '#333'; } clear() { if(this.mode & Drawing.DT_CANVAS) { this.c.clearRect(0,0,...this.dimensions.array); } else if(this.mode & Drawing.DT_SVG) { this.drawing.clear(); } } rect(position, dimensions) { this.#instructions.push({ f: 'rect', args: [position, dimensions] }); if(this.mode & Drawing.DT_CANVAS) { this.c.beginPath(); this.c.rect(...position.array, ...dimensions.array); this.c.stroke(); } else if(this.mode & Drawing.DT_SVG) { this.drawing.rect(dimensions.width, dimensions.height).move(...position.array).fill("none").stroke('#f06'); } } circle(position, radius) { this.#instructions.push({ f: 'circle', args: [position, radius] }); if(this.mode & Drawing.DT_CANVAS) { this.c.beginPath(); this.c.arc(position.x, position.y, radius, 0, 2 * Math.PI); if(this.stroke) this.c.stroke(); } else if(this.mode & Drawing.DT_SVG) { this.drawing.circle(radius*2).fill("none").stroke(this.stroke).move(...position.subtractScalarNew(radius).array); } } line(a, b) { this.#instructions.push({ f: 'line', args: [a, b] }); if(this.mode & Drawing.DT_CANVAS) { this.c.beginPath(); this.c.moveTo(a.x, a.y); this.c.lineTo(b.x, b.y); if(this.stroke) this.c.stroke(); } else if(this.mode & Drawing.DT_SVG) { this.drawing.line(...a.array, ...b.array).stroke(this.stroke); } } polyline(points) { this.#instructions.push({ f: 'polyline', args: points }); if(this.mode & Drawing.DT_CANVAS) { this.c.beginPath(); points.forEach((p, i) => { if(i === 0) this.c.moveTo(p.x, p.y); else this.c.lineTo(p.x, p.y); }) if(this.stroke) this.c.stroke(); } else if(this.mode & Drawing.DT_SVG) { this.drawing.polyline(points.map(p => p.array)).fill('none').stroke(this.stroke); } } polygon(points) { this.#instructions.push({ f: 'polygon', args: [points] }); if(this.mode & Drawing.DT_CANVAS) { this.c.beginPath(); points.forEach((p, i) => { if(i === 0) this.c.moveTo(p.x, p.y); else this.c.lineTo(p.x, p.y); }) if(this.stroke) this.c.stroke(); } else if(this.mode & Drawing.DT_SVG) { this.drawing.polygon(points.map(p => p.array)).fill('none').stroke(this.stroke); } } download() { let d; if(this.mode & Drawing.DT_CANVAS) { d = new Drawing(Drawing.DT_SVG).size(this.dimensions); this.#instructions.forEach((i) => { d[i.f](...i.args); }); } else if(this.mode & Drawing.DT_SVG) { d = this; } const fileName = "untitled.svg" const url = "data:image/svg+xml;utf8," + encodeURIComponent(d.drawing.svg()); const link = document.createElement("a"); link.download = fileName; link.href = url; link.click(); } addTo(element) { if(typeof(element) === 'string') { if(this.mode & Drawing.DT_CANVAS) { document.body.querySelector(element).appendChild(this.drawing); } else if(this.mode & Drawing.DT_SVG) { this.drawing.addTo(element); } } return this; } size(d) { if(this.mode & Drawing.DT_CANVAS) { this.drawing.width = d.width; this.drawing.height = d.height; } else if(this.mode & Drawing.DT_SVG) { this.drawing.size(...d.array); } this.#dimensions = d; return this; } #dimensions set dimensions(v) { if(v instanceof Vec2) { this.#dimensions = v; this.size(v); } } get dimensions() { return this.#dimensions; } get drawing() { return this.#drawing; } get c() { if(this.mode & Drawing.DT_CANVAS) { if(this.#ctx) return this.#ctx; this.#ctx = this.drawing.getContext('2d'); return this.#ctx; } } #stroke; set stroke(v) { this.#stroke = v; if(this.mode & Drawing.DT_CANVAS) { this.c.strokeStyle = v; } } get stroke() { return this.#stroke; } set mode(v) { if(v & (Drawing.DT_CANVAS | Drawing.DT_SVG)) { this.#mode = v; } } get mode() { return this.#mode || Drawing.DT_CANVAS; } } // vars.drawing = SVG().addTo('#container').size(config.dimensions.x, config.dimensions.y); vars.drawing = new Drawing(config.drawingType).addTo('#container .drawing').size(config.dimensions); /// Create the download button const dl = document.createElement('button'); dl.innerText = 'download'; dl.addEventListener('click', () => { vars.drawing.download(); }); document.body.querySelector('#container').appendChild(dl); class Grid { static #defaults = { size: config.dimensions.clone(), cellSize: new Vec2(50,50), fill: null }; #grid = []; #size; #cellSize; constructor(settings) { settings = Object.assign({}, Grid.#defaults, settings); this.#size = Vec2.interpolate(settings.size) || Grid.#defaults.size; this.#cellSize = Vec2.interpolate(settings.cellSize) || Grid.#defaults.cellSize; this.#grid.length = this.gridSize.area; this.#grid.fill(settings.fill || Grid.#defaults.fill); } addChild(child, i) { this.#grid[i] = child; } addChildAtPosition(child, pos) { this.#grid[this.getArrayPosition(pos)] = child; } addChildAtGridPosition(child, gridPos) { this.#grid[this.getArrayPositionFromGridPos(gridPos)] = child; } getChild(i) { return this.#grid[i]; } getChildAtPosition(pos) { return this.#grid[this.getArrayPosition(pos)]; } getChildAtGridPosition(gridPos) { return this.#grid[this.getArrayPositionFromGridPos(gridPos)]; } getGridPositionForIndex(i) { const gsize = this.gridSize; return new Vec2(i%gsize.x, Math.floor(i/gsize.x)); } getArrayPositionFromGridPos(gpos) { const gsize = this.gridSize; if( gpos.x < 0 || gpos.x >= gsize.x || gpos.y < 0 || gpos.y >= gsize.y ) return null; gpos.x = gpos.x % gsize.x; const arraypos = (gpos.x) + (gpos.y*gsize.x); return arraypos; } getArrayPosition(realPos) { const gpos = this.getGridPos(realPos); return this.getArrayPositionFromGridPos(gpos); } getGridPos(realPos) { if(realPos instanceof Vec2) { return realPos.divideNew(this.#cellSize).floor(); } // Throw an error } getSubPos(realPos) { if(realPos instanceof Vec2) { return realPos.modNew(this.#cellSize); } // Throw an error } getRealPos(gridPos) { if(gridPos instanceof Vec2) { return gridPos.multiplyNew(this.#cellSize); } // Throw an error } get size() { return this.#size; } get gridSize() { return this.#size.divideNew(this.#cellSize).floor(); } get cellSize() { return this.#cellSize; } get length() { return this.#grid.length; } } class BlueNoise { static #defaults = { size: config.dimensions.clone(), offset: new Vec2(0,0), r: 100, k: 32, d: 5, initialList: [new Vec2(375,250)] }; #activeList = []; #newPositions = []; #grid; #r; #k; #d; #size; #offset; constructor(settings) { settings = Object.assign({}, BlueNoise.#defaults, settings); this.#r = settings.r; this.#k = settings.k; this.#d = settings.d; this.#size = settings.size; this.#offset = settings.offset; this.#grid = new Grid({ size: this.#size, cellSize: this.#r / Math.SQRT2, // cellSize: 1. / Math.SQRT2 / this.#r, fill: -1 }); this.addElementAtPosition(...settings.initialList); } addElementAtPosition(...positions) { positions.forEach((pos) => { this.#grid.addChildAtPosition(pos, pos); this.#activeList.push(pos); this.#newPositions.push(pos); }); } draw() { this.news.forEach((newPos, i) => { const r = 3; const pos = newPos.addNew(this.#offset); vars.drawing.circle(pos, r); this.news[i] = null; }); this.#newPositions = this.news.filter(v => v !== null); // this.news.length = 0; } step() { const loopL = Math.min(this.active.length, this.#d); for(let l = 0; l < loopL; l++) { const ri = Math.floor(0, floatRandomBetween(this.active.length)); const c = this.active[ri]; let numfound = 0; for(var i = 0; i < this.#k; i++) { const a = floatRandomBetween(0, Math.PI*2); const l = floatRandomBetween(this.#r, this.#r*2); const pos = new Vec2(Math.cos(a)*l, Math.sin(a)*l).add(c); // console.log(this.grid.getChildAtPosition(pos)); if(this.grid.getChildAtPosition(pos) === -1) { const gridPos = this.grid.getGridPos(pos); let tooClose = false; for (var i = -1; i <= 1; i++) { for (var j = -1; j <= 1; j++) { if(i == 0 && j == 0) continue; const p = this.grid.getChildAtGridPosition(gridPos.addNew(new Vec2(i, j))); if(p !== -1 && p instanceof Vec2) { const d = pos.distance(p); if(d < this..........完整代码请登录后点击上方下载按钮下载查看
网友评论0