通过队列保证JQuery动画animate的顺序
问题来源
因为项目需要需要在前端进行实时模拟设备运行状态,暂时用的是2D,所以就要用到了JQuery的animate,但是只是animate还不能满足需求,因为可能存在这样的一个场景,当这个动画还没执行完,下一个就来了,那么就需要保证前后动画的有序执行,如果不能保证运动的有序执行,那么节点会出现紊乱,特别是css类似{height:'+=10px'}
这样的情况,那么就会出现很严重的bug。
解决办法
在动画的基础上添加一个队列来保证动画的执行有序性,队列保存还未运动的动画,这样就不会出现上一个动画还没完成,下一个动画就实现了,好,思路清晰了,那就甩开膀子干吧。
原始版本
//队列Queue.js
var ArrayQueue = function() {
this.arr = [];
//入队操作
this.push = function(element) {
this.arr.push(element);
return true;
}
//出队操作
this.pop = function() {
return this.arr.shift();
}
//获取队首
this.getFront = function() {
return this.arr[0];
}
//获取队尾
this.getRear = function() {
return this.arr[this.arr.length - 1]
}
//清空队列
this.clear = function() {
this.arr = [];
}
//获取队长
this.size = function() {
return this.arr.length;
}
this.isEmpty = function() {
return this.arr.length == 0;
}
};
export default new ArrayQueue();
//index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://libs.baidu.com/jquery/1.10.2/jquery.min.js"></script>
</head>
<body>
<div class="text" style="width: 6.25rem;height: 12.5rem;border: bisque solid 0.0625rem;overflow-x: hidden;overflow-y: scroll;">
</div>
<div class="content" style="width: 37.5rem;height: 3.125rem;border: #000000 solid 0.0625rem;">
<div class="tr" style="width: 1.25rem; height: 1.25rem; margin-top: 1.25rem;background-color: aqua;"></div>
</div>
<button class="send">发送</button>
<script type="module">//type="module" 必须要有,因为import 是导入模块,如果其他script标签也用到了,那么同样需要加
import ArrayQueue from './js/Queue.js';
$(function(){
var isrunning = false;
function animate(){
let msg = ArrayQueue.pop();
isrunning = true;
$(".tr").animate({marginLeft:msg+'px'},3000,null,function(){
$('.text').append('<li>'+$('.tr').css('marginLeft')+'</li>');
if(ArrayQueue.isEmpty()){
isrunning = false;
}else{
animate();
}
});
};
function onMessage(msg){
ArrayQueue.push(msg);
console.log(msg);
if(!isrunning){
animate();
}
}
//模拟发送
$(".send").click(function(){
let location = (578*Math.random()).toFixed(0);
onMessage(location);
});
});
</script>
</body>
</html>
效果
优化
作为一个后端java开发工程师,这样写真的是太难受了,既然队列封装了,那么动画何不封装一下呢?继续干~~
//Animate.js Queue.js没变化,就没重复写出来了。
/**
*
* 基于jq的包含队列的逐帧动画,为啥要加入队列呢?如果在同时产生了多个动画的情况下,
* 如果不能保证运动的有序执行,那么节点会出现紊乱,特别是css类似{height:'+=10px'}
* 这样的情况,那么就会出现很严重的bug。
*
* */
import ArrayQueue from './Queue.js'
var Animate = function() {
//运动的节点
this.$el = null;
//当前是否在运动
this.isRunning = false;
//默认速度
this.defaultSpeed = 3000;
//默认回调函数
this.defaultCallback = function(callback) {
try{
callback();
}catch(e){
throw Error(e);
}finally{
if (ArrayQueue.isEmpty()) {
this.isRunning = false;
} else {
this.animate();
}
}
};
//初始化
this.init = function(el) {
if (!this.isElement(el)) {
throw Error("init: The type of variable el is not an Element")
} else {
this.$el = el;
}
};
//设置参数
this.setoptions = function(options) {
if(!options.css){
throw Error("setoptions: options.css cannot be empty");
}else{
let ins = {
css: options.css || {},
speed: options.speed || this.defaultSpeed,
easing: options.easing || null,
startBefore : options.startBefore(options) || function(options){},
callback: options.callback,
}
return ins;
}
};
//开始运行,加入队列,保证顺序。
this.start = function(options){
ArrayQueue.push(this.setoptions(options));
if(!this.isRunning){
this.Animate();
}
};
//动画
this.Animate = function() {
if (!this.isElement(this.$el)) {
throw Error("Animate: The type of variable el is not an Element")
} else {
try{
if(!ArrayQueue.isEmpty()){
let options = ArrayQueue.pop();
options.startBefore();
$(this.$el).animate(options.css,options.speed,options.easing,()=>{
this.defaultCallback(options.callback);
});
}
}catch(e){
throw Error(e);
}
}
};
//判断对象类型是否为节点
this.isElement = function(obj) {
return (typeof HTMLElement === 'object') ?
(obj instanceof HTMLElement) :
!!(obj && typeof obj === 'object' && (obj.nodeType === 1 || obj.nodeType === 9) && typeof obj.nodeName ===
'string');
}
}
export default new Animate();
//index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://libs.baidu.com/jquery/1.10.2/jquery.min.js"></script>
</head>
<body>
<div class="text" style="width: 6.25rem;height: 12.5rem;border: bisque solid 0.0625rem;overflow-x: hidden;overflow-y: scroll;">
</div>
<div class="content" style="width: 37.5rem;height: 3.125rem;border: #000000 solid 0.0625rem;">
<div id="tr" style="width: 1.25rem; height: 1.25rem; margin-top: 1.25rem;background-color: aqua;"></div>
</div>
<button class="send">发送</button>
<script type="module">//type="module" 必须要有,因为import 是导入模块,如果其他script标签也用到了,那么同样需要加
import Animate from './js/Animate.js';
$(function(){
Animate.init(document.getElementById('tr'));
//模拟发送
$(".send").click(function(){
let location = (578*Math.random()).toFixed(0);
let option = {
css:{marginLeft:location+'px'},
speed:3000,
startBefore:function(options){
console.log(options);
},
callback:function(){
$('.text').append('<li>'+$('#tr').css('marginLeft')+'</li>');
}
};
Animate.start(option);
});
});
</script>
</body>
</html>
效果
总结
总体来说还行,无论是原生html,还是vue,都可以使用(Vue需要npm install jquery
,并且引入jquery,才可以使用哟),其实不用jquery也可以,利用css3的动画和js自己手写一个逐帧动画,既然有现成的,而且这么成熟的jquery,为啥不用呢,我们要提倡节约,代码丑陋是丑陋了点,别计较,如果有bug,麻烦偷偷告诉我,好了,散会!