我想在 SVG 中制作阿基米德螺线。
我创建了一个具有四个二次贝塞尔点的螺旋,但我不确定应该将每个点的控制点放在哪里以获得完美的阿基米德螺旋:
<path class="spiral"
d="M100 50
C 100 116 12.5 99.5 12.5 50
C 12.5 0.5 75 9 75 50
C 75 83 37.5 74 37.5 50
C 37.5 38 50 42 50 50"
stroke="black" stroke-width="1" fill="none">
Run Code Online (Sandbox Code Playgroud)
我想详细阐述 Zev Eisenberg 在math.stackexchange上提出的问题。由此,Zev以 C 函数的形式实现了一个解决方案。它使用二次贝塞尔曲线而不是三次贝塞尔曲线,但优点是您可以自由设置路径部分的角度,从而根据需要最大限度地减少误差。
\n\n这是一个 Javascript 端口。根据您的喜好设置 getPath 的参数(角度以度为单位)。thetaStep是每个路径部分覆盖的角度。我认为 30\xc2\xb0 给出了相当不错的结果。
function lineIntersection (m1, b1, m2, b2) {\r\n if (m1 === m2) {\r\n throw new Error("parallel slopes");\r\n }\r\n const x = (b2 - b1) / (m1 - m2);\r\n return {x: x, y: m1 * x + b1};\r\n}\r\n\r\nfunction pStr (point) {\r\n return `${point.x},${point.y} `;\r\n}\r\n\r\nfunction getPath (center, startRadius, spacePerLoop, startTheta, endTheta, thetaStep) {\r\n // Rename spiral parameters for the formula r = a + b\xce\xb8\r\n const a = startRadius; // start distance from center\r\n const b = spacePerLoop / Math.PI / 2; // space between each loop\r\n\r\n // convert angles to radians\r\n let oldTheta = newTheta = startTheta * Math.PI / 180;\r\n endTheta = endTheta * Math.PI / 180;\r\n thetaStep = thetaStep * Math.PI / 180;\r\n\r\n // radii\r\n let oldR,\r\n newR = a + b * newTheta;\r\n\r\n // start and end points\r\n const oldPoint = {x: 0, y: 0};\r\n const newPoint = {\r\n x: center.x + newR * Math.cos(newTheta), \r\n y: center.y + newR * Math.sin(newTheta)\r\n };\r\n\r\n // slopes of tangents\r\n let oldslope,\r\n newSlope = (b * Math.sin(oldTheta) + (a + b * newTheta) * Math.cos(oldTheta)) /\r\n (b * Math.cos(oldTheta) - (a + b * newTheta) * Math.sin(oldTheta));\r\n\r\n let path = "M " + pStr(newPoint);\r\n \r\n while (oldTheta < endTheta - thetaStep) {\r\n oldTheta = newTheta;\r\n newTheta += thetaStep;\r\n\r\n oldR = newR;\r\n newR = a + b * newTheta;\r\n\r\n oldPoint.x = newPoint.x;\r\n oldPoint.y = newPoint.y;\r\n newPoint.x = center.x + newR * Math.cos(newTheta);\r\n newPoint.y = center.y + newR * Math.sin(newTheta);\r\n\r\n // Slope calculation with the formula:\r\n // (b * sin\xce\x98 + (a + b\xce\x98) * cos\xce\x98) / (b * cos\xce\x98 - (a + b\xce\x98) * sin\xce\x98)\r\n const aPlusBTheta = a + b * newTheta;\r\n\r\n oldSlope = newSlope;\r\n newSlope = (b * Math.sin(newTheta) + aPlusBTheta * Math.cos(newTheta)) /\r\n (b * Math.cos(newTheta) - aPlusBTheta * Math.sin(newTheta));\r\n\r\n const oldIntercept = -(oldSlope * oldR * Math.cos(oldTheta) - oldR * Math.sin(oldTheta));\r\n const newIntercept = -(newSlope * newR* Math.cos(newTheta) - newR * Math.sin(newTheta));\r\n\r\n const controlPoint = lineIntersection(oldSlope, oldIntercept, newSlope, newIntercept);\r\n\r\n // Offset the control point by the center offset.\r\n controlPoint.x += center.x;\r\n controlPoint.y += center.y;\r\n\r\n path += "Q " + pStr(controlPoint) + pStr(newPoint);\r\n }\r\n \r\n return path;\r\n}\r\n\r\nconst path = getPath({x:400,y:400}, 0, 50, 0, 6*360, 30);\r\n\r\nconst spiral = document.querySelector(\'#spiral\');\r\nspiral.setAttribute("d", path);Run Code Online (Sandbox Code Playgroud)\r\n<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400" viewBox="0 0 800 800">\r\n <path id="spiral" d="" fill="none" stroke="black" stroke-width="3"/>\r\n</svg>Run Code Online (Sandbox Code Playgroud)\r\n