RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1560785
Accepted
BlackStar1991
BlackStar1991
Asked:2024-01-09 05:14:25 +0000 UTC2024-01-09 05:14:25 +0000 UTC 2024-01-09 05:14:25 +0000 UTC

SVG 如何生成随机路径?

  • 772

有这样一个简单的例子。

const btn = document.getElementById('generator_btn');
const textarea = document.getElementById('textarea_generator');
const generatorField = document.getElementById('generator_field');
const generatorColor = document.getElementById('generator_color');

// Массив возможных путей для фигур ... это надо переделать, но не знаю как. 
const paths = [
  '<path class="st1" d="M1.8 23s15.2-7.7 24-7.9 14.4-.5 23.7 7.5c9.3 8 3.6 17.9-1 20-2.7 1.3-7.9.6-6.2-5.5 1.6-5.6 7.7-9.1 16.6-12.4 8.9-3.3 28.9-8.5 32.9-12.9 2.3-2.6 2.8-6-.3-7-2-.6-5.8.6-8.1 2.9C81.3 10 78.3 15.3 79 39c.5 19-20 22.2-25.1 17.8-6.1-5.2 1.5-15.4 8.7-18.7 7.2-3.3 14.7-5.9 21.1-3.1 6.4 2.8 10.8 5 13.5 5.3 2.7.2 9.3 0 15.4-6.9"/><path class="st1" d="m110.9 40.6 4.4-9.8-10.5 3.6"/>',
  '<path class="st1" d="M1.8 40.1s4.9-10 10.8-14.8c5.9-4.8 14.4-6.3 17.6-5.8 5.9.8 12.7 6.2 19.3 12.7 6.7 6.5 14.8 14.2 28.7 6.1 7.2-4.2 15.3-17.1 7.2-25.5-6.4-6.7-18 2.9-21.7 11.7-1.9 4.6-5 20 2.7 25.7 7.8 5.8 17.2 3.1 27.4-4.4 10.2-7.6 22.6-24 24-27"/><path class="st1" d="m112.6 21.1 5.6-3.9.5 8.7"/>',
  '<path class="st1" d="M.8 41.8s14-21 28.3-14.3c14.4 6.7 7.2 20.3 1.7 18.5-5.9-1.9-7-20 9.4-21.8s23.6 5 32.7 8.5c9.1 3.5 33.8 6.6 44.3-10.2"/><path class="st1" d="m111.2 25.9 6.2-4-.8 6"/>'
];

generatorColor.addEventListener('input', function() {
  includeSvg();
});

btn.addEventListener('click', function() {
  includeSvg();
});

function includeSvg() {
  let currentColor = generatorColor.value;
  // 
  let randomPath = paths[Math.floor(Math.random() * paths.length)];
  let svgCode = generateSVG(currentColor, randomPath);
  textarea.value = svgCode;
  generatorField.innerHTML = svgCode;
}

function generateSVG(currentColor, path) {
  let startFigure = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120" height="60" x="0" y="0" viewBox="0 0 120 64"><style>.st1{fill:none;stroke:' + currentColor + ';stroke-width:2;stroke-linecap:round;stroke-linejoin:round}</style>';
  let endFigure = '</svg>';
  return startFigure + path + endFigure;
}
body {
  margin: 0;
}

#generator_btn {
  margin: 0;
  display: inline-block;
  padding: 10px 15px;
  font-weight: 700;
  vertical-align: top;
}

#generator_color {
  height: 40px;
}

#textarea_generator {
  display: block;
  width: 100%;
  max-width: 90vw;
  margin: 10px 0;

}
<div class="gen_block">
  <button id="generator_btn">Generator SVG</button>
  <input type="color" id="generator_color" name="generator_color" value="#FE6C3B" />
</div>
<textarea id="textarea_generator" name="generator_svg" rows="4"></textarea>
<div id="generator_field"></div>

这个想法是通过单击按钮来创建一个像这样的随机路径生成器......很疯狂,但我不知道如何实现它。本质上,svg该元素由两条路径组成。第一条是一条令人困惑的曲线(但应该有一个平滑的过渡),第二条是一条在此路径中结束的箭头。svg嗯,生成的路径的动态着色,以便您可以在保存之前为元素分配颜色。请告诉我如何在元素的这个区域内创建这些随机路径?

