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