用 JavaScript 实现 jQuery 显示与隐藏动画

在 jQuery 中有 show(), hide(), slideDown(), slideUp() 等方法,能够以动画的形式显示或隐藏 DOM 元素。这一类的动画在页面交互中比较常用,这也是 jQuery 方便开发的地方之一,但如果只想使用这些动画效果而加载整个 jQuery 库那就有点浪费资源了,于是最近在折腾 JavaScript 的 Kayo 便尝试用原生的 JavaScript 实现这一类 jQuery 动画。

 

在介绍原生 JavaScript 代码前 Kayo 先想提出一个问题,也就是 jQuery 相对 JavaScript 的优势是什么?若开发者是先了解 jQuery 再学习原生 JavaScript ,那么以下这些优势将会让你很容易发现:

  • 强大而且灵活的选择器, 正如很多 JavaScript 类库一样,jQuery 使用 $() 函数来作为选择器,支持 CSS1 到 CSS3 的选择器,比起 JavaScript 中的 document.getElementById(), document.getElementsByTagName() ,这样的选择器无疑方便很多。
  • 带有常用的事件、方法,包括一些动画方法。
  • 完善的封装, jQuery 中的对象处理,传递,操作都有着完善的封装和处理机制,并且做好了针对不同浏览器的兼容,这些若使用原生 JavaScript 重写将是一项非常繁重的工作。

当然, jQuery 的劣势也非常明显——即使你的整个网站只是使用 jQuery 的几个动画方法,也要加载一个近 100K 的库文件。下面大家不妨带着上面几点思考去阅读下文,这样就会感觉到 jQuery 在使用上的方便。

 

回到一开始讨论的问题——使用原生的 JavaScript 实现 jQuery 动画,下面会放出代码,但由于代码已经封装好了,具体的过程通过代码比较难理解,这里再简要说明一下:

 

1.建立一个动画对象—— Transform ,增加一些方法:

  • 动画形式隐藏 DOM 元素
  • 动画形式显示 DOM 元素

2.建立一个 DOM 元素对象—— $ ,增加一些方法:

  • 获取某 id 的 DOM 元素
  • 获取该元素的某项 CSS
  • 动画形式隐藏元素(实例化 Transform 对象并调用其中的方法)
  • 动画形式显示元素(实例化 Transform 对象并调用其中的方法)

3.根据实际需要操作的 DOM 元素 id 实例化 $ 对象并调用相应的方法

 

可以看出,上面的方法有点累赘,为什么不直接把动画方法写到 $ 对象里呢?

 

之所以会这样写,完全是为了方便维护和提高对象的独立性,例如给动画对象添加更多的动画方法或是给 Dom 元素对象添加更多的操作,封装为两个不同的对象将会更加方便维护。如果只是希望使用简单的动画效果,完全可以不同封装,毕竟封装后效率会降低很多,当然这种效率的差别你很难在 PC 上直接看出。

 

下面给出具体方法和代码:

 

实现动画的原理很简单——按特定的单位时间连续播放一组静态画面,在 JavaScript 中,我们可以利用 window.setTimeout 或 window.setInterval 来实现此效果,这里 Kayo 采用 window.setTimeout 递归调用一个减少元素宽度和高度的方法来实现。

 

需要注意的是:

  • 在实现动画的方法中应该直接传递需要操作的 DOM 对象而不是传递 DOM 对象的 id ,因此我们最好写一个方法来获取 DOM 。
  • 动画应该是能同时作用于网页上的多个元素,因此避免使用全局变量。
  • 封装函数时应该使用 .prototype 添加方法,节省内存。
  • 这些 JavaScript 写好后应该放在 footer 而不是 header ,因为原生 JavaScript 中没有自带的函数检查页面中每一个 DOM 是否都加载完毕,因此应该把 JavaScript 写在最后,当然开发者也可以另外写出一个判断页面的 DOM 是否加载完毕的函数。

 

