繁体   English   中英

在SVG中画一条“波浪线”

[英]Draw a “squiggly line” in SVG

我正在尝试找出如何在任意SVG path元素上绘制波浪线。 该路径由React组件生成。 例如,我正在尝试复制此问题中的行:

波浪线示例

在SVG和/或JavaScript生成的路径中,有没有简便的方法可以做到这一点?

我已经考虑过使用s path命令连接一系列曲线,但是随后我需要计算曲线上的点。 我也考虑过某种位移滤波器,但我不确定从哪里开始。

在我看来,最简单的方法就是走这条路。 然后,在每个步骤中,插入一个二次贝塞尔曲线,其控制点位于它们之间的一半且垂直于该曲线。 然后,为下一步切换控制点打开的一侧。

 function makeSquiggle(squigglePathId, followPathId, squiggleStep, squiggleAmplitude) { var followPath = document.getElementById(followPathId); var pathLen = followPath.getTotalLength(); // Adjust step so that there are a whole number of steps along the path var numSteps = Math.round(pathLen / squiggleStep); var pos = followPath.getPointAtLength(0); var newPath = "M" + [pos.x, pos.y].join(','); var side = -1; for (var i=1; i<=numSteps; i++) { var last = pos; var pos = followPath.getPointAtLength(i * pathLen / numSteps); // Find a point halfway between last and pos. Then find the point that is // perpendicular to that line segment, and is squiggleAmplitude away from // it on the side of the line designated by 'side' (-1 or +1). // This point will be the control point of the quadratic curve forming the // squiggle step. // The vector from the last point to this one var vector = {x: (pos.x - last.x), y: (pos.y - last.y)}; // The length of this vector var vectorLen = Math.sqrt(vector.x * vector.x + vector.y * vector.y); // The point halfwasy between last point and tis one var half = {x: (last.x + vector.x/2), y: (last.y + vector.y/2)}; // The vector that is perpendicular to 'vector' var perpVector = {x: -(squiggleAmplitude * vector.y / vectorLen), y: (squiggleAmplitude * vector.x / vectorLen)}; // No calculate the control point position var controlPoint = {x: (half.x + perpVector.x * side), y: (half.y + perpVector.y * side)}; newPath += ("Q" + [controlPoint.x, controlPoint.y, pos.x, pos.y].join(',')); // Switch the side (for next step) side = -side; } var squigglePath = document.getElementById(squigglePathId); squigglePath.setAttribute("d", newPath); } makeSquiggle("squiggle", "follow", 25, 20); 
 #follow { fill: none; stroke: grey; stroke-width: 2; } #squiggle { fill: none; stroke: red; stroke-width: 2; } 
 <svg width="500" height="400"> <path id="follow" d="M 50,300 C 100,100 300,0, 350,250 L 450,200"/> <path id="squiggle" d="M0,0"/> </svg> 

最好的方法可能只是将贝塞尔曲线连接起来,然后偏移并反转要创建的每条后续曲线的值,直到达到所需的长度。

功能类似的解决方案

围绕曲线绘制cos(x)

注意 :f(x)是弯曲线应遵循的曲线,x0是开始绘制弯曲线的位置,xn是结束位置的位置,本示例假定x0和xn之间的f(x)不断增长

如果您有一条栅格化的曲线并且要绘制的弯曲线是cos(x),则可以找到绘制该线的点,如下所示:

  • 弯曲地画一条你想画的线
  • 弯曲弯曲线应遵循的曲线
  • point0曲线起点
  • pointN是x == N的曲线
  • lenN是曲线的从point0长度焦点N
  • H(在图像)是在焦点N 曲线弯曲线之间的距离,为cos(lenN)
  • alphaN是x == n的曲线切线与x轴之间的角度
  • a (在图像中)是-cos( alphaN
  • b (在图像中)是sin( alphaN
  • squigglyPointN焦点N .X + A, 对焦点N .Y + B

  'use strict' // // point = [int, int], point[0] = x, point[1] = y // rasterizedCurve = [point0, ...,pointN] // // int -> [int,...,int] function rangeFrom1ToN(N) { return Array(N).fill(0).map((x, index) => index).slice(1); } // [int, ...,int] -> [float, ..., float] function expandRange(Range, precision) { return Range.map(x => rangeFrom1ToN(precision).map((y, index) => x + 1/precision * index)) .reduce((acc, val) => acc.concat(val)); } function formatForSvg(points) { return points.map(x => x.toString()).reduce((acc, val) => {return acc + ' ' + val}) } // rasterizedCurve, index -> int function derivative(curve, index){ // // return dx' curve(x) // if (index === 0) { return 0; } const point1 = curve[index - 1]; const point2 = curve[index]; return (point2[1] - point1[1]) / (point2[0] - point1[0]); } // rasterizedCurve -> rasterizedCurve function squiggleAroundCurve(x, y, curve, index) { const len = lenCurve(curve, index); const h = Math.sin(len); const b = Math.sin(Math.atan2(1, derivative(curve, index))) * h; const a = Math.cos(Math.atan2(1, derivative(curve, index))) * h; x -= a; y += b; return [x, y]; } function pow2(x) { return Math.pow(x,2); } function dist(point1, point2) { return Math.sqrt(pow2(point2[0] - point1[0]) + pow2(point2[1] - point1[1])) } // rasterizedCurve, int -> int function lenCurve(rasterizedCurve, index) { const curve = rasterizedCurve.slice(0, index); return curve.reduce((sum, point, index) => { let len = 0; if (index > 0) { len = dist(point, curve[index - 1]); } return sum + len; }, 0); } const Curve = expandRange(rangeFrom1ToN(90),50).map(x => [x, (Math.log(x) * 15)]); const SquiggledCurve = Curve.map((point, index) => squiggleAroundCurve(point[0], point[1], Curve, index)) function zoom(curve, w) { return curve.map(point => [point[0] * w, point[1] * w]); } function getNode(n, v) { n = document.createElementNS("http://www.w3.org/2000/svg", n); for (var p in v) n.setAttributeNS(null, p.replace(/[AZ]/g, function(m, p, o, s) { return "-" + m.toLowerCase(); }), v[p]); return n } var svg = getNode("svg"); setTimeout(function() { document.body.appendChild(svg); const r = getNode('polyline', { points:formatForSvg(zoom(SquiggledCurve, 10)), fill:'none', stroke:'black'}); const c = getNode('polyline', { points:formatForSvg(zoom(Curve, 10)), fill:'none', stroke:'black'}); svg.appendChild(r); svg.appendChild(c); }, 1000); 
  svg { width: 1100px; height: 900px; } 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM