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

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWFuZ3lhbmdfY3M=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

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

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

先起个名字,外层的叫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);
});

实现的效果

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWFuZ3lhbmdfY3M=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

结语:

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

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

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

  1. 如何写一个jquery插件

      本文总结整理一下如何写一个jquery插件?虽然现今各种mvvm框架异常火爆,但是jquery这个陪伴我们成长,给我们带来很多帮助的优秀的库不应该被我们抛弃,写此文章,作为对以往欠下的笔记的补充, ...

  2. 【CSS】隐藏多行文本框Textarea在IE中的垂直滚动栏

    在<[CSS]禁止Google浏览器同意定义调整多行文本框>(点击打开链接)中已经提及过怎样使多行文本框Textarea在一些DOM2的浏览器中固定下来. 这不,多行文本框Textarea ...

  3. jQuery.hhNewSilder 滚动图片插件

    /**  * jQuery.hhNewSilder 滚动图片插件  * User: huanhuan  * QQ: 651471385  * Email: th.wanghuan@gmail.com ...

  4. 从0开始写一个简单的vite hmr 插件

    从0开始写一个简单的vite hmr 插件 0. 写在前面 在构建前端项目的时候,除开基本的资源格式(图片,json)以外,还常常会需要导入一些其他格式的资源,这些资源如果没有第三方vite插件的支持 ...

  5. 自己写一个 jQuery 插件

    我知道这一天终将会到来,现在,它来了. 需求 开发 SharePoint 的 CSOM 应用时,经常需要在网页上输出一些信息. 这种需求和 alert 的弹窗.F12 的断点查看信息的场景是不一样的: ...

  6. 如何使用jQuery写一个jQuery插件

    jQuery插件其实是前端框架的思维,构成一个框架,个人认为必须满足以下几个基础条件:1. 可重用,2. 兼容性,3. 维护方便,虽说现在有很多比较成熟的前端框架,但是也有部分存在配置麻烦,学习成本大 ...

  7. 被逼着写的jquery工作日管理日历插件

    因为工作原因,在我刚进入新公司之后,立马要求让我做一个jquery的插件demo.我的天,我面试的可是.net工程师啊.虽然以前接触过js,jquery,但也只是接触过一丢丢啊,没办法,只好硬着头皮上 ...

  8. 分享一个jQuery动态网格布局插件:Masonry(转)

    在线演示 Masonry是 一款非常强大的jQuery动态网格布局插件,可以帮助开发人员快速开发类似剪贴画的界面效果.和CSS中float的效果不太一样的地方在 于,float先水平排列,然后再垂直排 ...

  9. 自己写一个jqery的拖拽插件

    说实话,jQuery比原生的js好用多了,本来想用原生写的,也写出来的,仅仅是,感觉不像插件,所以用jQuery实现了一版. 实现的功能:能够指定拖拽的边界,在拖拽过程中,能够触发几个自己定义事件 先 ...

随机推荐

  1. 【C#学习笔记】调用C++生成的DLL

    首先用vs2010建立win32项目,选择dll和空项目. 头文件add.h extern "C" __declspec(dllexport) int add(int a,int ...

  2. db2数据库sql报错信息

    sqlcode sqlstate 说明 000 00000 SQL语句成功完成   01xxx SQL语句成功完成,但是有警告 +012 01545 未限定的列名被解释为一个有相互关系的引用 +098 ...

  3. 【转】iOS-延迟操作方法总结

    原文网址:http://lysongzi.com/2016/01/30/iOS-%E5%BB%B6%E8%BF%9F%E6%93%8D%E4%BD%9C%E6%96%B9%E6%B3%95%E6%80 ...

  4. Json::Value使用心得

    Json::Value 是sourceforge开源项目jsoncpp的数据对象,用来处理json数据  下载 1.打印Json数据 Json::Value jv; Json::FastWriter ...

  5. Delphi 让自己的软件实现双击打开文件 转

    unit shjAssociateFileType; interface uses Windows, Registry; {将文件类型strFileExtension与程序strExeFileName ...

  6. <转>Python3.x和Python2.x的区别介绍

    1.性能Py3.0运行 pystone benchmark的速度比Py2.5慢30%.Guido认为Py3.0有极大的优化空间,在字符串和整形操作上可以取得很好的优化结果.Py3.1性能比Py2.5慢 ...

  7. LoadRunner检查点实战

    码农博客 即将到期,现将博客中部分文章转载到博客园.转载时略有删减. 一.为什么要使用检查点 为什么要使用检查点,那就要说明一下LR如何判断脚本是否执行成功. LR判断脚本是否执行成功是根据服务器返回 ...

  8. C++ 使用Htmlcxx解析Html内容(VS编译库文件)

    1.下载Htmlcxx,http://sourceforge.net/projects/htmlcxx/ 2.解压htmlcxx-0.85.tar.gz 3.打开htmlcxx.vcproj,注意是h ...

  9. Oracle11g导入*.dmp数据文件

    imp命令导入数据:imp username/password@SID file=XXX.dmp fromuser=XXX touser=XXX tables=(XXX,XXX)  [ignore=y ...

  10. redo文件一

    redo log files and redo log buffer redo log files的作用的是确保数据库崩溃之后能正确的恢复数据库,恢复数据库到一,致性的状态 redo log file ...