自己写一个jQuery垂直滚动栏插件(panel)-阿里云开发者社区

开发者社区> 技术mix呢> 正文

自己写一个jQuery垂直滚动栏插件(panel)

简介:
+关注继续查看

html中原生的滚动栏比較难看,所以有些站点,会自己实现滚动栏,导航站点hao123在一个側栏中,就自己定义了垂直滚动栏,效果比較好看,截图例如以下:



这个滚动栏,仅仅有在鼠标悬停在这个区域内时才显示,半透明效果,非常节省空间的说~~,说实话,这个效果我非常喜欢。


垂直滚动栏的原理,简单来说:

先起个名字,外层的叫wrapper,内层的叫content。wrapper须要有非static的定位,content须要绝对定位,这样就能够通过调节top值来模拟内容滚动。

详细说一下:

1.wrapper的overflow须要设置为hidden,并在wrapper上监听鼠标滚动事件,依据滚动的速度设置content的top值;

2.给wrapper加入滚动框和滚动栏。滚动栏与滚动框的比例和wrapper和content的高度值的比例相应

3.拖拽滚动栏时,content的top值与滚动栏拖拽的距离成比例,反过来滑动鼠标滚轮的时候。滚动栏也要跟着滚动

滚动栏的拖拽,复用我上一篇文章中的拖拽插件。以上就是大致的原理。

下面是基本的代码,__creator是主体函数。先忽略代码的框架,主要看__creator

/*
 * panel
 * 參数:obj{
 * 	iWheelStep:鼠标滑轮滚动时步进长度
 *	sBoxClassName:滚动框的样式
 * 	sBarClassName:滚动栏的样式
 * }
 */
$.zUI.addWidget("panel",{
	defaults : {
			iWheelStep:16,
			sBoxClassName:"zUIpanelScrollBox",
			sBarClassName:"zUIpanelScrollBar"
	},
	__creator:function(ele){
		var jqThis = $(ele);
		//假设是static定位。加上relative定位
		if(jqThis.css("position") === "static"){
			jqThis.css("position","relative");
		}
		jqThis.css("overflow","hidden");
		
		//必须有一个唯一的直接子元素,给直接子元素加上绝对定位
		var jqChild = jqThis.children(":first");
		if(jqChild.length){
			jqChild.css({top:0,position:"absolute"});
		}else{
			return;
		}
		
		var opts = jqThis.data($.zUI.panel.sOptsName);
		//创建滚动框
		var jqScrollBox = $("<div style='position:absolute;display:none;line-height:0;'></div>");
		jqScrollBox.addClass(opts.sBoxClassName);
		//创建滚动栏
		var jqScrollBar= $("<div style='position:absolute;display:none;line-height:0;'></div>");
		jqScrollBar.addClass(opts.sBarClassName);
		jqScrollBox.appendTo(jqThis);
		jqScrollBar.appendTo(jqThis);
		
		opts.iTop = parseInt(jqScrollBox.css("top"));
		opts.iWidth = jqScrollBar.width();
		opts.iRight = parseInt(jqScrollBox.css("right"));
		

		//加入拖拽触发自己定义函数
		jqScrollBar.on("draggable.move",function(){
			var opts = jqThis.data($.zUI.panel.sOptsName);
			fnScrollContent(jqScrollBox,jqScrollBar,jqThis,jqChild,opts.iTop,0);
		});
		
	  //事件对象
		var oEvent ={
			mouseenter:function(){
				fnFreshScroll();
				jqScrollBox.css("display","block");
				jqScrollBar.css("display","block");
			},
			mouseleave:function(){
				jqScrollBox.css("display","none");
				jqScrollBar.css("display","none");
			}
		};
		
		var sMouseWheel = "mousewheel";
		if(!("onmousewheel" in document)){
			sMouseWheel = "DOMMouseScroll";
		}
		oEvent[sMouseWheel] = function(ev){
			var opts = jqThis.data($.zUI.panel.sOptsName);
			var iWheelDelta = 1;
			ev.preventDefault();//阻止默认事件
			ev = ev.originalEvent;//获取原生的event
			if(ev.wheelDelta){
					iWheelDelta = ev.wheelDelta/120;
			}else{
					iWheelDelta = -ev.detail/3;
			}
			var iMinTop = jqThis.innerHeight() - jqChild.outerHeight();
			//外面比里面高。不须要响应滚动
			if(iMinTop>0){
				jqChild.css("top",0);
				return;
			}
			var iTop = parseInt(jqChild.css("top"));
			var iTop = iTop + opts.iWheelStep*iWheelDelta;
			iTop = iTop > 0 ? 0 : iTop;
			iTop = iTop < iMinTop ? iMinTop : iTop;
			jqChild.css("top",iTop);
			fnScrollContent(jqThis,jqChild,jqScrollBox,jqScrollBar,0,opts.iTop);
		}
		//记录加入事件
		jqThis.data($.zUI.panel.sEventName,oEvent);
		//尾随滚动函数
		function fnScrollContent(jqWrapper,jqContent,jqFollowWrapper,jqFlollowContent,iOffset1,iOffset2){
			var opts = jqThis.data($.zUI.panel.sOptsName);
			var rate = (parseInt(jqContent.css("top"))-iOffset1)/(jqContent.outerHeight()-jqWrapper.innerHeight())//卷起的比率
			var iTop = (jqFlollowContent.outerHeight()-jqFollowWrapper.innerHeight())*rate + iOffset2;
			jqFlollowContent.css("top",iTop);
		}
	
		//刷新滚动栏
		function fnFreshScroll(){

			var opts = jqThis.data($.zUI.panel.sOptsName);
			var iScrollBoxHeight = jqThis.innerHeight()-2*opts.iTop;
			var iRate = jqThis.innerHeight()/jqChild.outerHeight();
			var iScrollBarHeight = iScrollBarHeight = Math.round(iRate*iScrollBoxHeight);
			//假设比率大于等于1。不须要滚动栏,自然也不须要加入拖拽事件
			if(iRate >= 1){
				jqScrollBox.css("height",0);
				jqScrollBar.css("height",0);
				return;
			}
			jqScrollBox.css("height",iScrollBoxHeight);
			jqScrollBar.css("height",iScrollBarHeight);
			//计算拖拽边界。加入拖拽事件
			var oBoundary = {iMinTop:opts.iTop};
			oBoundary.iMaxTop = iScrollBoxHeight - Math.round(iRate*iScrollBoxHeight)+opts.iTop;
			oBoundary.iMinLeft = jqThis.innerWidth() - opts.iWidth - opts.iRight;
			oBoundary.iMaxLeft = oBoundary.iMinLeft;
			fnScrollContent(jqThis,jqChild,jqScrollBox,jqScrollBar,0,opts.iTop);
			jqScrollBar.draggable({oBoundary:oBoundary});
		}
	},
		__destroyer:function(ele){
			var jqEle = $(ele);
			if(jqEle.data($.zUI.panel.sFlagName)){
				var opts = jqEle.data($.zUI.panel.sOptsName);
				jqEle.children("."+opts.sBoxClassName).remove();
				jqEle.children("."+opts.sBarClassName).remove();
			}
	}
});

