svg模仿指纹纹理效果代码
代码语言:html
所属分类:背景
代码描述:svg模仿指纹纹理效果代码
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <style> document, body { background: #111122; margin: 0; min-height: 100vh; } body { align-items: center; display: flex; justify-content: center; } #container { align-items: center; display: flex; flex-direction: column; } #container > :first-child { cursor: pointer; } button { max-width: 200px; margin-top: 10px; } .tracer { color: #fff; } </style> </head> <body> <div id="container"></div> <script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/svg.min.js"></script> <script type="module"> import { Vec2 } from 'https://cdn.skypack.dev/wtc-math'; import fitCurve from 'https://cdn.skypack.dev/fit-curves'; console.clear(); const config = { seed: 1337, drawingType: 1, dimensions: new Vec2(700, 700), nscale: .00125, sscale: 20, stepSize: 25, num: 1, r: 5, k: 8, testGridSize: 1, offset: new Vec2(10, -200), sneks: 200 }; const vars = { noise: null, grid: null, drawing: null } const setup = () => { const container = document.querySelector('#container'); config.offset.x = floatRandomBetween(-1000, 1000); config.offset.y = floatRandomBetween(-1000, 1000); config.nscale = floatRandomBetween(.000005, .002); container.innerHTML = ''; vars.drawing = new Drawing(config.drawingType).addTo('#container').size(config.dimensions); const t = document.createElement('div') t.className = 'tracer'; container.appendChild(t) vars.bluenoise = new BlueNoise({ size: config.dimensions.addNew(new Vec2(config.r*-1, config.r*-1)), offset: new Vec2(config.r*1, config.r*1), r: config.r, k: config.k }); vars.noise = new SimplexNoise(config.seed); vars.grid = new Grid({ cellSize: new Vec2(config.testGridSize, config.testGridSize), fill: -1 }); /// Create the download button // const dl = document.createElement('button'); // dl.innerText = 'download'; // dl.addEventListener('click', () => { // vars.drawing.download(); // }); // container.appendChild(dl); document.body.querySelector('#container>:first-child').addEventListener('click', () => { setup(); }); draw(); drawStep(); } let sneki = 0; const drawStep = () => { const sneks = []; if (vars.bluenoise.news.length > 0) { requestAnimationFrame(drawStep) for (let i = 0; i < config.sneks; i++) { const [p] = vars.bluenoise.news.splice(Math.floor(Math.random() * vars.bluenoise.news.length), 1.); if (!p) continue; if (p.subtractNew(config.dimensions.scaleNew(.5)).length > 200) continue; const pos = p; const dir = new Vec2(1, 0); const f = field(pos, 1., sneki+1); dir.angle = f.noise; // vars.drawing.stroke = '#ff0000AA'; // vars.drawing.circle(pos, 1); const distancebreak = 3; let cont = false; for (let x = -distancebreak*.5; x <= distancebreak*.5; x++) { for (let y = -distancebreak*.5; y <= distancebreak*.5; y++) { const offset = new Vec2(x, y); const t = vars.grid.getChildAtPosition(pos.addNew(offset)); if (t !== -1) cont = true; } } if (cont) continue; // vars.drawing.circle(pos, .5); const s = new Snek(pos, { direction: dir, maxLength: 2000, id: sneki++, distanceProjection: 5, distanceBreak: distancebreak, projectionBreakMultiplier: 0 }); sneks.push(s); // projectionBreakMultiplier: 2 // } } // for(let j = 0; j < config.lineLength; j++) { for (let i = 0; i < sneks.length; i++) { sneks[i].walkOut(); } // } // document.querySelector('.tracer').innerHTML = vars.bluenoise.news.length; for (let i = 0; i < sneks.length; i++) { vars.drawing.path(sneks[i].bezier); } } let interval; const draw = () => { vars.drawing.stroke = '#AAA'; vars.drawing.c.fillStyle = "#111122"; vars.drawing.c.fillRect(0, 0, config.dimensions.x, config.dimensions.y); vars.drawing.rect(new Vec2(0, 0), config.dimensions); vars.drawing.c.lineWidth = 1.5; while (vars.bluenoise.active.length > 0) { vars.bluenoise.step(); } } setTimeout(() => { setup(); }, 500); const ngon = (pos, r, nsides) => { const a = Math.atan2(pos.y, pos.x) + Math.PI*.5; const split = (Math.PI*2) / nsides; return pos.length * Math.cos(split * Math.floor(.5 + a / split) - a) - r; } const field = (pos, id = 1, instanceID = 0)=> { const normpos = pos.subtractNew(config.dimensions.scaleNew(.5)); const mask1 = ngon(normpos, 200, 40) < Math.random()*10; const mask2 = ngon(normpos.addNew(new Vec2(0, 50)), 250, 3) < 0.; let mask; if (!id) { if (mask2) { id = 2.; } else if (mask1) { id = 1.; } } let n; if (id == 1.) { n = vars.noise.noise2D(pos.addNew(config.offset).scale(config.nscale).array)*Math.PI; // const a = Math.atan2(normpos.y, normpos.x); // n = a - .5; } else if (id == 2.) { // const a = Math.atan2(normpos.y, normpos.x) + Math.PI * .5; // n = a + .8; const a = Math.atan2(normpos.y, normpos.x); n = a + .5; // n = noise.noise2D(pos.addNew(config.offset).scale(config.nscale).array)*Math.PI; } // let apos = pos.subtractNew(dimensions.scaleNew(.5)); // let a = Math.atan2(apos.y, apos.x) + Math.PI * .5; // const n = noise.noise2D(pos.addNew(config.offset).scale(config.nscale).array)*Math.PI; // const n = a + .8; // const mask = pos.subtractNew(dimensions.scaleNew(.5)).length < 350; // const mask = ngon(normpos, 150, 3) < 0.; return { noise: n, mask: instanceID % 3 == 0 ? true: mask1, id: id }; } class Snek { static #defaults = { grid: vars.grid, head: true, tail: true, direction: new Vec2(1, 0), maxLength: 1000, stepSize: 1, fieldID: 1, distanceBreak: 5, distanceProjection: 5, minLength: 10, id: 0, bailout: 10000, projectionBreakMultiplier: .5 }; #alive = [true, true]; /* Two alive directions per snek */ #directions = [0, 0]; /* The direction of the head and tail */ #positions = [0, 0]; /* The position of the head and tail */ #maxLength = 1000; /* The maximum length of the whole snake */ #stepSize = 1; #fieldID = 1; #distanceBreak = 5; #distanceProjection = 5; #minLength = 5; #points = []; #id = 0; #bailout = 10000; #projectionBreakMultiplier = .5; constructor(pos, settings) { settings = Object.assign({}, Snek.#defaults, settings); this.#alive[0] = settings.head === true; this.#alive[1] = settings.tail === true; this.directionHead = settings.direction.clone(); this.directionTail = settings.direction.rotateNew(Math.PI); this.#positions[0] = pos.clone(); this.#positions[1] = pos.clone(); this.#fieldID = settings.fieldID; this.#distanceBreak = settings.distanceBreak; this.#distanceProjection = settings.distanceProjection; this.#maxLength = settings.maxLength; this.#minLength = settings.minLength; this.#id = settings.id; this.#bailout = settings.bailout; this.#projectionBreakMultiplier = settings.projectionBreakMultiplier; } walkOut() { // console.log(this.length, this.#maxLength) let i = 0; while (this.length < this.#maxLength) { if (i++ > this.#bailout) break; if (this.dead) break; this.walk(this.#stepSize); } this.dead = true; } walk(distance, topTail = 0) { if (this.#alive[topTail]) { const dir = this.#directions[topTail]; const pos = this.#positions[topTail].addNew(dir.scaleNew(distance)); const f = field(pos, this.#fieldID, this.#id); const t = vars.grid.getChildAtPosition(pos); let draw = true; let i = -this.#distanceBreak; let a = dir.clone(); a.angle -= Math.PI * .5; while (i < this.#distanceBreak) { const p = pos.addNew(a.scaleNew(i)); const t = vars.grid.getChildAtPosition(p); if (t === -1 || t === this) {} else { draw = false; break; } i += config.testGridSize; } const dp = this.#distanceProjection; let np = pos.clone(); const ndir = dir.clone(); for (let i = 0; i < dp; i++) { if (draw === false) break; np.add(ndir.scaleNew(distance)); const nf = field(np, this.#fieldID, this.#id); ndir.angle = nf.noise; let j = -this.#distanceBreak*this.#projectionBreakMultiplier; let a = ndir.clone(); a.angle -= Math.PI * .5; while (j <= this.#distanceBreak*this.#projectionBreakMultiplier) { const p = np.addNew(a.scaleNew(j)); const t = vars.grid.getChildAtPosition(p); if (t === -1 || t === this) {} else { draw = false; break; } j += config.testGridSize; } } if (!f.mask) draw = false; if (draw) { if (topTail === 0) { this.#points.push(pos.clone()); } else { this.#points.splice(0, 0, pos.clone()); } vars.grid.addChildAtPosition(this, pos); this.#positions[topTail] = pos.clone(); } else { this.#alive[topTail] = false; } dir.angle = f.noise; if (topTail === 1) dir.rotate(Math.PI); } if (topTail === 0 && this.#alive[1]) { this.walk(distance, 1) } } get length() { return this.#points.length * this.#stepSize; } set dead(v) { if (v === false) { this.#alive[0] = false; this.#alive[1] = false; } else if (v === true) { this.#alive[0] = true; } } get dead() { return !this.#alive[0] && !this.#alive[1]; } get points() { let points = this.#points; if (points.length * this.#stepSize < this.#minLength) { points.forEach((p) => { vars.grid.addChildAtPosition(-1, p); }); return null; } return points.map(p => p.array); } get bezier() { const points = this.points; if (!points?.length || points.length <= 1) return; const bezierCurves = fitCurve(this.points, 1); let str = ""; bezierCurves.map(function(bezier, i) { if (i == 0) { str += "M " + bezier[0][0] + " " + bezier[0][1]; } str += "C " + bezier[1][0] + " " + bezier[1][1] + ", " + bezier[2][0] + " " + bezier[2][1] + ", " + bezier[3][0] + " " + bezier[3][1] + " "; }); return str; } get id() { return this.#fieldID; } set directionHead(v) { if (v instanceof Vec2) this.#directions[0] = v; } get directionHead() { return this.#directions[0]; } set directionTail(v) { if (v instanceof Vec2) this.#directions[1] = v; } get directionTail() { return this.#directions[1]; } } 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) { if (this.saving) { 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) { if (this.saving) { 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) { if (this.saving) { 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) { if (this.saving) { 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); } } path(path) { if (this.mode & Drawing.DT_CANVAS) { this.c.beginPath(); const p = new Path2D(path); if (this.stroke) this.c.stroke(p); } else if (this.mode & Drawing.DT_SVG) { this.drawing.path(path).fill('none').stroke(this.stroke); } } polygon(points) { if (this.saving) { 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('#container'); } } 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; } #saving = false set saving(v) { this.#saving = v === true; } get saving() { return this.#saving; } } // vars.drawing = SVG().addTo('#container').size(config.dimensions.x, config.dimensions.y); 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 =.........完整代码请登录后点击上方下载按钮下载查看
网友评论0