在 Javascript 中使用顺序 Canvas 动画的最佳方式
Best way to have sequential Canvas Animations In Javascript
所以我尝试使用 PaperJs(PaperJs 也是路径和元素的制作方式)为 canvas 元素(在本例中为 PaperJs Path/Text)制作动画。它的工作方式是我有一组用户创建的框架(当我说框架时,我指的是从这里开始的这个用户创建的框架),其中有 0-10 条由用户绘制的路径。从这里开始,我在一个位置创建了一个数字,并将对该 canvas 元素的引用存储在一个名为 text 的数组中,并将相应的路径存储在一个名为 paths 的数组中。对于每一帧,我附加的代码是 运行 及其路径和文本列表。我的代码可以工作,但非常简陋,并且对我要添加的功能有限制。问题是我希望能够调用函数 animate_frame(OBJ_WITH_PATHS[0]) 并让它为帧设置动画,然后一旦它完成第一个帧的动画调用 animate_frame(OBJ_WITH_PATHS[1]) 因此带有路径的整个 OBJ 列表按顺序进行动画处理。目前发生这种情况是因为行
if ((event.time > frame_num*2) && (event.time < ((frame_num*2) + 2))) {
.
需要这一行的原因是程序使每个帧的所有文本和路径都带有一个 onFrame 事件,并且没有严格限制对象可见的时间,而是同时对所有帧进行动画处理逐帧(我的帧有路径)。这种行为是我将每帧的总时间严格限制为 2 秒的原因。我还应该注意我将填充颜色更改为黑色然后 t运行sparent 的原因,因此元素仅在动画期间可见,而不是在 2 秒动画周期之前或之后。我目前使用的相关代码如下所示
var offset = [];
for(let i = 0; i < paths.length; i ++) {
offset[i] = 0;
}
var time_sec = 2;
for(let i = 0; i < paths.length; i ++) {
text[i].onFrame = function (event) {
var speed = paths[i].length/time_sec;
if ((event.time > frame_num*2) && (event.time < ((frame_num*2) + 2))) {
text[i].fillColor = "black";
if (offset[i] < paths[i].length){
text[i].position = paths[i].getPointAt(offset[i]);
offset[i] += event.delta*speed; // speed - 150px/second
} else {
offset[i] = paths[i].length;
}
} else {
text[i].fillColor = new paper.Color(0.5,0);
}
paper.view.draw();
}
}
我的代码限制了我想要添加的功能,例如
- 暂停
- 跳到下一Frame/back帧
- 播放帧(然后暂停直到他们再次点击播放然后播放单帧)
- 变速(即一帧2秒,第二帧4秒)
我还想谈谈我已经尝试在调用 animate_frame 之间实现等待 2 秒,但是在此等待期间,PaperJS 的 onFrame(event) 不会发生,所以它不起作用),我还尝试了标志 var 和 while 循环,但也没有用。
所以我想知道是否有更好的方法来做到这一点。我喜欢创建一个动画对象,其中该对象包含路径和 canvas 元素 moved/animated 并且可以通过 animation.play(); 调用动画;然后它还可以等待上一个动画完成,这样我就可以拥有一系列动画帧。对于整个项目,此时我已与 PaperJs 结婚,但我可以将路径位置数据放入常规 JS 数组并将其传递到新包中,如果有的话,我还可以在 canvas 之上为元素设置动画一个适合我正在尝试做的事情的包。
谢谢你assistance/Ideas!
对于复杂的动画场景,我建议你使用合适的动画库,这将使整个事情变得更容易。
例如,GSAP 可能对所有 play/pause... 处理有很大帮助。
这是一个简单的 fiddle 演示如何使用它来控制基于 Paper.js 的动画。
您应该能够根据您的特定用例对其进行调整。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Debug Paper.js</title>
<script src="https://unpkg.com/acorn"></script>
<script src="https://unpkg.com/paper"></script>
<script src="https://unpkg.com/gsap"></script>
<style>
html,
body {
margin : 0;
overflow : hidden;
height : 100%;
}
canvas[resize] {
width : 100%;
height : 100%;
}
#buttons {
position : fixed;
top : 50px;
left : 50px;
display : flex;
gap : 15px;
}
</style>
</head>
<body>
<div id="buttons">
<button id="play">play</button>
<button id="pause">pause</button>
<button id="reverse">reverse</button>
</div>
<canvas id="canvas" resize></canvas>
<script>
paper.setup('canvas');
const initialPosition = paper.view.center;
const circle = new paper.Path.Circle({
center: initialPosition,
radius: 50,
fillColor: 'orange'
});
function update(t) {
circle.position = initialPosition.add([t * 100, 0]);
}
const obj = { value: 0 };
var timeLine = gsap.timeline({
onUpdate: (event) => {
update(obj.value);
}
});
timeLine.to(obj, { value: 1, duration: 1 });
timeLine.pause();
document.querySelector('#play').addEventListener('click', () => timeLine.play());
document.querySelector('#pause').addEventListener('click', () => timeLine.pause());
document.querySelector('#reverse').addEventListener('click', () => timeLine.reverse());
</script>
</body>
</html>
所以我尝试使用 PaperJs(PaperJs 也是路径和元素的制作方式)为 canvas 元素(在本例中为 PaperJs Path/Text)制作动画。它的工作方式是我有一组用户创建的框架(当我说框架时,我指的是从这里开始的这个用户创建的框架),其中有 0-10 条由用户绘制的路径。从这里开始,我在一个位置创建了一个数字,并将对该 canvas 元素的引用存储在一个名为 text 的数组中,并将相应的路径存储在一个名为 paths 的数组中。对于每一帧,我附加的代码是 运行 及其路径和文本列表。我的代码可以工作,但非常简陋,并且对我要添加的功能有限制。问题是我希望能够调用函数 animate_frame(OBJ_WITH_PATHS[0]) 并让它为帧设置动画,然后一旦它完成第一个帧的动画调用 animate_frame(OBJ_WITH_PATHS[1]) 因此带有路径的整个 OBJ 列表按顺序进行动画处理。目前发生这种情况是因为行
if ((event.time > frame_num*2) && (event.time < ((frame_num*2) + 2))) {
.
需要这一行的原因是程序使每个帧的所有文本和路径都带有一个 onFrame 事件,并且没有严格限制对象可见的时间,而是同时对所有帧进行动画处理逐帧(我的帧有路径)。这种行为是我将每帧的总时间严格限制为 2 秒的原因。我还应该注意我将填充颜色更改为黑色然后 t运行sparent 的原因,因此元素仅在动画期间可见,而不是在 2 秒动画周期之前或之后。我目前使用的相关代码如下所示
var offset = [];
for(let i = 0; i < paths.length; i ++) {
offset[i] = 0;
}
var time_sec = 2;
for(let i = 0; i < paths.length; i ++) {
text[i].onFrame = function (event) {
var speed = paths[i].length/time_sec;
if ((event.time > frame_num*2) && (event.time < ((frame_num*2) + 2))) {
text[i].fillColor = "black";
if (offset[i] < paths[i].length){
text[i].position = paths[i].getPointAt(offset[i]);
offset[i] += event.delta*speed; // speed - 150px/second
} else {
offset[i] = paths[i].length;
}
} else {
text[i].fillColor = new paper.Color(0.5,0);
}
paper.view.draw();
}
}
我的代码限制了我想要添加的功能,例如
- 暂停
- 跳到下一Frame/back帧
- 播放帧(然后暂停直到他们再次点击播放然后播放单帧)
- 变速(即一帧2秒,第二帧4秒)
我还想谈谈我已经尝试在调用 animate_frame 之间实现等待 2 秒,但是在此等待期间,PaperJS 的 onFrame(event) 不会发生,所以它不起作用),我还尝试了标志 var 和 while 循环,但也没有用。
所以我想知道是否有更好的方法来做到这一点。我喜欢创建一个动画对象,其中该对象包含路径和 canvas 元素 moved/animated 并且可以通过 animation.play(); 调用动画;然后它还可以等待上一个动画完成,这样我就可以拥有一系列动画帧。对于整个项目,此时我已与 PaperJs 结婚,但我可以将路径位置数据放入常规 JS 数组并将其传递到新包中,如果有的话,我还可以在 canvas 之上为元素设置动画一个适合我正在尝试做的事情的包。 谢谢你assistance/Ideas!
对于复杂的动画场景,我建议你使用合适的动画库,这将使整个事情变得更容易。
例如,GSAP 可能对所有 play/pause... 处理有很大帮助。
这是一个简单的 fiddle 演示如何使用它来控制基于 Paper.js 的动画。
您应该能够根据您的特定用例对其进行调整。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Debug Paper.js</title>
<script src="https://unpkg.com/acorn"></script>
<script src="https://unpkg.com/paper"></script>
<script src="https://unpkg.com/gsap"></script>
<style>
html,
body {
margin : 0;
overflow : hidden;
height : 100%;
}
canvas[resize] {
width : 100%;
height : 100%;
}
#buttons {
position : fixed;
top : 50px;
left : 50px;
display : flex;
gap : 15px;
}
</style>
</head>
<body>
<div id="buttons">
<button id="play">play</button>
<button id="pause">pause</button>
<button id="reverse">reverse</button>
</div>
<canvas id="canvas" resize></canvas>
<script>
paper.setup('canvas');
const initialPosition = paper.view.center;
const circle = new paper.Path.Circle({
center: initialPosition,
radius: 50,
fillColor: 'orange'
});
function update(t) {
circle.position = initialPosition.add([t * 100, 0]);
}
const obj = { value: 0 };
var timeLine = gsap.timeline({
onUpdate: (event) => {
update(obj.value);
}
});
timeLine.to(obj, { value: 1, duration: 1 });
timeLine.pause();
document.querySelector('#play').addEventListener('click', () => timeLine.play());
document.querySelector('#pause').addEventListener('click', () => timeLine.pause());
document.querySelector('#reverse').addEventListener('click', () => timeLine.reverse());
</script>
</body>
</html>