有几点须要说明:

1.jQuery没有对鼠标滑轮滚动事件做兼容,所以,这里要使用原生的事件:

ff中叫做DOMMouseScroll:通过事件的detail属性得知鼠标的滚动情况,向下滚动为正,向上为负。以3的倍数标书滚动距离

其它叫做mousewheel,通过事件的wheelDelta能够得知鼠标的滚动情况。向上滚动为正,向下为负,以120的倍数标书滚动距离

以上代码使用jQuery的on来挂载事件,须要还要注意获取原生的event对象-->ev.originalEvent;

吐个槽:这里怎么是ff和其它浏览器。而不是IE和其它浏览器了呢~~~~

2.在事件mouseenter中,每次都会调用fnFreshScroll。就是说,每次鼠标移过来的时候,都会动态计算一遍滚动栏的大小,事实上就是为了兼容内容会动态变化的情况(也不是全部情况都能够,当内容变得非常少时,小于外层的高度。还是会有问题)

3.以上代码把全部的事件都放在了oEvent对象中。却没有加入到相应元素中,这是由于我对插件先封装了一层(你可能已经猜到。没错,就是开头的$.zUI.addWidget函数)。加入事件会在那一层中做。


在写这个插件的过程中,我将一些规范直接转成代码,写了一个插件骨架:

$.zUI = $.zUI || {}
$.zUI.emptyFn = function(){};
$.zUI.asWidget = [];
/*
 * core代码,定义添加一个插件的骨架
 */
