自己写一个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,因为没有横向滚动条,有两个原因,一是因为比较懒,不想实现了,原理类似,另外我比较讨厌横向滚动条~~~

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

时间: 2024-10-25 20:22:28

自己写一个jQuery垂直滚动条插件(panel)的相关文章

如何写一个Jquery 的Plugin插件

博客分类: Javascript /Jquery / Bootstrap / Web jQuery配置管理脚本FirebugJavaScript JQuery Plugin插件,如果大家不明白什么是JQuery插件或都不清楚如何编写可以查看其官方的网站:jQuery Authoring Guidelines 好了,下面有一些我觉得想做一个好的插件必须应有的要求: 1.在JQuery命名空间下声明只声明一个单独的名称 2.接受options参数,以便控制插件的行为 3.暴露插件的默认设置 ,以便外

jquery美化滚动条插件jscrollpane应用(转)

原文地址:http://www.jqcool.net/jquery-jscrollpane.html jScrollPane是一个设计非常灵活的跨浏览器的jQuery ,它将浏览器的默认滚动条或是元素溢出时出现的滚动条映射成一个HTML结构,从而很容易地使用CSS来定义滚动的样式.下面来看下这个强大而灵活的jScrollPane的使用方法吧. 引入核心文件,为了更方便的使用,这里引入了mousewheel库 <!-- styles specific to demo site --> <l

基于jQuery的滚动条插件-jquery.jscrollbar

jquery.jscrollbar 是一个基于jQuery的滚动条插件,支持水平滚动条和垂直滚动条,支持鼠标键盘事件 主要功能 支持水平滚动条 支持垂直滚动条 自动判断水平滚动条和垂直滚动条是否显示 支持外部调用来滚动内容 支持滚动条部分样式自定义 支持键盘方向键控制 支持鼠标滚动(需要mousewheel插件) 支持滚动条显示位置设置(外部|悬浮) 支持手动更新界面 依赖的库 jQuery (http://jquery.com/) jquery.jqdrag (https://github.c

自己写一个 jQuery 插件

我知道这一天终将会到来,现在,它来了. 需求 开发 SharePoint 的 CSOM 应用时,经常需要在网页上输出一些信息. 这种需求和 alert 的弹窗.F12 的断点查看信息的场景是不一样的:在这种场景下,你就是端着杯咖啡靠在那里坐着,看着关于应用程序运行的信息不断的在网页上输出,不用去关闭弹窗.也不用去设置断点. 详细的功能需求如下: 输出的信息中自动包含时间 输出的信息按照类型显示不同的样式 输出的信息类型包括:普通信息.调试信息.警告.错误 可选按照时间升序输出信息(稍作修改可以时

写一个简单的JQ插件(例子)

虽然现在 vue angular react 当道啊但是那 JQ还是有一席之地很多很多的小单位啊.其实还会用到 我也放一个例子吧虽然我也不是很肯定有没有人写的比我更好啊但是我相信 我这个还是蛮实用的 话不多说 丢代码 JQ插件标准的封装代码如下,首先需要闭包: <scripttype="text/javascript"> (function ($) {  //这里放入插件代码 })(jQuery); </script> 这是jQuery官方的插件开发规范,这样写

写一个Sublime Text 2插件(CSS文件里px单位替换成rem单位)

三年前我就知道了sublime text 不过那时候用DW还是很爽的样子,后来有天想为难自己了,于是用了两年的vim和五笔,最近又觉得这么好编辑器也可以试试,改变一下自己,用一下的,不过由于工作的原因,没有坚持下来,有时候顺手似乎比先进更重要一些. 最近工作都是做一些移动端的页面,而微信的长按出现二维码有个bug,在ios里缩放的页面长按是不会出现"识别二维码"的.所以需要转换一下,不要让页面缩放,要自适应,这个时候就用rem单位来做的会比较好一些,而我的同事之前开发了一个px to

自己写一个jquery

通过阅读jquery原代码, 我们可以模拟写一个简单的jquery 比如常用的 jQuery("div").css("color","red");jQuery("#span1").css("color","green"); 1. jQuery(), 因为是链式调用, 所以返回是一个对象. jQuery = function(selector){ return new jQuery.pro

jQuery自定义滚动条插件mCustomScrollbar

写项目的时候遇到了这种情况 我用了两个iframe,左边是一个菜单栏,可以展开也可以收回去的.就说这个滚动条太丑了,是浏览器默认的,所以学着用mCustomScrollbar来解决一下这个问题. 1:资源下载 https://github.com/malihu/malihu-custom-scrollbar-plugin,我是从这里下载的. 2:引入 注意顺序. <link rel="stylesheet" type="text/css" href="

Jquery自定义滚动条插件

下载地址:http://files.cnblogs.com/files/LoveOrHate/jquery.nicescroll.min.js <script src="jquery.nicescroll.js"></script> $(document).ready( function() { $("html").niceScroll(); } ); var nice = false; $(document).ready( function