跳到主要内容

Spline 样条曲线

样条曲线是开放的平滑曲线,与 Blob 的唯一区别是不闭合。

用法​

要使用Konva创建一个曲线(样条), 我们可以实例化一个Konva.Line()对象和tension属性.

有关属性和方法的完整列表,请参阅Konva.Line文档

<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/konva@10/konva.min.js"></script>
<meta charset="utf-8">
<title>Konva Line Spline Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #F0F0F0;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;

var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});

var layer = new Konva.Layer();

var redLine = new Konva.Line({
points: [5, 70, 140, 23, 250, 60, 300, 20],
stroke: 'red',
strokeWidth: 15,
lineCap: 'round',
lineJoin: 'round',
tension : 1
});

// dashed line
var greenLine = new Konva.Line({
points: [5, 70, 140, 23, 250, 60, 300, 20],
stroke: 'green',
strokeWidth: 2,
lineJoin: 'round',
/*
* line segments with a length of 33px
* with a gap of 10px
*/
dash: [33, 10],
lineCap: 'round',
tension : 0.5
});

// complex dashed and dotted line
var blueLine = new Konva.Line({
points: [5, 70, 140, 23, 250, 60, 300, 20],
stroke: 'blue',
strokeWidth: 10,
lineCap: 'round',
lineJoin: 'round',
/*
* line segments with a length of 29px with a gap
* of 20px followed by a line segment of 0.001px (a dot)
* followed by a gap of 20px
*/
dash: [29, 20, 0.001, 20],
tension : 0.7
});

/*
* since each line has the same point array, we can
* adjust the position of each one using the
* move() method
*/
redLine.move({
x : 20,
y : 5
});
greenLine.move({
x : 20,
y : 55
});
blueLine.move({
x : 20,
y : 105
});

layer.add(redLine);
layer.add(greenLine);
layer.add(blueLine);

// add the layer to the stage
stage.add(layer);
</script>

</body>
</html>

常见问题​

为什么画出来还是折线?​

tension 的默认值是 0,此时样条退化为直线段连接。必须显式设置一个正值, 常用范围是 0.3 到 0.6。

这是最常见的「功能没生效」原因——代码里用了 Konva.Line 想画曲线, 却没设 tension。

getClientRect 返回的范围比 points 的包围盒还大?​

是正常的。样条为了平滑会在顶点之间「冲出」一点,曲线的极值点可能落在 所有顶点构成的包围盒之外,tension 越大冲出越多。

做自动布局或碰撞检测时要用 getClientRect() 的实际值,不要拿 points 自己算—— 后者会小于真实占据的范围。

逐帧修改 points 做动画时曲线为什么会跳变?​

如果每帧传入的数组长度不同,Konva 要重新推导全部控制点,曲线形状会整体重算, 视觉上就是跳变。

做曲线动画时保持点数不变、只改坐标值,曲线就会平滑过渡。确实需要增删点时, 考虑先在目标位置插入一个与相邻点重合的点,再把它移动到位。

性能提示​

样条比同样点数的折线贵——每段都要计算控制点并绘制三次贝塞尔曲线,而折线只是 lineTo。点数很多的曲线(例如手绘轨迹)要留意这个成本。

高频更新的曲线上,关掉 perfectDrawEnabled 并避免使用 shadow。阴影会让每一帧 都多一遍模糊计算,是手绘、轨迹回放这类场景掉帧的常见原因。

如果曲线画完后不再变化,cache() 成位图;但正在被拖动编辑的曲线不要缓存, 那样每帧都要重建缓存,反而更慢。