这里以隐藏 DOM 元素为例,创建一个动画对象,包含以动画形式隐藏 DOM 元素的方法。在动画对象的接口中, obj 是需要操作的 DOM 对象, speed 是动画速度,数值越小动画越快, mode 是动画方式,有 “zoom” , “slide” 两种方式,分别模拟 jQuery 中 hide() 和 slideUp() 的效果, fn 为回调函数。另外 hide() 中的 width 和 height 两个参数为 DOM 元素的宽度和高度,会在 DOM 元素对象中调用 hide() 时赋值。

	// 动画对象模板
	var Transform = function(obj, speed, mode, fn){
		this.obj = obj;
		this.speed = speed; // 动画速度
		this.mode = mode;	// 动画形式
		this.heightChange = 10;
		this.fn = fn;
		if( speed === 'fast')
			this.speed = 10;
		else if( speed === 'normal' )
			this.speed = 20;
		else if( speed === 'slow' )
			this.speed = 30;
	}
	Transform.prototype = {
		// 处理隐藏
		hide: function( width, height ){
			var self = this;
			if ( !this.speed ) {
				this.obj.style.display = 'none';
				return;
			}
			width = width - this.widthChange > 0 ? width - this.widthChange : 0;
			height = height - this.heightChange > 0 ? width - this.heightChange : 0;	
			// 
			if( width !== 0 || height !== 0 ) {
				// 判断采用何种动画形式
				if( this.mode === 'zoom' ) this.obj.style.width = width + 'px';
				this.obj.style.height = height + 'px';
				// 递归调用自身
				setTimeout(function(){self.hide(width,height)},this.speed);
			} else {
				// 完成后清除所有 style 属性
				this.obj.style.cssText = "";
				// 设置 display: none
				this.obj.style.display = 'none';
				if(this.fn) this.fn.call(this.obj);
			}
		}

 

下面是创建一个 DOM 对象模板

	// DOM 元素对象模板
	var $ = function(id){
		this.id = id;
		this.obj = document.getElementById(this.id);
	}
	$.prototype = {
		// 获取元素的某项 CSS
		getClass: function(name){
			// IE8及以下版本
			if(this.obj.currentStyle) {
				return this.obj.currentStyle[name];
			}
			// Firefox, Chrome, IE9+
			else {
				var style = document.defaultView.getComputedStyle(this.obj, null);
				return style[name];
			}
		},
		// 以元素高宽生成动画对象
		cal: function(speed, mode, fn){
			// 创建动画对象
			this.theTransform = new Transform(this.obj, speed, mode, fn);
			this.theTransform.theWidth = this.getClass('width').replace('px','');
			this.theTransform.theHeight = this.getClass('height').replace('px','');
			// 按长宽比例计算单位时间内元素宽带减少值
			this.theTransform.widthChange = this.theTransform.heightChange*(this.theTransform.theWidth / this.theTransform.theHeight);
		},
		// 隐藏元素
		hide: function(speed, mode, fn){
			this.cal(speed, mode, fn);
			this.theTransform.hide(this.theTransform.theWidth, this.theTransform.theHeight);
		},
		// 显示元素
		show: function(speed, mode, fn){
			this.cal(speed, mode, fn);
			this.theTransform.show(0, 0);
		}
	}

 

这里使用一个 getCss 方法获取 DOM 元素的宽和高,注意在不同的浏览器中获取方式是不同的,所以要作出判断(感受到 jQuery 的优势了吧),另外每次减少的宽度 widthChange 是按元素宽高比例算出的,不是直接指定的,这样是为了模拟 jQuery 中的动画效果。

下面给出完整的 Demo

 

需要使用这类动画而不需要加载 jQuery 库的童鞋可以下载完整 Demo ,连接 Demo 中的 js 文件到你的网页中就可以使用动画,比如网面中有一 id="box" 的 div ,那么可以使用以下这段 JavaScript 和 HTML 来隐藏和显示该 div 。

 

以需要操作的 DOM 元素的 id 实例化一个 DOM 元素对象

// 创建 DOM 元素对象
var theDom = new $('box');

 

相应的 HTML ,其中 'fast' 为动画速度,还可以选择 'normal', 'slow' ,代表三种不同速度,也可以直接填数值(建议 50 以内,数值越小动画速度越快),不填(即参数为'')则取消动画,直接隐藏或显示元素,'zoom' 为动画形式,还可以选择 'slide' ,还有一个选填的回调函数,如theDom.hide('fast', 'zoom', function(){alert('succeed');})。这些用法可以参考 Demo 。

<a href="#" onclick="theDom.hide('fast', 'zoom')">点击隐藏</a>
<a href="#" onclick="theDom.show('fast', 'zoom')">点击显示</a>

本文由 Kayo Lee 发表,本文链接:https://kayosite.com/achieve-jquery-animate-by-javascript.html

评论列表

  • 评论者头像
    回复

    代码那里溢出了哦。。。沙发

    • 评论者头像
      回复

      @Demon 估计刚才高亮插件还没有加载好,貌似最近这个空间不大稳定!

  • 评论者头像
    回复

    汗。难道刚刚还没加载完成就停止 刷新了。

    • 评论者头像
      回复

      @Demon 空间不稳定时那个高亮代码插件加载会慢点,不过还没有加载好就停止应该不会吧 :arrow:

  • 评论者头像
    回复

    还是挺复杂的啊 :-x

  • 评论者头像
    回复

    闲着过来打酱油。

  • 评论者头像
    回复

    一个微型的动画框架出来了 :wink:

    • 评论者头像
      回复

      @万戈 哈哈,小小的动画操作,框架的话倒是想做一个用于表单UI美化的~

回复

你正在以游客身份访问网站,请输入你的昵称和 E-mail