在 HTML5 canvas 上动画绘制路径
Animate drawing of path on HTML5 canvas
我的问题是如何为两点之间的路径绘制动画。
考虑两点 A 和 B 之间的曲线或路径。我可以使用 Konvajs 中的画线功能在 canvas 上轻松绘制它。
然而,我真正想要的是为线条的显示设置动画,使其从 A 点开始逐渐绘制到 B 点。显示应该是动画的,这样我就可以应用令人愉悦的缓动。
作为一个比较示例,请参阅本网站上的简短视频 https://coggle.it/,其中视频显示了新框的创建以及将其连接到旧框的线条。
这是一个可能的答案(特别感谢 @markov00 re same technique in SVG). It works by manipulating the path dashOffset and dash attributes. There is an excellent explanation of the technique here in a post by Jake Archibald,其中还包括一个滑块的交互式实验,我发现它非常有用。
我已尝试使演示尽可能轻便,并仅展示技术 - 尽管我添加了一个滑块和一些 UI 以帮助理解该过程。 jquery 仅用于该技术不需要的 UI 部分。
两点:
- 这里的demo使用了3条直线段的路径。但我尝试了曲线和组合路径,并且该技术在这些情况下也适用 - 所以任何路径都应该有效。
- 我发现在路径上使用闭合路径命令 (z) 会导致路径长度函数在真实距离上变短。这显示为在任一端保持描边或间隙的路径量,其大小取决于第一个和最后一个之间的跳跃以关闭路径。
- 路径长度实际上总是小数,因此不要尝试将所有内容都设为整数,因为您最终会发现您的笔划略微过长或较短。
要将其用于动画和缓动等,从滑块更改事件中取出几行并将它们粘贴到帧回调中,根据您的情况处理数学。
// Set up the canvas / stage
var stage = new Konva.Stage({container: 'container1', width: 320, height: 180});
// Add a layer
var layer = new Konva.Layer({draggable: false});
stage.add(layer);
// show where the start of the path is.
var circle = new Konva.Circle({
x: 66,
y: 15,
radius: 5,
stroke: 'red'
})
layer.add(circle);
// draw a path.
var path = new Konva.Path({
x: 0,
y: 0,
data: 'M66 15 L75 100 L225 120 L100 17 L66 15',
stroke: 'green'
});
// get the path length and set this as the dash and dashOffset.
var pathLen = path.getLength();
path.dashOffset(pathLen);
path.dash([pathLen]);
layer.add(path)
stage.draw();
// Some UI bits
$('#dist').attr('max', parseInt(pathLen)); // set slider max to length of path
$('#pathLen').html('Path : ' + pathLen); // display path length
// jquery event listener on slider change
$('#dist').on('input', function(){
// compute the new dash lenth as original path length - current slider value.
// Means that dashLen initially = path len and moves toward zero as slider val increases.
var dashLen = pathLen - $(this).val();;
path.dashOffset(dashLen); // set new value
layer.draw(); // refresh the layer to see effect
// update the UI elements
$('#dashLen').html('Dash: ' + dashLen);
$('#pathPC').html(parseInt(100-(100 * (dashLen/pathLen)), 10) + '%');
})
.info
{
padding-left: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.js"></script>
<div class="slidecontainer">
<input class='slider' id='dist' type="range" min="0" max="100" value="0" class="slider" id="myRange"/>
<span class='info' id='pathPC'></span>
<span class='info' id='pathLen'></span>
<span class='info' id='dashLen'></span>
</div>
<div id='container1' style="display: inline-block; width: 300px, height: 200px; background-color: silver; overflow: hidden; position: relative;"></div>
<div id='img'></div>
我的动画解决方案:
var width = window.innerWidth;
var height = window.innerHeight;
// Set up the canvas / stage
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
// Add a layer
var layer = new Konva.Layer({
draggable: false
});
stage.add(layer);
// show where the start of the path is.
var circle = new Konva.Circle({
x: 66,
y: 15,
radius: 5,
stroke: 'red'
})
layer.add(circle);
// draw a path.
var path = new Konva.Path({
x: 0,
y: 0,
data: 'M66 15 L75 100 L225 120 L100 17 L66 15',
stroke: 'green'
});
// get the path length and set this as the dash and dashOffset.
var pathLen = path.getLength();
path.dashOffset(pathLen);
path.dash([pathLen]);
// make some animation with stop
var anim = new Konva.Animation(function (frame) {
var dashLen = pathLen - frame.time / 5;
path.dashOffset(dashLen);
if (dashLen < 0) {
anim.stop();
}
}, layer);
anim.start();
layer.add(path)
stage.draw();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.js"></script>
<div id='container' style="display: inline-block; width: 300px, height: 200px; background-color: silver; overflow: hidden; position: relative;"></div>
<div id='img'></div>
这是 @Roxane 的动画版本的替代方案,但使用了补间。
var width = window.innerWidth;
var height = window.innerHeight;
// Set up the canvas / stage
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
// Add a layer
var layer = new Konva.Layer({
draggable: false
});
stage.add(layer);
// show where the start of the path is.
var circle = new Konva.Circle({
x: 66,
y: 15,
radius: 5,
stroke: 'red'
})
layer.add(circle);
// draw a path.
var path = new Konva.Path({
x: 0,
y: 0,
data: 'M66 15 L75 100 L225 120 L100 17 L66 15',
stroke: 'green'
});
// get the path length and set this as the dash and dashOffset.
var pathLen = path.getLength();
path.dashOffset(pathLen);
path.dash([pathLen]);
layer.add(path); // have to add to layer for tweening.
// create the tween
var tween = new Konva.Tween({
node: path,
dashOffset: 0,
easing: Konva.Easings['BounceEaseOut'],
duration: 1.5
});
tween.play(); // execute the tween
stage.draw();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.js"></script>
<div id='container' style="display: inline-block; width: 300px, height: 200px; background-color: silver; overflow: hidden; position: relative;"></div>
<div id='img'></div>
我的问题是如何为两点之间的路径绘制动画。
考虑两点 A 和 B 之间的曲线或路径。我可以使用 Konvajs 中的画线功能在 canvas 上轻松绘制它。
然而,我真正想要的是为线条的显示设置动画,使其从 A 点开始逐渐绘制到 B 点。显示应该是动画的,这样我就可以应用令人愉悦的缓动。
作为一个比较示例,请参阅本网站上的简短视频 https://coggle.it/,其中视频显示了新框的创建以及将其连接到旧框的线条。
这是一个可能的答案(特别感谢 @markov00 re same technique in SVG). It works by manipulating the path dashOffset and dash attributes. There is an excellent explanation of the technique here in a post by Jake Archibald,其中还包括一个滑块的交互式实验,我发现它非常有用。
我已尝试使演示尽可能轻便,并仅展示技术 - 尽管我添加了一个滑块和一些 UI 以帮助理解该过程。 jquery 仅用于该技术不需要的 UI 部分。
两点:
- 这里的demo使用了3条直线段的路径。但我尝试了曲线和组合路径,并且该技术在这些情况下也适用 - 所以任何路径都应该有效。
- 我发现在路径上使用闭合路径命令 (z) 会导致路径长度函数在真实距离上变短。这显示为在任一端保持描边或间隙的路径量,其大小取决于第一个和最后一个之间的跳跃以关闭路径。
- 路径长度实际上总是小数,因此不要尝试将所有内容都设为整数,因为您最终会发现您的笔划略微过长或较短。
要将其用于动画和缓动等,从滑块更改事件中取出几行并将它们粘贴到帧回调中,根据您的情况处理数学。
// Set up the canvas / stage
var stage = new Konva.Stage({container: 'container1', width: 320, height: 180});
// Add a layer
var layer = new Konva.Layer({draggable: false});
stage.add(layer);
// show where the start of the path is.
var circle = new Konva.Circle({
x: 66,
y: 15,
radius: 5,
stroke: 'red'
})
layer.add(circle);
// draw a path.
var path = new Konva.Path({
x: 0,
y: 0,
data: 'M66 15 L75 100 L225 120 L100 17 L66 15',
stroke: 'green'
});
// get the path length and set this as the dash and dashOffset.
var pathLen = path.getLength();
path.dashOffset(pathLen);
path.dash([pathLen]);
layer.add(path)
stage.draw();
// Some UI bits
$('#dist').attr('max', parseInt(pathLen)); // set slider max to length of path
$('#pathLen').html('Path : ' + pathLen); // display path length
// jquery event listener on slider change
$('#dist').on('input', function(){
// compute the new dash lenth as original path length - current slider value.
// Means that dashLen initially = path len and moves toward zero as slider val increases.
var dashLen = pathLen - $(this).val();;
path.dashOffset(dashLen); // set new value
layer.draw(); // refresh the layer to see effect
// update the UI elements
$('#dashLen').html('Dash: ' + dashLen);
$('#pathPC').html(parseInt(100-(100 * (dashLen/pathLen)), 10) + '%');
})
.info
{
padding-left: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.js"></script>
<div class="slidecontainer">
<input class='slider' id='dist' type="range" min="0" max="100" value="0" class="slider" id="myRange"/>
<span class='info' id='pathPC'></span>
<span class='info' id='pathLen'></span>
<span class='info' id='dashLen'></span>
</div>
<div id='container1' style="display: inline-block; width: 300px, height: 200px; background-color: silver; overflow: hidden; position: relative;"></div>
<div id='img'></div>
我的动画解决方案:
var width = window.innerWidth;
var height = window.innerHeight;
// Set up the canvas / stage
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
// Add a layer
var layer = new Konva.Layer({
draggable: false
});
stage.add(layer);
// show where the start of the path is.
var circle = new Konva.Circle({
x: 66,
y: 15,
radius: 5,
stroke: 'red'
})
layer.add(circle);
// draw a path.
var path = new Konva.Path({
x: 0,
y: 0,
data: 'M66 15 L75 100 L225 120 L100 17 L66 15',
stroke: 'green'
});
// get the path length and set this as the dash and dashOffset.
var pathLen = path.getLength();
path.dashOffset(pathLen);
path.dash([pathLen]);
// make some animation with stop
var anim = new Konva.Animation(function (frame) {
var dashLen = pathLen - frame.time / 5;
path.dashOffset(dashLen);
if (dashLen < 0) {
anim.stop();
}
}, layer);
anim.start();
layer.add(path)
stage.draw();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.js"></script>
<div id='container' style="display: inline-block; width: 300px, height: 200px; background-color: silver; overflow: hidden; position: relative;"></div>
<div id='img'></div>
这是 @Roxane 的动画版本的替代方案,但使用了补间。
var width = window.innerWidth;
var height = window.innerHeight;
// Set up the canvas / stage
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
// Add a layer
var layer = new Konva.Layer({
draggable: false
});
stage.add(layer);
// show where the start of the path is.
var circle = new Konva.Circle({
x: 66,
y: 15,
radius: 5,
stroke: 'red'
})
layer.add(circle);
// draw a path.
var path = new Konva.Path({
x: 0,
y: 0,
data: 'M66 15 L75 100 L225 120 L100 17 L66 15',
stroke: 'green'
});
// get the path length and set this as the dash and dashOffset.
var pathLen = path.getLength();
path.dashOffset(pathLen);
path.dash([pathLen]);
layer.add(path); // have to add to layer for tweening.
// create the tween
var tween = new Konva.Tween({
node: path,
dashOffset: 0,
easing: Konva.Easings['BounceEaseOut'],
duration: 1.5
});
tween.play(); // execute the tween
stage.draw();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.js"></script>
<div id='container' style="display: inline-block; width: 300px, height: 200px; background-color: silver; overflow: hidden; position: relative;"></div>
<div id='img'></div>