javascript
  • 4 4 个回答
  • 209 Views

4 个回答

  • Voted
  1. Best Answer
    Stanislav Volodarskiy
    2024-01-16T20:56:34Z2024-01-16T20:56:34Z

    代码中添加了一个按钮,用于显示构造曲线的控制段。这些段并非完全随机:第一个段从设计的左边缘开始,然后几个段位于设计的顶部和底部边缘,最后一个段在右边缘结束。

    所有控制段的末端都位于绘图的内部或边缘。这确保了曲线也完全位于内部。

    const scale = (v, f) => {
        const [x, y] = v;
        return [f * x, f * y];
    };
    
    const normalize = v => scale(v, 1 / Math.hypot(...v));
    
    const rotate = (v, a) => {
        const [x, y] = v;
        const c = Math.cos(a);
        const s = Math.sin(a);
        return [c * x - s * y, s * x + c * y];
    };
    
    const round = (v, f) => {
        const [x, y] = v;
        return [Math.round(f * x) / f, Math.round(f * y) / f];
    };
    
    const curveD = (segments, withArrow) => {
        const n = segments.length;
        let d = '';
    
        const [x1, y1] = segments[0][0];
        d += `M ${x1} ${y1}`;
    
        for (let i = 1; i < n - 1; ++i) {
            let [x2, y2] = segments[i - 1][1];
            let [[x3, y3], [x4, y4]] = segments[i];
            d += ` C ${x2} ${y2}, ${x3} ${y3}, ${(x3 + x4) / 2} ${(y3 + y4) / 2}`;
        }
    
        let [x2, y2] = segments[n - 2][1];
        let [[x3, y3], [x4, y4]] = segments[n - 1];
        d += ` C ${x2} ${y2}, ${x3} ${y3}, ${x4} ${y4}`;
    
        if (withArrow) {
            const v = scale(normalize([x3 - x4, y3 - y4]), 10);
            const [rx, ry] = round(rotate(v, Math.PI / 8), 100);
            const [lx, ly] = round(rotate(v, -Math.PI / 8), 100);
    
            d += ` l ${rx} ${ry} m ${-rx} ${-ry} l ${lx} ${ly}`;
        }
    
        return d;
    };
    
    const controlsD = segments => {
        const n = segments.length;
        let d = '';
        const r = 2;
    
        const drawPoint = (x, y) => {
            d += ` M ${x + r} ${y} a ${r} ${r} 0 0 0 ${-2 * r} 0`;
            d += ` M ${x - r} ${y} a ${r} ${r} 0 0 0 ${2 * r} 0`;
        };
    
        for (let i = 0; i < n; ++i) {
            const [[x1, y1], [x2, y2]] = segments[i];
            d += ` M ${x1} ${y1} L ${x2} ${y2}`;
            drawPoint(x1, y1);
            if (0 < i && i < n - 1) {
                drawPoint((x1 + x2) / 2, (y1 + y2) / 2);
            }
            drawPoint(x2, y2);
        }
        return d;
    };
    
    const drawRandomCurve = (svg, curve, controls, textarea) => {
        const width = svg.getAttribute('width');
        const height = svg.getAttribute('height');
    
        const randomX = () => Math.floor(width * Math.random());
        const randomY = () => Math.floor(height * Math.random());
    
        const randomControlSegments = n => {
            const segments = [];
    
            segments.push([
                [0, randomY()],
                [width - 1 - Math.floor(0.8 * randomX()), randomY()]
            ]);
    
            let y = (height - 1) * Math.floor(2 * Math.random());
            for (let i = 1; i < n - 1; ++i) {
                const p1 = [randomX(), y];
                y = height - 1 - y;
                const p2 = [randomX(), y];
                segments.push([p1, p2]);
            }
    
            segments.push([
                [Math.floor(0.8 * randomX()), randomY()],
                [width - 1, randomY()]
            ]);
    
            return segments;
        };
    
        const segments = randomControlSegments(5);
        const d = curveD(segments, true);
    
        curve.setAttribute('d', d);
        textarea.value = d;
    
        controls.setAttribute('d', controlsD(segments));
    };
    
    
    const doDraw = () => drawRandomCurve(
        document.getElementById('board'),
        document.getElementById('curve'),
        document.getElementById('controls'),
        document.getElementById('text')
    );
    document.getElementById('draw!').onclick = doDraw;
    doDraw();
    
    const showControls = () => {
        const show = document.getElementById('show_controls').checked;
        document.getElementById('controls').setAttribute(
            'visibility',
            (show) ? 'visible' : 'hidden'
        );
    };
    document.getElementById('show_controls').onclick = showControls;
    showControls();
    <button id="draw!">Обновить кривую</button>
    <br>
    <textarea id="text" cols="60", rows="3"></textarea>
    <br>
    <svg id="board" width="120" height="60" style="border: 1px solid gray;">
    <path id="curve" stroke="black" stroke-width="2" fill="transparent"></path>
    <path id="controls" stroke="red" stroke-width="1" fill="transparent" visibility="hidden"></path>
    <br>
    <input type="checkbox" id="show_controls" name="show_controls" />
    <label for="show_controls">показать контрольные отрезки</label>
    </svg>

    • 7
  2. puffleeck
    2024-01-14T22:19:06Z2024-01-14T22:19:06Z

    要了解语法,请SVG path在此处查看三次贝塞尔曲线的运算符:

    https://developer.mozilla.org/ru/docs/Web/SVG/Tutorial/Paths

    其余信息也不会造成影响,但这是本示例框架内的主要内容。

    ps实际上是添加了一行然后随机添加的,所以结果是这样的...)))

    // 根据你的回答


    路人注意事项:

    如果您想为您的护照发明一个签名,那么这个是完美的:3

    更新 15 01 2024 / 19:25

    拉伸视图框,放大屏幕截图,让它随机变得更有趣,并破坏它,这样它就不会干扰滚动:3

    更新 16 01 2024 / 23:35

    添加了箭头标记

    来源https://developer.mozilla.org/en-US/docs/Web/SVG/Element/marker

    片段中的 ps 因为放大了,观看没有用,展开到全屏

    const btn = document.getElementById('generator_btn');
    const textarea = document.getElementById('textarea_generator');
    const generatorField = document.getElementById('generator_field');
    const generatorColor = document.getElementById('generator_color');
    
    const W = 150;
    const H = 125; /*!!!!!!!!!!*/
    const MIN_ANGLE = 60;
    const MIN_DISTANCE = (Math.min(W, H)) / 20;
    const MAX_DISTANCE = (Math.min(W, H)) / 4;
    let lastTwoPoints = [];
    
    generatorColor.addEventListener('input', function() {
      let currentSvg = textarea.value;
      let strokeValue = /stroke:[^;]+;/.exec(currentSvg)[0];
      let updatedSvg = currentSvg.replace(strokeValue, 'stroke:' + generatorColor.value + ';');
      textarea.value = updatedSvg;
      generatorField.innerHTML = updatedSvg;
    });
    
    btn.addEventListener('click', function() {
      includeSvg();
    });
    
    function includeSvg() {
      let currentColor = generatorColor.value;
      let randomPath = generateRandomPath();
      let svgCode = generateSVG(currentColor, randomPath);
      textarea.value = svgCode;
      generatorField.innerHTML = svgCode;
    }
    
    function generateRandomPath() {
      const startX = getRandomNumber(H * 0.8);
      const startY = getRandomNumber(H * 0.8);
      let pathString = `M${startX} ${startY} `;
    var t=document.getElementById('t');
    
    let x1, x2, x3, x4, y1, y2, y3, y4;
    let q = 1.3; // коэфицент отклонения контрольной точки
      for (let i = 0; i < 2; i++) {
    x1 = getRandomNumber(H * 0.35 + ((i+i)*15));
    y1 = getRandomNumber(H * 0.35);
    x2 = getRandomNumber(H * 0.75);
    y2 = getRandomNumber(H * 0.35);
    x3 = getRandomNumber(H * 0.75);
    y3 = getRandomNumber(H * 0.75);
    x4 = getRandomNumber(H * 0.35);
    y4 = getRandomNumber(H * 0.75);
    
    pathString += '\n' + (i == 0 ? 'Q' : 'T') +
    ` ${x1} ${y1}, ${x1/q} ${y1/q} 
    T ${x2} ${y2}, ${x2*q} ${y2/q}
    T ${x3} ${y3}, ${x3*q} ${y3*q}
    T ${x4} ${y4}, ${x4/q} ${y4*q}`;
      }
      t.value=pathString;
      return '<path marker-end="url(#arrow)" d="' + pathString + '"/>';
    }
    
    function getDistance(pointA, pointB) {
      return Math.sqrt((pointA[0] - pointB[0]) ** 2 + (pointA[1] - pointB[1]) ** 2);
    }
    
    
    function getAngle(pointA, pointB, pointC) {
      let a = getDistance(pointA, pointB);
      let b = getDistance(pointB, pointC);
      let c = getDistance(pointC, pointA);
      return Math.acos((a * a + b * b - c * c) / (2 * a * b)) * (180 / Math.PI);
    }
    
    
    function getRandomPoint() {
      let x = getRandomNumber(W * 0.6) + W * 0.2;
      let y = getRandomNumber(H * 0.6) + H * 0.2;
    
      let point = [x, y];
    
      if (lastTwoPoints.length < 2) {
        lastTwoPoints.push(point);
      } else {
        if (getAngle(...lastTwoPoints, point) < MIN_ANGLE ||
          getDistance(lastTwoPoints[1], point) < MIN_DISTANCE ||
          getDistance(lastTwoPoints[1], point) > MAX_DISTANCE) {
          point = getRandomPoint();
        } else {
          lastTwoPoints.shift();
          lastTwoPoints.push(point);
        }
      }
    
      return `${point[0]} ${point[1]}`;
    }
    
    function generateSVG(currentColor, path) {
      let startFigure = '<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 500 500"><style>path{fill:none;stroke:' + currentColor + ';stroke-width:2;stroke-linecap:round;stroke-linejoin:round}</style>';
      let endFigure = '</svg>';
      let arrow = ` <defs>
        <!-- A marker to be used as an arrowhead -->
        <marker
          id="arrow"
          viewBox="0 0 10 10"
          refX="5"
          refY="5"
          markerWidth="6"
          markerHeight="6"
          orient="auto-start-reverse">
          <path d="M 0 0 L 10 5 L 0 10 z" />
        </marker>
      </defs>`;
      /********************************************/
      return startFigure + path + arrow + endFigure;
    }
    
    function getRandomNumber(max) {
      return (Math.floor(Math.random() * max)) + 225;/*!!!!!!*/
    }
    body {margin: 0;}
    #generator_color {height: 40px;}
    #textarea_generator, #t{width: 47%;}
    
    #generator_btn {
      margin: 0;
      display: inline-block;
      padding: 10px 15px;
      font-weight: 700;
      vertical-align: top;
    }
    <div class="gen_block">
    <button id="generator_btn">Generator SVG</button>
    <input type="color" id="generator_color" name="generator_color" value="#FE6C3B" />
    </div>
    <textarea id='t'></textarea>
    <textarea id="textarea_generator" name="generator_svg"></textarea>
    
    <div id="generator_field"></div>

    • 5
  3. BlackStar1991
    2024-01-13T22:42:32Z2024-01-13T22:42:32Z

    感谢@XelaNimed 的选择,我们成功创建了一个随机路径生成器。但质量不适合我......它不适用于循环,只能使用轨道附近的随机直线。也许有人知道如何改进它,使其看起来更平滑并且不会超出该区域的过道SVG?

    const btn = document.getElementById('generator_btn');
    const textarea = document.getElementById('textarea_generator');
    const generatorField = document.getElementById('generator_field');
    const generatorColor = document.getElementById('generator_color');
    
    const W = 120;
    const H = 64;
    const MIN_ANGLE = 60;
    const MIN_DISTANCE = (Math.min(W, H)) / 20;
    const MAX_DISTANCE = (Math.min(W, H)) / 4;
    let lastTwoPoints = [];
    
    generatorColor.addEventListener('input', function() {
      let currentSvg = textarea.value;
      let strokeValue = /stroke:[^;]+;/.exec(currentSvg)[0];
      let updatedSvg = currentSvg.replace(strokeValue, 'stroke:' + generatorColor.value + ';');
      textarea.value = updatedSvg;
      generatorField.innerHTML = updatedSvg;
    });
    
    btn.addEventListener('click', function() {
      includeSvg();
    });
    
    function includeSvg() {
      let currentColor = generatorColor.value;
      let randomPath = generateRandomPath();
      let svgCode = generateSVG(currentColor, randomPath);
      textarea.value = svgCode;
      generatorField.innerHTML = svgCode;
    }
    
    function generateRandomPath() {
      const startX = getRandomNumber(W * 0.6) + W * 0.2;
      const startY = getRandomNumber(H * 0.6) + H * 0.2;
      let pathString = `<path d="M${startX} ${startY} `;
    
      for (let i = 0; i < 10; i++) {
        const controlX1 = getRandomNumber(W * 0.6) + W * 0.2;
        const controlY1 = getRandomNumber(H * 0.6) + H * 0.2;
        const controlX2 = getRandomNumber(W * 0.6) + W * 0.2;
        const controlY2 = getRandomNumber(H * 0.6) + H * 0.2;
        const endX = getRandomNumber(W * 0.6) + W * 0.2;
        const endY = getRandomNumber(H * 0.6) + H * 0.2;
    
        pathString += `${controlX1} ${controlY1} ${controlX2} ${controlY2} ${endX} ${endY}`;
      }
    
      return pathString + '"/>';
    }
    
    function getDistance(pointA, pointB) {
      return Math.sqrt((pointA[0] - pointB[0]) ** 2 + (pointA[1] - pointB[1]) ** 2);
    }
    
    
    function getAngle(pointA, pointB, pointC) {
      let a = getDistance(pointA, pointB);
      let b = getDistance(pointB, pointC);
      let c = getDistance(pointC, pointA);
      return Math.acos((a * a + b * b - c * c) / (2 * a * b)) * (180 / Math.PI);
    }
    
    
    function getRandomPoint() {
      let x = getRandomNumber(W * 0.6) + W * 0.2;
      let y = getRandomNumber(H * 0.6) + H * 0.2;
    
      let point = [x, y];
    
      if (lastTwoPoints.length < 2) {
        lastTwoPoints.push(point);
      } else {
        if (getAngle(...lastTwoPoints, point) < MIN_ANGLE ||
          getDistance(lastTwoPoints[1], point) < MIN_DISTANCE ||
          getDistance(lastTwoPoints[1], point) > MAX_DISTANCE) {
          point = getRandomPoint();
        } else {
          lastTwoPoints.shift();
          lastTwoPoints.push(point);
        }
      }
    
      return `${point[0]} ${point[1]}`;
    }
    
    function generateSVG(currentColor, path) {
      let startFigure = '<svg xmlns="http://www.w3.org/2000/svg" width="240" height="128" viewBox="0 0 120 64"><style>path{fill:none;stroke:' + currentColor + ';stroke-width:2;stroke-linecap:round;stroke-linejoin:round}</style>';
      let endFigure = '</svg>';
      return startFigure + path + endFigure;
    }
    
    function getRandomNumber(max) {
      return Math.floor(Math.random() * max);
    }
    body {
      margin: 0;
    }
    
    #generator_btn {
      margin: 0;
      display: inline-block;
      padding: 10px 15px;
      font-weight: 700;
      vertical-align: top;
    }
    
    #generator_color {
      height: 40px;
    }
    
    #textarea_generator {
      display: block;
      width: 100%;
      max-width: 90vw;
      margin: 10px 0;
    }
    <div class="gen_block">
      <button id="generator_btn">Generator SVG</button>
      <input type="color" id="generator_color" name="generator_color" value="#FE6C3B" />
    </div>
    <textarea id="textarea_generator" name="generator_svg" rows="10"></textarea>
    <div id="generator_field"></div>

    • 2
  4. Serg Bakay
    2024-01-09T22:37:21Z2024-01-09T22:37:21Z

    尝试使用类似的库: kute.js

    • -1

相关问题

  • 第二个 Instagram 按钮的 CSS 属性

  • 由于模糊,内容不可见

  • 弹出队列。消息显示不正确

  • 是否可以在 for 循环中插入提示?

  • 如何将 JSON 请求中的信息输出到数据表 Vuetify vue.js?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5