滚动条那个图片毕竟不是美工,自己切图切的真是恶心到爆了...
总体来说还是可以看的,还是缺少一个动画。在写这个插件意识到自己的插件用到了一些比较基础的函数,于是想到把这些函数应该封装起来,最近仍然在深入学习js,把手头上这本书看完就应该着手写这个基础函数的插件了,当然,动画引擎必不可少。话不多说,源码在此(注意:本插件完整版的是有图片的,请在文末附件中下载完整的插件):
/*
* This plugin is defined on the simulation Webpage scroll bar, please insert after binding for DOM events
*
* Comment version: 1.0.0
* Author:linkfly
* Sina:为你聚焦半世纪 | cnblogs:http://www.cnblogs.com/silin6/ | Email:linkFly6@live.com
* date:2014-02-05 02:38:35
*
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
(function (window, undefined) {
//配置参数信息
var config = {
auto: true,
height: 'auto',
width: 'auto'
};
var linkFlyScroll = function (dom, options) {
/// <summary>
/// 1: 生成模拟滚动条对象,【请在本对象工作之后再为您指定的对象绑定事件,否则您之前绑定的事件将不会进行工作】
///
1.1 - linkFlyScroll(dom) - 在指定的dom上生成滚动条对象
///
1.2 - linkFlyScroll(dom,options) - 生成滚动条对象,同时提供一系列的参数允许您自定义配置该对象的工作模型
/// </summary>
/// <param name="dom" type="String Or element">
/// 传入js的dom对象,或者为string类型的该对象ID
/// </param>
/// <param name="options" type="Json">
/// 自定义配置该对象的工作模型,有如下选项:
///
[可选]auto(Boolean):当内容并未达到容器的高度的时候,是否自动隐藏滚动条,默认为true(是)
///
[可选]height(Int Or String):默认单位为px,可以为int和String.值为auto则默认采用css的高度
///
[可选]width(Int Or String):默认单位为px,可以为int和String.值为auto则默认采用css的宽度
/// </param>
/// <returns type="linkFlyScroll" />
if (typeof (dom) === 'string') {
dom = document.getElementById(dom);
}
//没有指定dom和没有查找到有效的dom
//linkFlyScroll("")、 linkFlyScroll(null)、linkFlyScroll(undefined)
if (!dom || !dom.nodeType)
return this;
//创建容器对象
var scrollObj = document.createElement('div');
//深度克隆内容对象,并未包含事件,所以需要等到linkFlyScroll对象工作完毕后才可以为该dom对象绑定事件
var cloneObj = dom.cloneNode(true);
scrollObj.className = 'rollDiv';
scrollObj.appendChild(cloneObj);
//替换页面上当前对象
dom.parentNode.replaceChild(scrollObj, dom);
return new linkFlyScroll.prototype.init(scrollObj, options ? options : {});
};
linkFlyScroll.prototype.init = function (dom, options) {
/// <summary>
/// 1: 本对象才是真正意义上工作的对象,特殊的工作方式是因为可能存在linkFlyScroll的静态调用和实例化调用
///
1.1 - init(dom,options) - 在指定的dom上生成滚动条对象
/// </summary>
/// <param name="dom" type="element">
/// dom对象
/// </param>
/// <param name="options" type="Json">
/// 自定义配置该对象的工作模型,有如下选项:
///
[可选]auto(Boolean):当内容并未达到容器的高度的时候,是否自动隐藏滚动条,默认为true(是)
///
[可选]height(Int Or String):默认单位为px,可以为int和String.值为auto则默认采用css的高度
///
[可选]width(Int Or String):默认单位为px,可以为int和String.值为auto则默认采用css的宽度
/// </param>
/// <returns type="linkFlyScroll" />
/*
* 本对象包含以下属性:
* isDrag:是否正在拖拽滚动条
* startTop:(工作中)滚动条开始滚动位置
* endTop:(工作中)滚动条结束滚动位置
* topLimit:滚动条顶部极限位置
* bottomLimit:滚动条底部极限位置
* context:内容Dom
* scrollRadix:滚动基数
* target:容器Dom
*/
//当前this对象,为防止this指针在环境中会经常改变(例如绑定事件的时候),所以将当前对象保存起来
var currScroll = this;
//DOMElement
if (dom.nodeType) {
//保存容器和内容DOM
currScroll.target = dom;
currScroll.context = dom.firstChild;
//合并配置参数
currScroll.options = tool.extend(config, options);
if (currScroll.options.width !== 'auto') {
dom.style.width = tool.convertValue(currScroll.options.width);
}
if (currScroll.options.height !== 'auto') {
dom.style.height = tool.convertValue(currScroll.options.height);
}
//查找到有效的dom
while (currScroll.context.nodeType != 1) {
currScroll.context = currScroll.context.nextSibling;
}
//创建滚动条dom
currScroll.scrollUl = document.createElement('ul');
currScroll.scrollUl.className = 'lf_Scroll';
currScroll.scrollUl.appendChild(tool.setClass('li', 'lfs_Top'));
currScroll.scrollUl.appendChild(tool.setClass('li', 'lfs_Center'));
currScroll.scrollUl.appendChild(tool.setClass('li', 'lfs_Bottom'));
currScroll.context.style.position = 'relative';
//先呈现在页面上才可以读取位置数据
dom.appendChild(currScroll.scrollUl);
this.change();
tool.addScrollEvent(currScroll.context, function (e) {
//绑定鼠标滚轮事件,3px滚动单位
if (e.wheel > 0) {//滚轮向上滚动
var currTop = currScroll.endTop -= 3;
currScroll.scrollEvent.call(currScroll, currTop);
} else {//滚轮向下滚动
var currTop = currScroll.endTop += 3;
currScroll.scrollEvent.call(currScroll, currTop);
}
});
//需要处理禁止文字在拖动的时候被选中 TODO
//鼠标点下事件,需要判断是否是左键点击,目前右键也会实现滚动 TODO
tool.addEvent(currScroll.scrollUl, 'mousedown', function (e) {
mouseDown.call(currScroll, e);
});
//追加事件,为了更好的用户体验在body上实现监听
tool.addEvent(document.body, 'mousemove', function (e) {
if (currScroll.isDrag) {
//获取当前鼠标位置
var position = tool.getMousePos(e);
//当前滚动条top位置
var currTop = (currScroll.endTop + position.y - currScroll.startTop);
//call是为了让this指针准确的指向本工作对象
currScroll.scrollEvent.call(currScroll, currTop);
}
return false;
});
//追加鼠标释放事件,为了准确的捕捉到释放事件在body上监听
tool.addEvent(document.body, 'mouseup', function () {
mouseUp.call(currScroll, []);
});
var mouseDown = function (e) {
/// <summary>
/// 1: 鼠标按下事件
///
1.1 - mouseDown(e) - 滚动条中鼠标按下滚动条事件
/// </summary>
/// <param name="e" type="Event">
/// Event对象
/// </param>
/// <returns type="linkFlyScroll" />
currScroll.isDrag = true;
//获取当前鼠标y位置
currScroll.startTop = tool.getMousePos(e).y;
tool.addClass(currScroll.scrollUl, 'lf_ScrollFocus');
return false;
};
var mouseUp = function () {
/// <summary>
/// 1: 鼠标释放事件
///
1.1 - mouseUp() - 滚动条中鼠标释放滚动条事件
/// </summary>
/// <returns type="linkFlyScroll" />
currScroll.isDrag = false;
currScroll.endTop = currScroll.scrollUl.style.top ? parseInt(currScroll.scrollUl.style.top) : 0;
tool.removeClass(currScroll.scrollUl, 'lf_ScrollFocus');
return false;
};
currScroll.scrollEvent = function (currTop) {
/// <summary>
/// 1: 滚动事件(核心),传入需要滚动的坐标即可(滚动条top)
///
1.1 - scrollEvent(currTop) - 核心滚动事件
/// </summary>
/// <param name="currTop" type="Int">
/// 滚动条顶部距离上一层容器的top值
/// </param>
/// <returns type="void" />
if (currTop <= currScroll.topLimit || currTop < 0) {//顶部极限
currTop = currScroll.topLimit;
} else if (currTop >= currScroll.bottomLimit) {//底部极限
currTop = currScroll.bottomLimit;
}
//滚动条偏移效果
currScroll.scrollUl.style.top = currTop + 'px';
var tempTop = parseInt(currScroll.context.style.top ? currScroll.context.style.top : 0);
//debug code
// document.getElementById('postionInfo').innerHTML = 'currTop:' + currTop + ' 滚动基数:' + currScroll.scrollRadix + ' bottomLimit:' + currScroll.bottomLimit + ' endTop:' + currScroll.endTop + ' startTop:' + currScroll.startTop + " Y:" + currTop + " offsetTop:" + currScroll.scrollUl.offsetTop + " compute:" + (currTop * currScroll.scrollRadix * -1) + 'px';
//text code
//内容滚动:当前滚动条top*基数取负数
currScroll.context.style.top = currTop * currScroll.scrollRadix * -1 + 'px';
};
return currScroll;
};
};
linkFlyScroll.prototype.init.prototype.change = function () {
/// <summary>
/// 1: 滚动条内容改变函数
///
1.1 - change() - 本函数代表刷新本滚动条对象的数据,在某些情况下,内容的数据是一直在变化的,可以调用本函数对当前滚动条对象刷新数据
/// </summary>
/// <returns type="linkFlyScroll" />
/*
* linkFlyScroll包含的属性主要在本函数中初始化或重新定义:
* isDrag:是否正在拖拽滚动条
* startTop:(工作中)滚动条开始滚动位置
* endTop:(工作中)滚动条结束滚动位置
* topLimit:滚动条顶部极限位置
* bottomLimit:滚动条底部极限位置
* context:内容Dom
* scrollRadix:滚动基数
* target:容器Dom
*/
//重置或读取一系列数据
var currScroll = this;
currScroll.isDrag = false,
currScroll.startTop = 0,
currScroll.endTop = (currScroll.scrollUl.style.top ? parseInt(currScroll.scrollUl.style.top) : 0),
currScroll.topLimit = currScroll.target.scrollTop,
currScroll.bottomLimit = currScroll.target.clientHeight,
currScroll.scrollRadix = 10;
//得出滚动条的高度:内容高度*(容器高度/内容高度=容器占内容百分比)
var scrollPx = currScroll.target.clientHeight * (currScroll.target.clientHeight / currScroll.context.offsetHeight);
//滚动条高度
currScroll.scrollUl.childNodes[1].style.height = scrollPx + 'px';
if (currScroll.context.clientHeight <= currScroll.target.clientHeight && currScroll.options.auto) {
currScroll.scrollUl.style.display = 'none';
} else {
currScroll.scrollUl.style.display = 'block';
//当滚动条显示,修正最大位置数据
currScroll.bottomLimit -= currScroll.scrollUl.offsetHeight;
}
//设置滚动条滚动基数(滚动条没滚动1px内容滚动像素):(内容高度-容器高度[因为当前容器已经显示了一屏])/滚动条top(滚动条空白可滚动高度)
currScroll.scrollRadix = (currScroll.context.offsetHeight - currScroll.target.clientHeight) / currScroll.bottomLimit;
return currScroll;
};
linkFlyScroll.prototype.init.prototype.roll = function (value) {
/// <summary>
/// 1: 滚动条偏移方法
///
1.1 - roll(value) - 滚动条滚动方法
/// </summary>
/// <param name="value" type="Int">
/// 滚动条目标滚动的百分比
/// </param>
/// <returns type="linkFlyScroll" />
var currScroll = this;
if (typeof (value) !== 'number') {
return currScroll;
}
var currTop = (currScroll.bottomLimit - currScroll.topLimit) * value / 100;
currScroll.scrollEvent(currTop);
currScroll.endTop = currTop;
return currScroll;
};
/*
* 工具类
*/
var tool = {
setClass: function (element, className) {
/// <summary>
/// 1: 设置元素节点的class属性
///
1.1 - setClass(element,className) - 设置元素节点的class属性,如没有该节点则创建该节点,并返回修改后的节点对象
/// </summary>
/// <param name="element" type="Element Or String">
/// 传入String则创建该节点,否则修改该节点
/// </param>
/// <param name="className" type="String">
/// Class Name
/// </param>
/// <returns type="Element" />
if (typeof element === 'string') {
element = document.createElement(element);
}
element.className = className;
return element;
},
hasClass: function (element, className) {
/// <summary>
/// 1: 判断元素是否有class
///
1.1 - hasClass(element,className) - 判断元素是否有class,在业务中异常(基本没有该情况的发生)和有该class返回true,否则返回false
/// </summary>
/// <param name="element" type="Element Or String">
/// 节点对象
/// </param>
/// <param name="className" type="String">
/// Class Name
/// </param>
/// <returns type="Element" />
if (!element || element.nodeType !== 1)//让异常通过,外面不进行处理
return true;
var elementClassName = element.className;
if (elementClassName.length < 1) {
return false;
}
if (elementClassName == className || elementClassName.match(new RegExp("(^|//s)" + className + "(
//s|$)"))) {
return true;
}
return false;
},
addClass: function (element, className) {
/// <summary>
/// 1: 为元素【追加】class
///
1.1 - addClass(element,className) - 为元素【追加】class,并返回修改后的class
/// </summary>
/// <param name="element" type="Element Or String">
/// 节点对象
/// </param>
/// <param name="className" type="String">
/// Class Name
/// </param>
/// <returns type="Element" />
if (!tool.hasClass(element, className)) {
if (element.className.length < 1) {
element.className = className;
} else {
element.className += ' ' + className;
}
}
return element;
},
removeClass: function (element, className) {
/// <summary>
/// 1: 为元素移除class
///
1.1 - addClass(element,className) - 为元素移除class,并返回修改后的class
/// </summary>
/// <param name="element" type="Element Or String">
/// 节点对象
/// </param>
/// <param name="className" type="String">
/// Class Name
/// </param>
/// <returns type="Element" />
if (tool.hasClass(element, className)) {
var reg = new RegExp("(^|//s)" + className + "(
//s|$)");
element.className = element.className.replace(reg, '');
}
return element;
},
css: function (element, key) {
/// <summary>
/// 1: 获取元素css指定的属性值
///
1.1 - css(element,className) - 获取元素css指定的属性值
/// </summary>
/// <param name="element" type="Element Or String">
/// 节点对象
/// </param>
/// <param name="key" type="String">
/// 要获取的css属性
/// </param>
/// <returns type="String" />
return element.currentStyle ? element.currentStyle[key] : document.defaultView.getComputedStyle(element, false)[key];
},
addEvent: function (element, type, fn) {
/// <summary>
/// 1: 为元素追加事件
///
1.1 - css(element, type, fn) - 为元素追加事件,函数中this指向事件源
/// </summary>
/// <param name="element" type="Element Or String">
/// 节点对象
/// </param>
/// <param name="type" type="String">
/// 追加的事件名,不含字符on
/// </param>
/// <param name="fn" type="Function">
/// 事件对象
/// </param>
/// <returns type="void" />
if (element.attachEvent) {
element['e' + type + fn] = fn;
element[type + fn] = function () { element['e' + type + fn](window.event); }
element.attachEvent('on' + type, element[type + fn]);
} else if (element.addEventListener) {
element.addEventListener(type, fn, false);
}
},
// removeEvent: function (element, type, fn) {
// /// <summary>
// /// 1: 为元素删除事件,本函数并未用到
// ///
1.1 - removeEvent(element, type, fn) - 为元素删除事件
// /// </summary>
// /// <param name="element" type="Element Or String">
// /// 节点对象
// /// </param>
// /// <param name="type" type="String">
// /// 删除的事件名
// /// </param>
// /// <param name="key" type="String">
// /// 删除的事件的函数名
// /// </param>
// /// <returns type="void" />
// if (element.detachEvent) {
// element.detachEvent('on' + type, element[type + fn]);
// element[type + fn] = null;
// } else if (element.removeEventListener) {
// element.removeEventListener(type, fn, false);
// }
// },
addScrollEvent: function (element, fn) {
/// <summary>
/// 1: 追加ScrollEvent事件
///
1.1 - addScrollEvent(element,fn) - 在元素上追加ScrollEvent事件(特殊事件,在元素上鼠标滚轮滚动事件)
/// </summary>
/// <param name="element" type="Element Or String">
/// 元素节点
/// </param>
/// <param name="fn" type="Function">
/// 事件方法
/// </param>
/// <returns type="void" />
var bindScrollFn = function (e) {
e = e || window.event;
//判断滚轮滚动方向:Firefox和其他浏览器不同
e.wheel = (e.wheelDelta ? e.wheelDelta : -e.detail) > 0 ? 1 : -1; // 通过事件判断鼠标滚轮反向,1是向上,-1是向下
//阻止浏览器默认行为
if (e.preventDefault) { //ff
e.preventDefault();
} else {
e.returnValue = false; //ie
}
fn.call(element, e);
}
if (document.addEventListener) {
//ff
element.addEventListener('DOMMouseScroll', bindScrollFn, false);
//w3c
element.addEventListener('mousewheel', bindScrollFn, false);
} else//ie
{
element.attachEvent('onmousewheel', bindScrollFn);
}
},
getEvent: function () {
/// <summary>
/// 1: 获取Event对象
///
1.1 - getEvent() - 在无参数的情况下获取Event对象,同时兼容性处理IE和FF
/// </summary>
/// <returns type="Event" />
if (document.all) {
return window.event;
}
func = getEvent.caller;
while (func != null) {
var arg0 = func.arguments[0];
if (arg0) {
if ((arg0.constructor == Event || arg0.constructor == MouseEvent) || (typeof (arg0) == "object" && arg0.preventDefault && arg0.stopPropagation)) {
return arg0;
}
}
func = func.caller;
}
return null;
},
getMousePos: function (ev) {
/// <summary>
/// 1: 获取当前鼠标坐标
///
1.1 - getMousePos(ev) - 获取当前鼠标坐标,兼容性处理,返回的对象格式:{ x:鼠标x坐标 , y:鼠标y坐标 }
/// </summary>
/// <param name="ev" type="Event">
/// Event事件对象
/// </param>
/// <returns type="Json" />
if (!ev) {
ev = currScroll.getEvent();
}
if (ev.pageX || ev.pageY) {
return {
x: ev.pageX,
y: ev.pageY
};
}
if (document.documentElement && document.documentElement.scrollTop) {
return {
x: ev.clientX + document.documentElement.scrollLeft - document.documentElement.clientLeft,
y: ev.clientY + document.documentElement.scrollTop - document.documentElement.clientTop
};
}
else if (document.body) {
return {
x: ev.clientX + document.body.scrollLeft - document.body.clientLeft,
y: ev.clientY + document.body.scrollTop - document.body.clientTop
};
}
},
extend: function (oldObj, newObj) {
/// <summary>
/// 1: 将两个对象进行合并
///
1.1 - extend(oldObj,newObj) - 将两个对象合并,并返回合并后的对象,采用clone的方式实现,所以不会对两个对象产生任何影响
/// </summary>
/// <param name="oldObj" type="Object">
/// 要合并的对象A,该对象作为基础对象,将新对象的同名属性覆盖到基础对象中
/// </param>
/// <param name="newObj" type="Object">
/// 要合并的对象B
/// </param>
/// <returns type="Object" />
var tempObj = tool.clone(oldObj);
for (var key in newObj) {
if (newObj.hasOwnProperty(key) && !tempObj.hasOwnProperty(key)) {
tempObj[key] = newObj[key];
}
}
return tempObj;
},
clone: function (obj) {
/// <summary>
/// 1: 克隆一个对象
///
1.1 - clone(obj) - 克隆一个对象,并返回克隆后的新对象,该对象的原型是被克隆的对象
/// </summary>
/// <param name="obj" type="Object">
/// 要克隆的对象
/// </param>
/// <returns type="Object" />
function Clone() { }
Clone.prototype = obj;
var newObj = new Clone();
for (var key in newObj) {
if (typeof newObj[key] == "object") {
newObj[key] = tool.clone(newObj[key]);
}
}
return newObj;
},
convertValue: function (value) {
/// <summary>
/// 1: 将数值转换为有效的数值
///
1.1 - convertValue(value) - 将Json配置的css数值转换为有效的数值,请保证value的值不为"auto"
/// </summary>
/// <param name="value" type="Object">
/// 要转换的数值
/// </param>
/// <returns type="Object" />
var reg = /^/d+$/g;
if (typeof (value) === 'number' || reg.test(value)) {
return value + 'px';
} else
return value;
}
};
//注册到window下
window.linkFlyScroll = linkFlyScroll;
//注册到window.so命名空间下
if (!window.so) {
window.so = {};
}
window.so.scroll = window.linkFlyScroll;
})(window);