[转帖]Mootools源码分析-48 -- Slider
原帖地址:http://space.flash8.net/space/?uid-18713-action-viewspace-itemid-410192
原作者:我佛山人
代码
//滑动条
//演示:http://demos.mootools.net/Slider
var Slider = new Class({
//继承实现Events和Options,UI插件的特征
Implements: [Events, Options],
options: {/*
//位置改变事件
onChange: $empty,
//拖动改变位置完成事件
onComplete: $empty,*/
//每一步的事件
onTick: function(position) {
//如果指定吸附,根据步数计算位置
if(this.options.snap) position = this.toPosition(this.step);
//设置滑块的位置
this.knob.setStyle(this.property, position);
},
snap: false,
//偏移量
offset: 0,
//指定滑动范围
range: false,
//滚轮支持
wheel: false,
//总步数
steps: 100,
//滑动模式,水平或纵向
mode: 'horizontal'
},
//构造函数,需要三个参数
//element为滑块所在容器,knob为滑块
initialize: function(element, knob, options) {
//合并参数
this.setOptions(options);
//取滑块所在的容器对象
this.element = $(element);
//滑块
this.knob = $(knob);
//初始化位置标记变量
this.previousChange = this.previousEnd = this.step = -1;
//添加鼠标按下的事件监听,实现点击时自动滑到指定位置
this.element.addEvent('mousedown', this.clickedElement.bind(this));
//如果设置支持滚轮,为容器添加滚轮监听事件
if (this.options.wheel) this.element.addEvent('mousewheel', this.scrolledElement.bindWithEvent(this));
//初始化
var offset, limit = {}, modifiers = {'x': false, 'y': false};
//根据不同滑动模式初始化变量值
switch (this.options.mode) {
//纵向滑动时
case 'vertical':
//别名为y轴方向
this.axis = 'y';
//这时主要是需要改变滑块CSS属性中的top
this.property = 'top';
//此时需要取值的属性为offsetHeiht
offset = 'offsetHeight';
break;
//横向滑动时
case 'horizontal':
//别名为x轴方向
this.axis = 'x';
//这时主要是需要改变滑块CSS属性中的left
this.property = 'left';
//此时需要取值的属性为offsetWidth
offset = 'offsetWidth';
}
//取滑块在当前滑动方向上的尺寸的一半
this.half = this.knob[offset] / 2;
//计算滑块移动的总距离
this.full = this.element[offset] - this.knob[offset] + (this.options.offset * 2);
//取范围的下限,默认为0
this.min = $chk(this.options.range[0]) ? this.options.range[0] : 0;
//取范围的上限,默认为滑动的总步数
this.max = $chk(this.options.range[1]) ? this.options.range[1] : this.options.steps;
//计算范围值
this.range = this.max - this.min;
//总步数,如果steps参数值为0,则设为滑块移动的总距离
this.steps = this.options.steps || this.full;
//计算每滑动一步的距离
this.stepSize = Math.abs(this.range) / this.steps;
//计算每一步所占的宽度(根据上面的等级,这里不如直接用this.stepWidth = this.full/this.steps)
this.stepWidth = this.stepSize * this.full / Math.abs(this.range) ;
//滑块相对于容器为相对定位
this.knob.setStyle('position', 'relative').setStyle(this.property, - this.options.offset);
//当前方向上对应的属性,转化用于Drag类的参数
modifiers[this.axis] = this.property;
//滑块的坐标范围
limit[this.axis] = [- this.options.offset, this.full - this.options.offset];
//使滑块可拖动
this.drag = new Drag(this.knob, {
snap: 0,
//滑块的可拖动范围
limit: limit,
//指定拖动的方向及对应属性
modifiers: modifiers,
onDrag: this.draggedKnob.bind(this),
onStart: this.draggedKnob.bind(this),
onComplete: function() {
this.draggedKnob();
this.end();
}.bind(this)
});
//如果指定吸附值
if (this.options.snap) {
//滑块拖动将网格化,单元格边长为滑动每一步所占的宽度
this.drag.options.grid = Math.ceil(this.stepWidth);
//改变移动范围的上限,此时忽略偏移量
this.drag.options.limit[this.axis][1] = this.full;
}
},
//设置值
set: function(step) {
//当范围上下限从小到大并且当前步值小于下限,或当范围上下限从大到小并且当前步值大于下限时,设置步值为下限值
if (!((this.range > 0) ^ (step < this.min))) step = this.min;
//当范围上下限从小到大并且当前步值大于是限,或当范围上下限从大到小并且当前步值小于上限时,设置步值为上限值
if (!((this.range > 0) ^ (step > this.max))) step = this.max;
//计算步数
this.step = Math.round(step);
//检查步数变化,以决定是否触发事件
this.checkStep();
//结束滑动
this.end();
//触发步进事件
this.fireEvent('onTick', this.toPosition(this.step));
return this;
},
//点击直接跳到指定位置
clickedElement: function(event) {
//判断方向
var dir = this.range < 0 ? -1 : 1;
//取当前模式下的鼠标在容器内的偏移值
var position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half;
//边界处理
position = position.limit(-this.options.offset, this.full -this.options.offset);
//计算步数
this.step = Math.round(this.min + dir * this.toStep(position));
//检查步数变化,以决定是否触发事件
this.checkStep();
//结束滑动
this.end();
//触发步进事件
this.fireEvent('onTick', position);
},
//利用滚轮跳到指定位置
scrolledElement: function(event) {
//横向滑动和纵向滑动时,滚轮前滚和后滚导致的滑块的变化方向相反
var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0);
//mode为真是向小值(左上)滑动,返之向大值(右下)滑动
this.set(mode ? this.step - this.stepSize : this.step + this.stepSize);
//阻止事件冒泡及返回值
event.stop();
},
//滑块移动时的处理
draggedKnob: function() {
//判断方向
var dir = this.range < 0 ? -1 : 1;
//取当前模式下的鼠标坐标值
var position = this.drag.value.now[this.axis];
//边界处理
position = position.limit(-this.options.offset, this.full -this.options.offset);
//计算当前的步数
this.step = Math.round(this.min + dir * this.toStep(position));
//检查滑动位置有无变化
this.checkStep();
},
//检查步数有无变化
checkStep: function() {
//两值不等说明有改变
if (this.previousChange != this.step) {
//更新旧值
this.previousChange = this.step;
//触发onChange事件,传送当前步数作参数
this.fireEvent('onChange', this.step);
}
},
//结束滑动,检查步数有无变化
end: function() {
//两值不等说明有改变
if (this.previousEnd !== this.step) {
//更新旧值
this.previousEnd = this.step;
//触发onComplete事件,传送当前步数作参数
this.fireEvent('onComplete', this.step + '');
}
},
//根据鼠标位置计算当前的步数
toStep: function(position) {
var step = (position + this.options.offset) * this.stepSize / this.full * this.steps;
return this.options.steps ? Math.round(step -= step % this.stepSize) : step;
},
//根据当前的步数计算滑块的位置
toPosition: function(step) {
return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) - this.options.offset;
}
});
//演示:http://demos.mootools.net/Slider
var Slider = new Class({
//继承实现Events和Options,UI插件的特征
Implements: [Events, Options],
options: {/*
//位置改变事件
onChange: $empty,
//拖动改变位置完成事件
onComplete: $empty,*/
//每一步的事件
onTick: function(position) {
//如果指定吸附,根据步数计算位置
if(this.options.snap) position = this.toPosition(this.step);
//设置滑块的位置
this.knob.setStyle(this.property, position);
},
snap: false,
//偏移量
offset: 0,
//指定滑动范围
range: false,
//滚轮支持
wheel: false,
//总步数
steps: 100,
//滑动模式,水平或纵向
mode: 'horizontal'
},
//构造函数,需要三个参数
//element为滑块所在容器,knob为滑块
initialize: function(element, knob, options) {
//合并参数
this.setOptions(options);
//取滑块所在的容器对象
this.element = $(element);
//滑块
this.knob = $(knob);
//初始化位置标记变量
this.previousChange = this.previousEnd = this.step = -1;
//添加鼠标按下的事件监听,实现点击时自动滑到指定位置
this.element.addEvent('mousedown', this.clickedElement.bind(this));
//如果设置支持滚轮,为容器添加滚轮监听事件
if (this.options.wheel) this.element.addEvent('mousewheel', this.scrolledElement.bindWithEvent(this));
//初始化
var offset, limit = {}, modifiers = {'x': false, 'y': false};
//根据不同滑动模式初始化变量值
switch (this.options.mode) {
//纵向滑动时
case 'vertical':
//别名为y轴方向
this.axis = 'y';
//这时主要是需要改变滑块CSS属性中的top
this.property = 'top';
//此时需要取值的属性为offsetHeiht
offset = 'offsetHeight';
break;
//横向滑动时
case 'horizontal':
//别名为x轴方向
this.axis = 'x';
//这时主要是需要改变滑块CSS属性中的left
this.property = 'left';
//此时需要取值的属性为offsetWidth
offset = 'offsetWidth';
}
//取滑块在当前滑动方向上的尺寸的一半
this.half = this.knob[offset] / 2;
//计算滑块移动的总距离
this.full = this.element[offset] - this.knob[offset] + (this.options.offset * 2);
//取范围的下限,默认为0
this.min = $chk(this.options.range[0]) ? this.options.range[0] : 0;
//取范围的上限,默认为滑动的总步数
this.max = $chk(this.options.range[1]) ? this.options.range[1] : this.options.steps;
//计算范围值
this.range = this.max - this.min;
//总步数,如果steps参数值为0,则设为滑块移动的总距离
this.steps = this.options.steps || this.full;
//计算每滑动一步的距离
this.stepSize = Math.abs(this.range) / this.steps;
//计算每一步所占的宽度(根据上面的等级,这里不如直接用this.stepWidth = this.full/this.steps)
this.stepWidth = this.stepSize * this.full / Math.abs(this.range) ;
//滑块相对于容器为相对定位
this.knob.setStyle('position', 'relative').setStyle(this.property, - this.options.offset);
//当前方向上对应的属性,转化用于Drag类的参数
modifiers[this.axis] = this.property;
//滑块的坐标范围
limit[this.axis] = [- this.options.offset, this.full - this.options.offset];
//使滑块可拖动
this.drag = new Drag(this.knob, {
snap: 0,
//滑块的可拖动范围
limit: limit,
//指定拖动的方向及对应属性
modifiers: modifiers,
onDrag: this.draggedKnob.bind(this),
onStart: this.draggedKnob.bind(this),
onComplete: function() {
this.draggedKnob();
this.end();
}.bind(this)
});
//如果指定吸附值
if (this.options.snap) {
//滑块拖动将网格化,单元格边长为滑动每一步所占的宽度
this.drag.options.grid = Math.ceil(this.stepWidth);
//改变移动范围的上限,此时忽略偏移量
this.drag.options.limit[this.axis][1] = this.full;
}
},
//设置值
set: function(step) {
//当范围上下限从小到大并且当前步值小于下限,或当范围上下限从大到小并且当前步值大于下限时,设置步值为下限值
if (!((this.range > 0) ^ (step < this.min))) step = this.min;
//当范围上下限从小到大并且当前步值大于是限,或当范围上下限从大到小并且当前步值小于上限时,设置步值为上限值
if (!((this.range > 0) ^ (step > this.max))) step = this.max;
//计算步数
this.step = Math.round(step);
//检查步数变化,以决定是否触发事件
this.checkStep();
//结束滑动
this.end();
//触发步进事件
this.fireEvent('onTick', this.toPosition(this.step));
return this;
},
//点击直接跳到指定位置
clickedElement: function(event) {
//判断方向
var dir = this.range < 0 ? -1 : 1;
//取当前模式下的鼠标在容器内的偏移值
var position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half;
//边界处理
position = position.limit(-this.options.offset, this.full -this.options.offset);
//计算步数
this.step = Math.round(this.min + dir * this.toStep(position));
//检查步数变化,以决定是否触发事件
this.checkStep();
//结束滑动
this.end();
//触发步进事件
this.fireEvent('onTick', position);
},
//利用滚轮跳到指定位置
scrolledElement: function(event) {
//横向滑动和纵向滑动时,滚轮前滚和后滚导致的滑块的变化方向相反
var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0);
//mode为真是向小值(左上)滑动,返之向大值(右下)滑动
this.set(mode ? this.step - this.stepSize : this.step + this.stepSize);
//阻止事件冒泡及返回值
event.stop();
},
//滑块移动时的处理
draggedKnob: function() {
//判断方向
var dir = this.range < 0 ? -1 : 1;
//取当前模式下的鼠标坐标值
var position = this.drag.value.now[this.axis];
//边界处理
position = position.limit(-this.options.offset, this.full -this.options.offset);
//计算当前的步数
this.step = Math.round(this.min + dir * this.toStep(position));
//检查滑动位置有无变化
this.checkStep();
},
//检查步数有无变化
checkStep: function() {
//两值不等说明有改变
if (this.previousChange != this.step) {
//更新旧值
this.previousChange = this.step;
//触发onChange事件,传送当前步数作参数
this.fireEvent('onChange', this.step);
}
},
//结束滑动,检查步数有无变化
end: function() {
//两值不等说明有改变
if (this.previousEnd !== this.step) {
//更新旧值
this.previousEnd = this.step;
//触发onComplete事件,传送当前步数作参数
this.fireEvent('onComplete', this.step + '');
}
},
//根据鼠标位置计算当前的步数
toStep: function(position) {
var step = (position + this.options.offset) * this.stepSize / this.full * this.steps;
return this.options.steps ? Math.round(step -= step % this.stepSize) : step;
},
//根据当前的步数计算滑块的位置
toPosition: function(step) {
return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) - this.options.offset;
}
});