参考文章:用原生JavaScript写出类似jQuery中slideUp和slideDown效果_johnworks的博客-CSDN博客   作者:johnworks

目录

一、前言

二、第一次尝试

三、第一次失败的思考

四、让定时器乖乖就范

五、更灵活的管理方案

六、最终实现

七、个人修改后的最终代码


一、前言

在我自学JavaScript的时候,一直想实现类似安卓手机状态栏那种下拉上滑效果。在网上搜索一番后,我知道了jQuery的slideUp()和slideDown()方法。这两个方法让我很着迷,我迫不及待知道他是如何实现的。可当我尝试在网上寻找答案时,得到答案几乎都是:你去看jQuery源码不就知道了吗?于是我就去看了jQuery源码,但是面对几万行代码,让我望而却步。所以至今我也不知道slideUp()和slideDown()这两个方法在jQuery中到底是怎样实现的。

二、第一次尝试

在进一步学习JavaScript后,我了解了setTimeout和setInterval,并知道可以利用它们来逐渐的改变元素的属性,以此达到动画效果。于是我开始着手借助setInterval来实现我自己的下拉上滑效果。让我们以下面的HTML代码为基础,开始聊聊我的第一次实现:

<!doctype html>
<html>
<head><meta charset="utf-8"/><title>Slider</title>
</head>
<body><button id="btn">Button</button><div id="panel" style="width:600px;height:400px;background:red;"></div><script src="Slider.js"></script>
</body>
</html>

上面代码布局很简单,会显示一个id为btn的按钮;和一个宽为600px,高为400px,背景为红色,id为panel的div。最后我引入了一个文件名为Slider.js的JavaScript文件,这个文件目前还未创建,我将在下面给出。

为了方便描述,我把id为btn的按钮简记为btn,同理也把红色div简记为panel。现在我想实现一个效果:当我点击btn时,如果panel当前可见,就执行一个上滑动画来将它慢慢收起,从而让它变成不可见;如果当前不可见,那就执行一个下拉动画将它慢慢展开,从而让它变成可见。整个流程如下:

整个流程清楚了,现在就开始写JS代码了,以下是Slider.js中的内容:

window.onload = function() {// 获取btn和panelvar btn   = document.getElementById("btn"),panel = document.getElementById("panel");// 为btn绑定onclick事件btn.onclick = function() {// 通过panel的offsetHeight来判断元素是否可见if (panel.offsetHeight === 0) {// 不可见,调用下拉函数:在300毫秒内下拉slideDown(panel, 300);} else {// 可见,调用上滑函数:在300毫秒内上滑slideUp(panel, 300);}};
};function slideDown(element, time) {// 等待实现
}function slideUp(element, time) {// 等待实现
}

以上代码注释地很清楚,这里不再赘述。我们要注意的是其中有两个等待实现的函数slideDown(element, time)和slideUp(element, time) 。这两个函数分别用于执行下滑上拉动画,他们都接受一个Element类型的element和一个Number类型的time为参数,其中element表示要执行动画的元素,time表示执行动画一个需要多少时间(毫秒)。

我们先来实现slideUp(element, time)。我的思路是这样的:用一个setInterval定时器,每隔10毫秒执行一个函数,每次执行这个函数时就把element的height属性(element.style.height)减去一部分。举个例子,如果我们传入的time参数为500的话,这个函数就会执行(500/10) = 50次。也就是说我们要分50次把element.style.height减为0,那么每次要减去的值就是 element.offsetHeight/50 了(其中element.offsetHeight为元素的高度)。来看看slidUp的实现:

function slideUp(element, time) {// 获取元素总高度var totalHeight = element.offsetHeight;// 定义一个变量保存元素当前高度var currentHeight = totalHeight;// 计算每次减去的值var decrement = totalHeight/ (time/10);// 开始循环定时器var timer = setInterval(function() {// 减去当前高度的一部分currentHeight = currentHeight - decrement;// 把当前高度赋值给height属性element.style.height = currentHeight + "px";// 如果当前高度小于等于0,就关闭定时器if (currentHeight <= 0) {// 关闭定时器clearInterval(timer);// 把元素display设置为noneelement.style.display = "none";// 把元素高度值还原element.style.height = totalHeight + "px";}}, 10);
}
// ...

