仅需给出若干关键点,即可绘制出一条「经过」这些关键点的光滑曲线
A Pen by Michael Zhou on CodePen.
| <div class="svg-container"> | |
| <svg width="100%" height="100%"> | |
| <path class="spline" d="" style="fill: none; stroke: red; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1px;"></path> | |
| <g class="dots"></g> | |
| </svg> | |
| </div> |
| function getFirstControlPoints(rhs) { | |
| var n = rhs.length, | |
| x = [], // Solution vector. | |
| tmp = [], // Temp workspace. | |
| b = 2.0, | |
| i; | |
| x[0] = rhs[0] / b; | |
| for (i = 1; i < n; i++) { // Decomposition and forward substitution. | |
| tmp[i] = 1 / b; | |
| b = (i < n - 1 ? 4.0 : 2.0) - tmp[i]; | |
| x[i] = (rhs[i] - x[i - 1]) / b; | |
| } | |
| for (i = 1; i < n; i++) { | |
| x[n - i - 1] -= tmp[n - i] * x[n - i]; // Backsubstitution. | |
| } | |
| return x; | |
| } | |
| // 获取三次方贝塞尔曲线控制点坐标 | |
| function getCubicBezierCurvePoints(knots, firstControlPoints, secondControlPoints) { | |
| var rhs = [], | |
| n = knots.length - 1, | |
| x, y, i; | |
| if (n < 1) { | |
| return; | |
| } | |
| // Set right hand side X values | |
| for (i = 0; i < n - 1; ++i) { | |
| rhs[i] = 4 * knots[i].x + 2 * knots[i + 1].x; | |
| } | |
| rhs[0] = knots[0].x + 2 * knots[1].x; | |
| rhs[n - 1] = 3 * knots[n - 1].x; | |
| // Get first control points X-values | |
| x = getFirstControlPoints(rhs); | |
| // Set right hand side Y values | |
| for (i = 1; i < n - 1; ++i) { | |
| rhs[i] = 4 * knots[i].y + 2 * knots[i + 1].y; | |
| } | |
| rhs[0] = knots[0].y + 2 * knots[1].y; | |
| rhs[n - 1] = 3 * knots[n - 1].y; | |
| // Get first control points Y-values | |
| y = getFirstControlPoints(rhs); | |
| for (i = 0; i < n; ++i) { | |
| // First control point | |
| firstControlPoints[i] = { | |
| x: x[i], | |
| y: y[i] | |
| }; | |
| // Second control point | |
| if (i < n - 1) { | |
| secondControlPoints[i] = { | |
| x: 2 * knots[i + 1].x - x[i + 1], | |
| y: 2 * knots[i + 1].y - y[i + 1] | |
| }; | |
| } else { | |
| secondControlPoints[i] = { | |
| x: (knots[n].x + x[n - 1]) / 2, | |
| y: (knots[n].y + y[n - 1]) / 2 | |
| }; | |
| } | |
| } | |
| } | |
| function getCubicBezierCurvePath(knots) { | |
| var firstControlPoints = [], | |
| secondControlPoints = [], | |
| path = []; | |
| getCubicBezierCurvePoints(knots, firstControlPoints, secondControlPoints); | |
| for (var i = 0, len = knots.length; i < len; i++) { | |
| if (i === 0) { | |
| path.push(['M', knots[i].x, knots[i].y].join(' ')); | |
| } else { | |
| var firstControlPoint = firstControlPoints[i - 1], | |
| secondControlPoint = secondControlPoints[i - 1]; | |
| path.push([ | |
| 'C', firstControlPoint.x, firstControlPoint.y, // 第一个控制点 | |
| secondControlPoint.x, secondControlPoint.y, // 第二个控制点 | |
| knots[i].x, knots[i].y // 实点 | |
| ].join(' ')); | |
| } | |
| } | |
| return path.join(' '); | |
| } | |
| // 绘制节点 | |
| var points = [{ | |
| x: 0, | |
| y: 20 | |
| }, { | |
| x: 50, | |
| y: 30 | |
| }, { | |
| x: 100, | |
| y: 100 | |
| }, { | |
| x: 150, | |
| y: 50 | |
| }, { | |
| x: 200, | |
| y: 70 | |
| }, { | |
| x: 250, | |
| y: 90 | |
| }, { | |
| x: 300, | |
| y: 120 | |
| }]; | |
| var dots = document.querySelector('.svg-container .dots'); | |
| for (var i = 0, len = points.length; i < len; i++) { | |
| var xmlns = "http://www.w3.org/2000/svg", | |
| circle = document.createElementNS(xmlns, 'circle'); | |
| var point = points[i]; | |
| circle.setAttribute('fill', 'green'); | |
| circle.setAttribute('r', '4'); | |
| circle.setAttribute('cx', point.x); | |
| circle.setAttribute('cy', point.y); | |
| dots.appendChild(circle); | |
| } | |
| var path = getCubicBezierCurvePath(points); | |
| document.querySelector('.svg-container .spline').setAttribute('d', path); |
| .svg-container { | |
| width: 300px; | |
| height: 200px; | |
| } |
仅需给出若干关键点,即可绘制出一条「经过」这些关键点的光滑曲线
A Pen by Michael Zhou on CodePen.