$.zUI.addWidget = function(sName,oSefDef){
	//设置规范中的常量sFlagName、sEventName、sOptsName
	$.zUI.asWidget.push(sName);
	var w = $.zUI[sName] = $.zUI[sName] || {};
	var sPrefix = "zUI" + sName
	w.sFlagName = sPrefix;
	w.sEventName = sPrefix + "Event";
	w.sOptsName = sPrefix + "Opts";
	w.__creator = $.zUI.emptyFn;
	w.__destroyer = $.zUI.emptyFn;
	$.extend(w,oSefDef);
	w.fn = function(ele,opts){
		var jqEle = $(ele);
		jqEle.data(w.sOptsName,$.extend({},w.defaults,opts));
		//假设该元素已经运行过了该插件,直接返回,仅相当于改动了配置參数
		if(jqEle.data(w.sFlagName)){
			return;
		}
		jqEle.data(w.sFlagName,true);
		w.__creator(ele);
		jqEle.on(jqEle.data(w.sEventName));
	};
	w.unfn = function(ele){
		w.__destroyer(ele);
		var jqEle = $(ele);//移除监听事件
		if(jqEle.data(w.sFlagName)){
			jqEle.off(jqEle.data(w.sEventName));
			jqEle.data(w.sFlagName,false);
		}
	}
	
}

在写draggable插件时。我定义了几个规范,比方主体函数必须叫做fn,销毁函数必须叫做unfn,这里能够看到,在addWidget组件中定义了fn函数,并写下了骨架。__creator和__destroyer则须要详细插件实现,同样的代码,同样的逻辑放在骨架中,比方。以某种规则给插件须要用到的常量起名字。插件參数初始化的逻辑。插件第二次运行相当于改动參数而已,不会反复运行的逻辑~~~~


最后。把$上的统一方法放到$.fn上~~。这个在上一节中说过,$.zUI.asWidget是一个数组。里面放着插件的名字。这些名字自然是在$.zUI.addWidget这个函数中放进去的~~~

$.each($.zUI.asWidget,function(i,widget){
	unWidget = "un"+widget;
	var w = {};
	w[widget] = function(args){
			this.each(function(){
			$.zUI[widget].fn(this,args);
		});
		return this;
	};
	w[unWidget] = function(){
			this.each(function(){
			$.zUI[widget].unfn(this);
		});
		return this;
	}
	$.fn.extend(w);
});

实现的效果


结语:

不得不说。这不算一个panel。由于没有横向滚动栏,有两个原因,一是由于比較懒。不想实现了,原理类似,另外我比較讨厌横向滚动栏~~~

想下载源码的请看这里:源码





本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5362200.html,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
javascriptMVC教程 -- 14.FuncUnit实例:完成jquery焦点图插件的自动化功能测试
  FuncUnit相关的知识我在之前的博文已有简单介绍,大家可以自行阅读《javascriptMVC入门 -- 12.FuncUnit》。他提供了很多api方法,我的文章中没有涉及,大家可以去官网查看,地址:http://www.javascriptmvc.com/。
865 0
jQuery插件 - jqueryflexselect下拉框自动提示
这个是基于一个下拉列表框中的联想方式选择。这个可以直接使用在有固定选项的功能中做autocomplete类似的功能。   使用方法: jQuery(document).ready(function() { $("select[class*=flexselect]").
819 0
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
7251 0
动态醒目提示插件jquery.pulsate的自定义改造
1、页面引用 基础jquery库jquery.js 2、直接在jquery.pulsate.min.js文件中追加自定义内容,页面引用jquery.pulsate.min.js文件。
913 0
jquery插件整理篇(四)自动补全类插件
(1)AutoComplete-JQueryjQuery插件易于集成到现在的表单中(Form)。 (2)Facebook like Autocomplete 基于jQuery开发,类似于FaceBoox提供的AutoCompleter。
812 0
阿里云服务器安全组设置内网互通的方法
虽然0.0.0.0/0使用非常方便,但是发现很多同学使用它来做内网互通,这是有安全风险的,实例有可能会在经典网络被内网IP访问到。下面介绍一下四种安全的内网互联设置方法。 购买前请先:领取阿里云幸运券,有很多优惠,可到下文中领取。
11676 0
阿里云ECS云服务器初始化设置教程方法
阿里云ECS云服务器初始化是指将云服务器系统恢复到最初状态的过程,阿里云的服务器初始化是通过更换系统盘来实现的,是免费的,阿里云百科网分享服务器初始化教程: 服务器初始化教程方法 本文的服务器初始化是指将ECS云服务器系统恢复到最初状态,服务器中的数据也会被清空,所以初始化之前一定要先备份好。
11328 0
阿里云ECS云服务器初始化设置教程方法
阿里云ECS云服务器初始化是指将云服务器系统恢复到最初状态的过程,阿里云的服务器初始化是通过更换系统盘来实现的,是免费的,阿里云百科网分享服务器初始化教程: 服务器初始化教程方法 本文的服务器初始化是指将ECS云服务器系统恢复到最初状态,服务器中的数据也会被清空,所以初始化之前一定要先备份好。
5836 0
+关注
2969
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《Nacos架构&原理》
立即下载
《看见新力量:二》电子书
立即下载
云上自动化运维(CloudOps)白皮书
立即下载