现在当我们点击btn时,panel就会慢慢被收起。而当我们再次点击btn时,却没有任何反应,因为我们还没有实现slideDown。有了slideUp为基础,slideDown就不算什么难事了,直接上代码:

//...
function slideDown(element, time) {// 获取元素总高度element.style.display = "block";            // 1.显示元素,元素变为可见var totalHeight = element.offsetHeight;     // 2.保存总高度element.style.height = "0px";               // 3.再将元素高度设置为0,元素又变为不可见// 定义一个变量保存元素当前高度var currentHeight = 0;                      // 当前元素高度为0// 计算每次增加的值var increment = totalHeight / (time/10);// 开始循环定时器var timer = setInterval(function () {// 增加一部分高度currentHeight = currentHeight + increment;// 把当前高度赋值给height属性element.style.height = currentHeight + "px";// 如果当前高度大于或等于总高度则关闭定时器if (currentHeight >= totalHeight) {// 关闭定时器clearInterval(timer);// 把高度设置为总高度element.style.height = totalHeight + "px";}}, 10);
}
//...

OK,大功告成。现在来看看效果如何。首先点击btn,我们会看到panel慢慢被收起。等待收起动画完成后,再次点击btn,panel又会被慢慢展开。看起来很不错,达到了我们预期的效果。可是一切真的这么简单吗?请注意,我方才刻意强调了等待收起动画完成后再点击btn,但是如果我们快速连续点击btn又会出现什么情况呢?答案是我们的动画会崩溃!你可以亲自试一试,看看是不是如我所说。PS:效果如下图所示。

综上所述,我的第一次尝试是失败的,或者说是不完美的。因为当用户快速点击按钮时我们的动画会崩溃,这显然不是用户想要的。

三、第一次失败的思考

以上动画为什么会失败呢?那是因为我想当然的认为:JavaScript中的定时器会一个接一个的按着顺序执行。因为JavaScript是单线程,所以也这个认为看起来似乎并不是错的。但是,事实证明这是错的,比如从下面的列子中就可以看出:

// test.js
window.onload = function() {timer1();timer2();timer3();
};// 依次弹出 1 2 3
function timer1() {var num = 0;var timer = setInterval(function() {num ++;alert(num);if (num == 3) {clearInterval(timer);}}, 10);
}// 依次弹出 4 5 6
function timer2() {var num = 3;var timer = setInterval(function() {num ++;alert(num);if (num == 6) {clearInterval(timer);}}, 10);
}// 依次弹出 7 8 9
function timer3() {var num = 6;var timer = setInterval(function() {num ++;alert(num);if (num == 9) {clearInterval(timer);}}, 10);
}

从上面代码可以看出,我们使用三个定时器,希望浏览器依次按顺序弹出1-9这9个数字(每隔10毫秒弹出一个)。把Slider.html 中的Slider.js改为test.js,执行以下看看结果。结果我们会发现,弹出的顺序根本毫无规律可言。这也再次说明了,JavaScript中定时器并不会按代码顺序依次执行。至于为什么,我在这里不做深入研究,在此,我们只需记住这个结论即可。

其实,根本不用做上面的实验,我们也能轻易得出这个结论。因为很多网页上不只有一个动画,如果所有的动画都按顺序一个接一个的执行的话,那岂不是说在该网页上同时至多只能有一个动画在执行,与此同时其余动画都是静止的(因为还未轮到它们执行),这显然和我们看到的不一致。

四、让定时器乖乖就范

既然定时器如此顽皮地不按顺序执行,所以我们必需得想个法子让它乖乖就范。

要让定时器按顺序执行,那就必需使用回调。也就是说,在一个函数执行函数完成后去调用另一个函数。具体到刚刚那个test.js,要让timer1(),timer2(),timer3()三个定时器依次执行。我们可以在timer1()执行完成后主动去调用timer2(),timer2()完成后又主动去调用timer3()。这样一来,我们只需执行timer1(),三个定时器就都会被依次执行,就像下面一样:

