仅需给出若干关键点,即可绘制出一条「经过」这些关键点的光滑曲线
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.