原帖地址:http://space.flash8.net/space/?uid-18713-action-viewspace-itemid-408445
原作者:我佛山人
代码
//
UI插件,拖动/缩放基类
var Drag = new Class({
// 继承自Events和Options,UI组件的基本要求:事件支持及属性可选
Implements: [Events, Options],
options: { /*
//开始拖动前事件
onBeforeStart: $empty,
//开始拖动事件
onStart: $empty,
//拖动事件
onDrag: $empty,
//取消拖动事件
onCancel: $empty,
//拖动完成事件
onComplete: $empty, */
// 自动吸附距离
snap: 6 ,
// 单位
unit: ' px ' ,
// 网格大小
grid: false ,
// 指定是否CSS属性
style: true ,
// 拖动范围限制
limit: false ,
// 可拖动的句柄
handle: false ,
// 反转
invert: false ,
// 鼠标拖动时对象改变的样式属性,当x,y为left和top时是移动,为width和height时为缩放
modifiers: {x: ' left ' , y: ' top ' }
},
// 构造函数
initialize: function () {
// Array.link对参数处理,实现配置属性与作用对象的位置无关性
var params = Array.link(arguments, { ' options ' : Object.type, ' element ' : $defined});
// 拖动作用的对象
this .element = $(params.element);
// 拖动作用对象所属的文档对象
this .document = this .element.getDocument();
// 调用Options类的方法,实现可选参数
this .setOptions(params.options || {});
// 句柄参数的类型
var htype = $type( this .options.handle);
// 触发拖动的句柄,可支持多个对象,也可为单个,当不提供handle参数时使用拖动作用的对象
this .handles = (htype == ' array ' || htype == ' collection ' ) ? $$( this .options.handle) : $( this .options.handle) || this .element;
// 鼠标的相关值集合
this .mouse = { ' now ' : {}, ' pos ' : {}};
// 对象的坐标值集合
this .value = { ' start ' : {}, ' now ' : {}};
// 选取事件名,ie内核浏览器使用onselectstart
this .selection = (Browser.Engine.trident) ? ' selectstart ' : ' mousedown ' ;
// 使用闭包绑定当前对象的事件方法集
this .bound = {
// 开始
start: this .start.bind( this ),
// 检查
check: this .check.bind( this ),
// 拖动
drag: this .drag.bind( this ),
// 拖动
stop: this .stop.bind( this ),
// 取消
cancel: this .cancel.bind( this ),
// 停止事件冒泡,使用$lambda快速构造出functon(){return false;}这样的函数
eventStop: $lambda( false )
};
// 附加事件
this .attach();
},
// 附加事件
attach: function () {
this .handles.addEvent( ' mousedown ' , this .bound.start);
return this ;
},
// 移除事件
detach: function () {
// 为句柄对象移除鼠标按下事件,之所以要提前绑定并保存方法,是为了事件的可移除性
// 如果addevent和removeEvent中都使用this.start.bind(this),移除事件将失败
this .handles.removeEvent( ' mousedown ' , this .bound.start);
return this ;
},
// 开始拖动处理
start: function (event) {
// 触发拖动前事件
this .fireEvent( ' onBeforeStart ' , this .element);
// 鼠标的初始位置信息
this .mouse.start = event.page;
// 范围限制
var limit = this .options.limit;
this .limit = { ' x ' : [], ' y ' : []};
// 遍历作用的属性名
for ( var z in this .options.modifiers) {
// 如果值为false,忽略
if ( ! this .options.modifiers[z]) continue ;
// 如果指定使用样式属性,从样式中取对应属性值
if ( this .options.style) this .value.now[z] = this .element.getStyle( this .options.modifiers[z]).toInt();
// 否则从属性中取值
else this .value.now[z] = this .element[ this .options.modifiers[z]];
// 如果指定反转,取负值(正取负,负取正)
if ( this .options.invert) this .value.now[z] *= - 1 ;
// 计算鼠标相对位置
this .mouse.pos[z] = event.page[z] - this .value.now[z];
// 如果指定相应方向上的范围限制
if (limit && limit[z]) {
// 因为范围的限制是一个两项的数组,指定上限及下限,所以循环2次(为什么不直接用(2).times?)
for ( var i = 2 ; i -- ; i) {
// 如果有定义,取值,因为是每次重新求值,并且使用了$lambda,所以可以使用函数返回值
// 比如limit :{x : false, y : [function(){return 1}, function(){return 10}]}
// 当然,你的函数里可以做更复杂的处理
if ($chk(limit[z][i])) this .limit[z][i] = $lambda(limit[z][i])();
}
}
}
// 网格方式的移动/缩放
if ($type( this .options.grid) == ' number ' ) this .options.grid = { ' x ' : this .options.grid, ' y ' : this .options.grid};
// 监听当前文档的鼠标移动和弹起事件
this .document.addEvents({mousemove: this .bound.check, mouseup: this .bound.cancel});
// 屏蔽当前文档的选择
this .document.addEvent( this .selection, this .bound.eventStop);
},
// 检查条件约束
check: function (event) {
// 计算当前鼠标移动的距离
var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this .mouse.start.x, 2 ) + Math.pow(event.page.y - this .mouse.start.y, 2 )));
// 如果超出吸附距离
if (distance > this .options.snap) {
// 移除拖动前检查约束的事件监听
this .cancel();
// 添加拖动处理相关的事件监听
this .document.addEvents({
mousemove: this .bound.drag,
mouseup: this .bound.stop
});
// 触发onStart和onSnap事件
this .fireEvent( ' onStart ' , this .element).fireEvent( ' onSnap ' , this .element);
}
},
// 拖动
drag: function (event) {
// 当前鼠标坐标
this .mouse.now = event.page;
// 遍历拖动作用的属性
for ( var z in this .options.modifiers) {
// 如果值为false,忽略该属性
if ( ! this .options.modifiers[z]) continue ;
// 计算当前值
this .value.now[z] = this .mouse.now[z] - this .mouse.pos[z];
// 反转值
if ( this .options.invert) this .value.now[z] *= - 1 ;
// 范围限制处理
if ( this .options.limit && this .limit[z]) {
// 如果当前方向上的值大于上限
if ($chk( this .limit[z][ 1 ]) && ( this .value.now[z] > this .limit[z][ 1 ])) {
// 取上限值
this .value.now[z] = this .limit[z][ 1 ];
// 如果当前值小于下限
} else if ($chk( this .limit[z][ 0 ]) && ( this .value.now[z] < this .limit[z][ 0 ])) {
// 取下限值
this .value.now[z] = this .limit[z][ 0 ];
}
}
// 相对于网格大小取值
if ( this .options.grid[z]) this .value.now[z] -= ( this .value.now[z] % this .options.grid[z]);
// 根据指定样式还是属性的值设置
if ( this .options.style) this .element.setStyle( this .options.modifiers[z], this .value.now[z] + this .options.unit);
else this .element[ this .options.modifiers[z]] = this .value.now[z];
}
// 触发onDrag事件
this .fireEvent( ' onDrag ' , this .element);
},
// 取消
cancel: function (event) {
// 移除事件监听
this .document.removeEvent( ' mousemove ' , this .bound.check);
this .document.removeEvent( ' mouseup ' , this .bound.cancel);
if (event) {
this .document.removeEvent( this .selection, this .bound.eventStop);
// 触发onCancel事件
this .fireEvent( ' onCancel ' , this .element);
}
},
// 停止,打扫战场
stop: function (event) {
// 移除各事件
this .document.removeEvent( this .selection, this .bound.eventStop);
this .document.removeEvent( ' mousemove ' , this .bound.drag);
this .document.removeEvent( ' mouseup ' , this .bound.stop);
// 触发onComplete事件
if (event) this .fireEvent( ' onComplete ' , this .element);
}
});
// 根据Drag为Element扩展功能
Element.implement({
// 使当前对象可缩放
makeResizable: function (options) {
return new Drag( this , $merge({modifiers: { ' x ' : ' width ' , ' y ' : ' height ' }}, options));
}
});
var Drag = new Class({
// 继承自Events和Options,UI组件的基本要求:事件支持及属性可选
Implements: [Events, Options],
options: { /*
//开始拖动前事件
onBeforeStart: $empty,
//开始拖动事件
onStart: $empty,
//拖动事件
onDrag: $empty,
//取消拖动事件
onCancel: $empty,
//拖动完成事件
onComplete: $empty, */
// 自动吸附距离
snap: 6 ,
// 单位
unit: ' px ' ,
// 网格大小
grid: false ,
// 指定是否CSS属性
style: true ,
// 拖动范围限制
limit: false ,
// 可拖动的句柄
handle: false ,
// 反转
invert: false ,
// 鼠标拖动时对象改变的样式属性,当x,y为left和top时是移动,为width和height时为缩放
modifiers: {x: ' left ' , y: ' top ' }
},
// 构造函数
initialize: function () {
// Array.link对参数处理,实现配置属性与作用对象的位置无关性
var params = Array.link(arguments, { ' options ' : Object.type, ' element ' : $defined});
// 拖动作用的对象
this .element = $(params.element);
// 拖动作用对象所属的文档对象
this .document = this .element.getDocument();
// 调用Options类的方法,实现可选参数
this .setOptions(params.options || {});
// 句柄参数的类型
var htype = $type( this .options.handle);
// 触发拖动的句柄,可支持多个对象,也可为单个,当不提供handle参数时使用拖动作用的对象
this .handles = (htype == ' array ' || htype == ' collection ' ) ? $$( this .options.handle) : $( this .options.handle) || this .element;
// 鼠标的相关值集合
this .mouse = { ' now ' : {}, ' pos ' : {}};
// 对象的坐标值集合
this .value = { ' start ' : {}, ' now ' : {}};
// 选取事件名,ie内核浏览器使用onselectstart
this .selection = (Browser.Engine.trident) ? ' selectstart ' : ' mousedown ' ;
// 使用闭包绑定当前对象的事件方法集
this .bound = {
// 开始
start: this .start.bind( this ),
// 检查
check: this .check.bind( this ),
// 拖动
drag: this .drag.bind( this ),
// 拖动
stop: this .stop.bind( this ),
// 取消
cancel: this .cancel.bind( this ),
// 停止事件冒泡,使用$lambda快速构造出functon(){return false;}这样的函数
eventStop: $lambda( false )
};
// 附加事件
this .attach();
},
// 附加事件
attach: function () {
this .handles.addEvent( ' mousedown ' , this .bound.start);
return this ;
},
// 移除事件
detach: function () {
// 为句柄对象移除鼠标按下事件,之所以要提前绑定并保存方法,是为了事件的可移除性
// 如果addevent和removeEvent中都使用this.start.bind(this),移除事件将失败
this .handles.removeEvent( ' mousedown ' , this .bound.start);
return this ;
},
// 开始拖动处理
start: function (event) {
// 触发拖动前事件
this .fireEvent( ' onBeforeStart ' , this .element);
// 鼠标的初始位置信息
this .mouse.start = event.page;
// 范围限制
var limit = this .options.limit;
this .limit = { ' x ' : [], ' y ' : []};
// 遍历作用的属性名
for ( var z in this .options.modifiers) {
// 如果值为false,忽略
if ( ! this .options.modifiers[z]) continue ;
// 如果指定使用样式属性,从样式中取对应属性值
if ( this .options.style) this .value.now[z] = this .element.getStyle( this .options.modifiers[z]).toInt();
// 否则从属性中取值
else this .value.now[z] = this .element[ this .options.modifiers[z]];
// 如果指定反转,取负值(正取负,负取正)
if ( this .options.invert) this .value.now[z] *= - 1 ;
// 计算鼠标相对位置
this .mouse.pos[z] = event.page[z] - this .value.now[z];
// 如果指定相应方向上的范围限制
if (limit && limit[z]) {
// 因为范围的限制是一个两项的数组,指定上限及下限,所以循环2次(为什么不直接用(2).times?)
for ( var i = 2 ; i -- ; i) {
// 如果有定义,取值,因为是每次重新求值,并且使用了$lambda,所以可以使用函数返回值
// 比如limit :{x : false, y : [function(){return 1}, function(){return 10}]}
// 当然,你的函数里可以做更复杂的处理
if ($chk(limit[z][i])) this .limit[z][i] = $lambda(limit[z][i])();
}
}
}
// 网格方式的移动/缩放
if ($type( this .options.grid) == ' number ' ) this .options.grid = { ' x ' : this .options.grid, ' y ' : this .options.grid};
// 监听当前文档的鼠标移动和弹起事件
this .document.addEvents({mousemove: this .bound.check, mouseup: this .bound.cancel});
// 屏蔽当前文档的选择
this .document.addEvent( this .selection, this .bound.eventStop);
},
// 检查条件约束
check: function (event) {
// 计算当前鼠标移动的距离
var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this .mouse.start.x, 2 ) + Math.pow(event.page.y - this .mouse.start.y, 2 )));
// 如果超出吸附距离
if (distance > this .options.snap) {
// 移除拖动前检查约束的事件监听
this .cancel();
// 添加拖动处理相关的事件监听
this .document.addEvents({
mousemove: this .bound.drag,
mouseup: this .bound.stop
});
// 触发onStart和onSnap事件
this .fireEvent( ' onStart ' , this .element).fireEvent( ' onSnap ' , this .element);
}
},
// 拖动
drag: function (event) {
// 当前鼠标坐标
this .mouse.now = event.page;
// 遍历拖动作用的属性
for ( var z in this .options.modifiers) {
// 如果值为false,忽略该属性
if ( ! this .options.modifiers[z]) continue ;
// 计算当前值
this .value.now[z] = this .mouse.now[z] - this .mouse.pos[z];
// 反转值
if ( this .options.invert) this .value.now[z] *= - 1 ;
// 范围限制处理
if ( this .options.limit && this .limit[z]) {
// 如果当前方向上的值大于上限
if ($chk( this .limit[z][ 1 ]) && ( this .value.now[z] > this .limit[z][ 1 ])) {
// 取上限值
this .value.now[z] = this .limit[z][ 1 ];
// 如果当前值小于下限
} else if ($chk( this .limit[z][ 0 ]) && ( this .value.now[z] < this .limit[z][ 0 ])) {
// 取下限值
this .value.now[z] = this .limit[z][ 0 ];
}
}
// 相对于网格大小取值
if ( this .options.grid[z]) this .value.now[z] -= ( this .value.now[z] % this .options.grid[z]);
// 根据指定样式还是属性的值设置
if ( this .options.style) this .element.setStyle( this .options.modifiers[z], this .value.now[z] + this .options.unit);
else this .element[ this .options.modifiers[z]] = this .value.now[z];
}
// 触发onDrag事件
this .fireEvent( ' onDrag ' , this .element);
},
// 取消
cancel: function (event) {
// 移除事件监听
this .document.removeEvent( ' mousemove ' , this .bound.check);
this .document.removeEvent( ' mouseup ' , this .bound.cancel);
if (event) {
this .document.removeEvent( this .selection, this .bound.eventStop);
// 触发onCancel事件
this .fireEvent( ' onCancel ' , this .element);
}
},
// 停止,打扫战场
stop: function (event) {
// 移除各事件
this .document.removeEvent( this .selection, this .bound.eventStop);
this .document.removeEvent( ' mousemove ' , this .bound.drag);
this .document.removeEvent( ' mouseup ' , this .bound.stop);
// 触发onComplete事件
if (event) this .fireEvent( ' onComplete ' , this .element);
}
});
// 根据Drag为Element扩展功能
Element.implement({
// 使当前对象可缩放
makeResizable: function (options) {
return new Drag( this , $merge({modifiers: { ' x ' : ' width ' , ' y ' : ' height ' }}, options));
}
});