// test.js
window.onload = function() {timer1();
};// 依次弹出 1 2 3
function timer1() {var num = 0;var timer = setInterval(function() {num ++;alert(num);if (num == 3) {clearInterval(timer);// 主动调用timer2();timer2();}}, 10);
}// 依次弹出 4 5 6
function timer2() {var num = 3;var timer = setInterval(function() {num ++;alert(num);if (num == 6) {clearInterval(timer);// 主动调用 timer3();timer3();}}, 10);
}// 依次弹出 7 8 9
function timer3() {var num = 6;var timer = setInterval(function() {num ++;alert(num);if (num == 9) {clearInterval(timer);}}, 10);
}

运行上面的代码,我们发现,浏览器会像我们期待的那样依次按顺序弹出9个数字。

五、更灵活的管理方案

虽然上面代码会让定时器依次执行,但这种方式并不灵活。想象一下,如果我们要添加一个新的定时器timer4(),我们就必须在timer3()中去调用它。如果要添加1000个呢?那工作量会相当可观。

现在我们用一个更为灵活的方案来管理定时器的执行。我们可以把所有要按一定顺序执行的定时器都保存在一个数组中,然后把这个数组当成一个队列使用,最后按顺序一个接一个的执行队列里面的定时器。

提示:JavaScript中的数组本身就可以当作队列使用(参见数组的shift()方法和push()方法),所以我们不要实现自己的队列数据结构。

在此,我们创建一个TimerManager对象来管理动画队列。其代码如下:

// test.js
// ...
// 声明TimerManager
var TimerManager = {};TimerManager.timers = [];       // 用于保存定时器的数组(队列)
TimerManager.isFiring = false;  // 用于记录当前是否有定时器在执行// 一个用于添加定时器的方法
TimerManager.add = function(timer) {// 把定时器存入队列this.timers.push(timer);// 调用fire()执行队列中第一个定时器this.fire();
};// 一个用于执行队列中第一个定时器的方法
TimerManager.fire = function() {if ( !this.isFiring ) { // 如果当前没有定时器在执行var firstTimer = this.timers.shift();  // 取出队列中的第一项if (firstTimer) { // 如果第一个定时器存在, 就执行// 设置isFiring为true表明当前有定时器在执行this.isFiring = true; firstTimer();}}
};// 一个用于执行下一个定时器的方法
TimerManager.next = function() {// 先把isFiring设置为false,表明当前没有定时器在执行this.isFiring = false;// 调用fire()执行第一个定时器this.fire();
};// ...

TimerManager一共有两个属性,timers和isFiring;还有三个方法分别是add(timer), fire()和next()。其中我们常用的是add(timer)和next()。

fire() 是一个用来执行队列中第一个定时器的内部方法,执行的时候,它会先判断当前是否有定时器在执行,如果没有的话,它便会把第一个定时器从队列中取出来并立即执行;如果当前有定时器正在执行,它就什么都不做。

next() 是一个用来执行队列中下一个动画的方法,它应该在定时器结束的时候被调用,以执行队列中下一个定时器。这也表明了,我们在写定时器时,必须在定时器结束时主动调用这个方法。

add(timer) 用于向队列中添加定时器。timer即要添加的函数名。

前面说过,必须在定时器结束时调用next()方法。所以我们把之前的定时器(timer1,timer2,timer3)都修改一下,改成下面这样:

// test.js
// ...
// 依次弹出 1 2 3
function timer1() {var num = 0;var timer = setInterval(function() {num ++;alert(num);if (num == 3) {clearInterval(timer);// 调用next();TimerManager.next();}}, 10);
}// 依次弹出 4 5 6
function timer2() {var num = 3;var timer = setInterval(function() {num ++;alert(num);if (num == 6) {clearInterval(timer);// 调用next();TimerManager.next();}}, 10);
}// 依次弹出 7 8 9
function timer3() {var num = 6;var timer = setInterval(function() {num ++;alert(num);if (num == 9) {clearInterval(timer);// 调用next();TimerManager.next();}}, 10);
}
// ... 

完成以上修改后,我就可以调用add(timer)方法把所有定时器添加进去。因为在add(timer)内部会主动调用fire()来执行队列中的第一个定时器,所以我们不用手动调用fire()。我们要做的只是把定时器添加进去,其他什么也不用做,定时器就会乖乖地排着队去会执行。以下便是test.js修改后全部代码:

// test.js// 声明TimerManager
var TimerManager = {};TimerManager.timers = [];       // 用于保存定时器的数组(队列)
TimerManager.isFiring = false;  // 用于记录当前是否有定时器在执行// 一个用于添加定时器的方法
TimerManager.add = function(timer) {// 把定时器存入队列this.timers.push(timer);// 调用fire()执行队列中第一个定时器this.fire();
};// 一个用于执行队列中第一个定时器的方法
TimerManager.fire = function() {if ( !this.isFiring ) { // 如果当前没有定时器在执行var firstTimer = this.timers.shift();  // 取出队列中的第一项if (firstTimer) { // 如果第一个定时器存在, 就执行// 设置isFiring为true表明当前有定时器在执行this.isFiring = true; firstTimer();}}
};// 一个用于执行下一个定时器的方法
TimerManager.next = function() {// 先把isFiring设置为false,表明当前没有定时器在执行this.isFiring = false;// 调用fire()执行第一个定时器this.fire();
};window.onload = function() {// 调用add(timer)添加定时器TimerManager.add(timer1);TimerManager.add(timer2);TimerManager.add(timer3);
};// 依次弹出 1 2 3
function timer1() {var num = 0;var timer = setInterval(function() {num ++;alert(num);if (num == 3) {clearInterval(timer);// 调用next();TimerManager.next();}}, 10);
}// 依次弹出 4 5 6
function timer2() {var num = 3;var timer = setInterval(function() {num ++;alert(num);if (num == 6) {clearInterval(timer);// 调用next();TimerManager.next();}}, 10);
}// 依次弹出 7 8 9
function timer3() {var num = 6;var timer = setInterval(function() {num ++;alert(num);if (num == 9) {clearInterval(timer);// 调用next();TimerManager.next();}}, 10);
}

运行上述代码后我们会得到一样的运行结果,不同的是我们采用了更为灵活的管理方式。

六、最终实现

上面说了这么多,好像偏题了。但是,实现定时器的有序执行对实现我们的下拉上滑动画来说的确十分重要。现在,我们就利用上面的成果,来完成我们的下拉上滑。为了方便描述,我们新建一个JS文件Slider2.js。我们会在这个文件中实现一个Slider对象,它包含一个slideUp(element, time)和一个slideDown(element, time)方法。以下是这个文件的结构:

window.Slider = (function() {var Slider = {};// 等待实现return Slider;
})();

这里创建了一个匿名函数,创建并返回一个对象(Slider),我们所有的代码都将在这个闭包中完成,最后只提供两个接口 —— slideUp(element, time)和slideDown(element, time)。

在给出最终实现代码前,先来说说我的思路:

I. 首先,由于一个网页中往往有多个绑定动画的元素,我们要求每个元素的动画单独连续执行,各个元素的动画的执行相互独立。所以我们不能用一个TimerManger来管理所有元素的动画队列,因此我们需要做的是为每个动画元素分配一个唯一的TimerManger。于是我们先要定义一个TimerManager类。

II. 我们要修改之前的动画函数:在动画结束的时候,获取动画元素的TimerManager,并调用它的next()方法

III. 用Slider对象把TimerManger与动画函数整合起来,并提供外部访问接口以下是Slider2.js中所有代码:

window.Slider = (function() {// 定义Slider对象var Slider = {};// I.定义一个TimerManager类// 1)构造函数function TimerManager() {this.timers = [];       // 保存定时器this.args = [];         // 保存定时器的参数this.isFiring = false;}// 2)静态方法:为element添加一个TimerManager实例TimerManager.makeInstance = function(element) {// 如果element.__TimerManager__上没有TimerManager,就为其添加一个if (!element.__TimerManager__ || element.__TimerManager__.constructor != TimerManager) {element.__TimerManager__ = new TimerManager();}};// 3)扩展TimerManager原型,分别实现add,fire,next方法TimerManager.prototype.add = function(timer, args) {this.timers.push(timer);this.args.push(args);this.fire();};TimerManager.prototype.fire = function() {if ( !this.isFiring ) {var timer = this.timers.shift(),        // 取出定时器args  = this.args.shift();          // 取出定时器参数if (timer && args) {this.isFiring = true;// 传入参数,执行定时器函数timer(args[0], args[1]);}}};TimerManager.prototype.next = function() {this.isFiring = false;this.fire();};// II. 修改动画函数并在定时器结束后调用element.__TimerManager__.next()// 1)下滑函数function fnSlideDown(element, time) {if (element.offsetHeight == 0) {  //如果当前高度为0,则执行下拉动画// 获取元素总高度element.style.display = "block";            // 1.显示元素,元素变为可见var totalHeight = element.offsetHeight;     // 2.保存总高度element.style.height = "0px";               // 3.再将元素高度设置为0,元素又变为不可见// 定义一个变量保存元素当前高度var currentHeight = 0;                      // 当前元素高度为0// 计算每次增加的值var increment = totalHeight / (time/10);// 开始循环定时器var timer = setInterval(function () {// 增加一部分高度currentHeight = currentHeight + increment;// 把当前高度赋值给height属性element.style.height = currentHeight + "px";// 如果当前高度大于或等于总高度则关闭定时器if (currentHeight >= totalHeight) {// 关闭定时器clearInterval(timer);// 把高度设置为总高度element.style.height = totalHeight + "px";if (element.__TimerManager__ && element.__TimerManager__.constructor == TimerManager) {element.__TimerManager__.next();}}}, 10);} else {  // 如果当前高度不为0,则直接执行队列里的下一个函数if (element.__TimerManager__ && element.__TimerManager__.constructor == TimerManager) {element.__TimerManager__.next();}}}// 2)上拉函数function fnSlideUp(element, time) {if (element.offsetHeight > 0) {  // 如果当前高度不为0,则执行上滑动画// 获取元素总高度var totalHeight = element.offsetHeight;// 定义一个变量保存元素当前高度var currentHeight = totalHeight;// 计算每次减去的值var decrement = totalHeight / (time/10);// 开始循环定时器var timer = setInterval(function() {// 减去当前高度的一部分currentHeight = currentHeight - decrement;// 把当前高度赋值给height属性element.style.height = currentHeight + "px";// 如果当前高度小于等于0,就关闭定时器if (currentHeight <= 0) {// 关闭定时器clearInterval(timer);// 把元素display设置为noneelement.style.display = "none";// 把元素高度值还原element.style.height = totalHeight + "px";if (element.__TimerManager__ && element.__TimerManager__.constructor == TimerManager) {element.__TimerManager__.next();}}}, 10);} else {  // 如果当前高度为0, 则直接执行队列里的下一个函数if (element.__TimerManager__ && element.__TimerManager__.constructor == TimerManager) {element.__TimerManager__.next();}}}// III.定义外部访问接口// 1)下拉接口Slider.slideDown = function(element, time) {TimerManager.makeInstance(element);element.__TimerManager__.add(fnSlideDown, arguments);return this;};// 2)上滑接口Slider.slideUp = function(element, time) {TimerManager.makeInstance(element);element.__TimerManager__.add(fnSlideUp, arguments);return this;};// 返回Slider对象return Slider;
})();

以上代码注释相当清楚(之前描述过的实现在此不再注释),这里不再赘述。

Slider对象完成了,现在新建一个test2.js来调用Slider的方法。以下为test2.js的代码:

window.onload = function() {// 获取btn和panelvar btn   = document.getElementById("btn"),panel = document.getElementById("panel");// 为btn绑定onclick事件btn.onclick = function() {// 通过panel的offsetHeight来判断元素是否可见if (panel.offsetHeight === 0) {// 不可见,调用Slider.slideDown函数:在300毫秒内下拉Slider.slideDown(panel, 300);} else {// 可见,调用Slider.slideUp函数:在300毫秒内上滑Slider.slideUp(panel, 300);}};
};

现在有了Slider2.jstest2.js,我们只要修改一下Slider.html引入这两个JS文件就行了。以下为Slider.html的代码:

<!doctype html>
<html>
<head><meta charset="utf-8"/><title>Slider</title>
</head>
<body><button id="btn">Button</button><div id="panel" style="width:600px;height:400px;background:red;"></div><script src="Slider2.js"></script><script src="test2.js"></script>
</body>
</html>

大功告成,运行以上代码,现在无论我们怎样疯狂的点击btn,我们的动画始终会正确执行。

至此我们已经完成了我们的目标,只不过我们的动画函数还不够优秀,因为它们都是一些简单的匀速运动。不过限于篇幅,这里就不再深究,以后有机会再来实现一些复杂的变速运动,当然,如果读者有兴趣的话也可以自行实现。

还有就是当我们在一个周期内(也就是分别执行一次上拉和下滑所用的全部时间内)多次点击btn时,动画最多执行两次,而不会执行一共点击的次数。当然,这也不能算是一个bug,因为这是笔者刻意为之。如果需要响应多次点击的话,也可以通过简单的修改来实现,不过限于篇幅,笔者也不再深究,留给有心的读者实现。

七、个人修改后的最终代码

window.Slider = (function() {// 定义Slider对象,外部访问接口对象var Slider = {slideUp: function(elemt, speed) { //【 上拉接口 】TimerManager.creatObject(slideUp, elemt, speed);return this;},slideDown: function(elemt, speed) { //【 下拉接口 】TimerManager.creatObject(slideDown, elemt, speed);return this;},slideTotle: function(elemt, speed) { //【 滑动切换接口 】TimerManager.creatObject(slideTotle, elemt, speed);return this;}};// II.构造对象记录计时器和动画状态function TimerManager(args) {this.func = args[0];this.elemt = args[1];this.speed = args[2] != 0 && args[2] != undefined && args[2] != null ? args[2] : 500;this.isStart = false;}// 静态方法:为element添加一个TimerManager实例TimerManager.creatObject = function(funcName, elemt, speed) {// 如果elemt对象没有TimerManager属性,或者该属性值不是TimerManager,则就为其添加或更换一个if(!elemt.TimerManager || elemt.TimerManager.constructor != TimerManager) {elemt.TimerManager = new TimerManager(arguments);}// 判断该elemt对象的计时器是否启动,如果没有启动,则启动,并执行动画,执行完毕之后修改计时器状态if(!elemt.TimerManager.isStart) {if(elemt.TimerManager.func.constructor != funcName) {elemt.TimerManager.func = funcName;}elemt.TimerManager.isStart = true;elemt.TimerManager.func(elemt, speed)}}// III.上滑和下滑函数// 1)上拉函数function slideUp(elemt, speed) { //向上滑动  Height ~ 0//如果当前高度不为0,则执行上拉动画if(elemt.offsetHeight != 0) {var speed = speed || 500; //执行总时间var timeSpeed = speed / 100; //速度elemt.style.cssText = "display:block;overflow:hidden;";// 顶部内边距var paddingTop = parseInt(getStyle(elemt, "paddingTop"));var styPaddingTop = paddingTop;// 底部内边距var paddingBottom = parseInt(getStyle(elemt, "paddingBottom"));var styPaddingBottom = paddingBottom;// 顶部外边距var marginTop = parseInt(getStyle(elemt, "marginTop"));var styMarginTop = marginTop;// 底部外边距var marginBottom = parseInt(getStyle(elemt, "marginBottom"));var styMarginBottom = marginBottom;// 高度var height = elemt.clientHeight - paddingTop - paddingBottom;var styHeight = height;var num = 0;var timer = setInterval(function() {// 更改高度if(styHeight != 0) {styHeight = styHeight - height / 100elemt.style.height = styHeight < 1 ? 0 : styHeight + "px";}// 更改Padding-topif(styPaddingTop != 0) {styPaddingTop = styPaddingTop - paddingTop / 100elemt.style.paddingTop = styPaddingTop < 1 ? 0 : styPaddingTop + "px";}// 更改Padding-bottomif(styPaddingBottom != 0) {styPaddingBottom = styPaddingBottom - paddingBottom / 100elemt.style.paddingBottom = styPaddingBottom < 1 ? 0 : styPaddingBottom + "px";}// 更改Padding-topif(styMarginTop != 0) {styMarginTop = styMarginTop - marginBottom / 100elemt.style.marginBottom = styMarginTop < 1 ? 0 : styMarginTop + "px";}// 更改Padding-bottomif(styMarginBottom != 0) {styMarginBottom = styMarginBottom - marginBottom / 100elemt.style.marginBottom = styMarginBottom < 1 ? 0 : styMarginBottom + "px";}num += timeSpeed;if(num >= speed) {elemt.style.cssText = "display:none";clearInterval(timer);elemt.TimerManager.isStart = false;}}, timeSpeed);} else {elemt.TimerManager.isStart = false;}}// 2)下拉函数function slideDown(elemt, speed) { //向下滑动 0 ~ Height//如果当前高度为0,则执行下拉动画if(elemt.offsetHeight == 0) {var speed = speed || 500; //执行总时间var timeSpeed = speed / 100; //速度elemt.style.cssText = "display:block";// 顶部内边距var paddingTop = parseInt(getStyle(elemt, "paddingTop"));var styPaddingTop = 0;// 底部内边距var paddingBottom = parseInt(getStyle(elemt, "paddingBottom"));var styPaddingBottom = 0;// 顶部外边距var marginTop = parseInt(getStyle(elemt, "marginTop"));var styMarginTop = 0;// 底部外边距var marginBottom = parseInt(getStyle(elemt, "marginBottom"));var styMarginBottom = 0;// 高度var height = elemt.clientHeight - paddingTop - paddingBottom;var styHeight = 0;elemt.style.height = "0px";elemt.style.paddingTop = "0px";elemt.style.paddingBottom = "0px";elemt.style.marginTop = "0px";elemt.style.marginBottom = "0px";var num = 0;var timer = setInterval(function() {// 更改高度if(height > 0) {styHeight = styHeight + height / 100;elemt.style.height = styHeight > height ? height : styHeight + "px";}// 更改Padding-topif(paddingTop > 0) {styPaddingTop = styPaddingTop + paddingTop / 100elemt.style.paddingTop = styPaddingTop > paddingTop ? paddingTop : styPaddingTop + "px";}// 更改Padding-bottomif(paddingBottom > 0) {styPaddingBottom = styPaddingBottom + paddingBottom / 100elemt.style.paddingBottom = styPaddingBottom > paddingBottom ? paddingBottom : styPaddingBottom + "px";}// 更改Margin-topif(marginTop > 0) {styMarginTop = styMarginTop + marginTop / 100elemt.style.marginTop = styMarginTop > marginTop ? marginTop : styMarginTop + "px";}// 更改Margin-bottomif(marginBottom > 0) {styMarginBottom = styMarginBottom + marginBottom / 100elemt.style.marginBottom = styMarginBottom > marginBottom ? marginBottom : styMarginBottom + "px";}num += timeSpeed;if(num >= speed) {elemt.style.cssText = "display:block";elemt.TimerManager.isStart = false;clearInterval(timer);}}, timeSpeed);} else {elemt.TimerManager.isStart = false;}}// 3)上拉/下拉切换函数function slideTotle(elemt, speed) { //【 滑动切换接口 】// 通过panel的offsetHeight来判断元素是否可见if(elemt.offsetHeight === 0) {// 执行下滑动画,调用slideDown()函数slideDown(elemt, speed);return this;} else {// 执行上滑动画,调用slideUp()函数slideUp(elemt, speed);return this;}}return Slider;
})();

最终实现效果如下图所示。

原生JavaScript实现jQuery中的slideUp和slideDown滑动效果相关推荐

  1. 微信小程序实现slideUp、slideDown滑动效果及点击空白隐藏功能示例

    本文实例讲述了微信小程序实现slideUp.slideDown滑动效果及点击空白隐藏功能.分享给大家供大家参考,具体如下: 怎样实现jq中的slideUp或者slideDown这种动画效果呢,我的思路 ...

  2. 微信小程序实现slideUp、slideDown滑动效果及点击空白隐藏功能示例 1

    本文实例讲述了微信小程序实现slideUp.slideDown滑动效果及点击空白隐藏功能.分享给大家供大家参考,具体如下: 怎样实现jq中的slideUp或者slideDown这种动画效果呢,我的思路 ...

  3. jQuery中的slideUp()、slideDown()、hide()、show()

    slideUp(speed,[callback]) 通过高度变化(向上减小)来动态地隐藏所有匹配的元素,在隐藏完成后可选地触发一个回调函数. 这个动画效果只调整元素的高度,可以使匹配的元素以" ...

  4. jQuery中的slideUp()、slideDown()、hide()、show() 的比较

    slideUp(speed,[callback]) 通过高度变化(向上减小)来动态地隐藏所有匹配的元素,在隐藏完成后可选地触发一个回调函数. 这个动画效果只调整元素的高度,可以使匹配的元素以" ...

  5. 原生javascript取代jquery的一些方法(jQuery-free)

    转自:http://www.qingdou.me/2687.html jQuery是最流行的JavaScript工具库.据统计,目前全世界57.3%的网站使用它.也就是说,10个网站里面,有6个使用j ...

  6. 流程代码中js报错,在javaScript或者jQuery中字符串比较没有equals()方法

    问题: 流程走不下去. 原因: 在javaScript或者jQuery中字符串比较没有equals()方法,要比较两个字符串是否相等可以直接用==或者is()进行判断. //判断是否为未签约有风险等级 ...

  7. 51 jQuery-使用slideDown()与slideUp()方法实现滑动效果

    1.效果图 2.HTML代码 <!DOCTYPE html> <html> <head><meta charset="utf-8"> ...

  8. JavaScript或jQuery中使用键盘控制对象运动

    <div id="monkey"><img src="img/monkey.png" ></div> 在JavaScript ...

  9. 演员选择框三级联动(原生javascript和jquery实现)

    使用Jquery做成选择演员选择框的三级联动效果.代码如下,测试可以下载附件. <html><head><meta http-equiv="Content-Ty ...

最新文章

  1. 阿里程序员35岁P7!4年了晋升无望!明年股票拿完,年包腰斩!世界那么大,要不要去看看?...
  2. python arm64_PyTorch-aarch64
  3. asp.net中的参数传递:Context.Handler 的用法
  4. kvm cobbler无人值守批量安装操作系统
  5. python 公众号爬虫_python_爬虫_微信公众号抓取
  6. A quick presentation of the Visual Studio 2010 editions per role
  7. C#设计模式之18-备忘录模式
  8. android Intent 全面点的介绍
  9. hystrix源码小贴士之中断
  10. POJ-2152 Fire (树形DP)
  11. 《软件设计师》考点分布
  12. 破茧成蝶2——以产品为中心的设计革命
  13. xp系统打开计算机硬盘分区,如何在xp系统对硬盘进行分区
  14. MATLAB的.fig文件打不开——有效解决
  15. java毕业设计演唱会门票订售及管理系统Mybatis+系统+数据库+调试部署
  16. 名片互赞软件(安卓版)
  17. Gradle 下载及代理设置
  18. python交换数组中的两个元素_Python 交换数组元素
  19. 坚果云显示连接服务器失败怎么办,坚果云提示同步过程中遇到错误,怎么解决?...
  20. DeepFM:深度学习算法助力华为应用市场APP推荐

热门文章

  1. DNS服务器上门维修电话,全国DNS服务器地址列举
  2. python --将图片处理为圆角
  3. 身份证号码的编码规则及校验
  4. 正则表达式的简单用法+利用正则表达式检验身份证号码格式
  5. python函数的用法字帖_GitHub - plateaukao/cns11643query: 用來查詢書法字帖的 python script,不用再層層的在官網上搜尋,只要輸入單詞即可...
  6. 华为云如何赋能无人车飞驰?从这群AI热血少年谈起
  7. 软件测试需要学什么?年薪30W+的测试工程师需要掌握哪些技能?
  8. mysql日期加天_MySql日期加天数,小时,分钟...得到新的时间
  9. 香港旅客最常经历航班延误与取消的航空公司:宿雾太平洋航空和曼谷航空分列第一 | 美通社头条...
  10. 1 分钟搞定!ChatGPT + XMind 打造最高效的思维导图