div拖拽缩放jquery插件编写——带8个控制点
项目中需要对div进行拖拽缩放,需要有控制面板8个控制点的那种,原以为这么常见的效果应该能搜索到很多相关插件,然而可以完成拖拽的实繁,却找不到我想要的,还是自己动手丰衣足食吧
效果预览(只支持pc端)
思路
一、舞台拖拽项定义
拖拽以及缩放需要有一个范围,姑且管这个叫舞台(stage)。另外,在舞台范围内,如何以一种简单的方式定义拖拽项?可以通过给div增加一个拖拽class(例如:resize-item),拖拽项应该是absolute布局,舞台是relative/absolute布局
所以拖拽类需要有如下两个参数
/**
* 默认参数
*/
var defaultOpts = {
stage: document, //舞台,默认为stage
itemClass: 'resize-item', //可缩放的类名
};
/**
* 定义类
*/
var ZResize = function(options) {
this.options = $.extend({}, defaultOpts, options);
this.init();
}
二、生成控制面板
- 面板:为了尽可能少的影响到拖拽项,就不直接在拖拽项上生成控制点,而是为拖拽项增加一个半透明层作为控制面板,控制面板尺寸位置和拖拽项一致,将控制面板插入拖拽项(由于都是absolute布局,不影响原内容),可以更好的控制位置和一一对应
var width = $(this).width();
var height = $(this).height();
var resizePanel = $('<div class"resize-panel"></div>');
resizePanel.css({
width: width,
height: height,
top: 0,
left: 0,
position: 'absolute',
'background-color': 'rgba(0,0,0,0.5)',
cursor: 'move',
display: 'none'
});
self.appendHandler(resizePanel, $(this));
- 控制点:为控制面板增加东、南、西、北、东北、西北、西南、东南控制点,为每个控制点设置对应的cursor
'cursor': 'n-resize' //北
'cursor': 's-resize' //南
'cursor': 'e-resize' //东
'cursor': 'w-resize' //西
'cursor': 'ne-resize' //东北
'cursor': 'nw-resize' //西北
'cursor': 'se-resize' //东南
'cursor': 'sw-resize' //西南
三、缩放
- 每个控制点影响的属性不一样
北(上):
高度(height)
位置y(拖拽上边,应该保持下边固定,所以高度变化之后位置也要变化)
南(下):
高度(height)
东(右):
宽度(width)
西(左):
宽度(width)
位置x(拖拽左边,应该保持右边固定,所以宽度变化之后位置也要变化)
东北、西北、东南、西南 以上两两结合
- 监听控制点mousedown,分别定义一个标志,并且记录下原始位置,如:
//西
var wmove = false;
el.on('mousedown','.w', function(e) {
ox = e.pageX;//原始x位置
ow = el.width();
oleft = parseInt(org.css('left').replace('px', ''));
wmove = true;
});
- 监听控制面板的mousedown,同样定义一个标志,用于拖动(drag)
- 监听舞台(stage)的mousemove(注意这里不是监听控制点的mousemove,因为控制点实在是太小,无法灵敏的拖动),判断各个控制点以及控制面板mousedown的标志分别对宽高和位置进行处理。处理的逻辑为:touchmove过程中的
e.pageX/e.pageY - ox/oy(原始) = 偏移量
,对于第一点中提到的每个控制点影响的属性,根据偏移量进行变化
- 监听舞台(stage)的mouseup事件(这里同样不能是控制点的mouseup),把所有的mousedown标志重置为false
pc端的mouse事件不同于手机端的touch事件,touchstart->touchmove->touchend触发有顺序性,mouse事件没有顺序性,未触发mousedown也能触发mousemove,所以需要设置标志来判断
四、控制面板的出现隐藏
控制面板初始化的时候,应该是隐藏的,只有点击对应的拖拽项的时候才出现,并且点击舞台空白部分,需要隐藏控制面板(组织事件冒泡)
/**
+ 点击item显示拖拽面板
*/
bindTrigger: function(el) {
var self = this;
el.on('click', function(e) {
//组织事件冒泡,不然会被舞台触发,舞台触发会把控制面板隐藏掉
e.stopPropagation();
self.triggerResize(el);
});
},
/**
+ 点击舞台空闲区域 隐藏缩放面板
*/
bindHidePanel: function(el) {
var stage = this.options.stage;
var itemClass = this.options.itemClass;
$(stage).bind('click', function() {
$('.' + itemClass).children('div').css({
display: 'none'
});
})
}
相应的HTML内容:
<!doctype html> <html> <head> <meta charset="utf-8"> <title>jQuery拖拽放大缩小插件idrag</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <style type="text/css"> .item1 { width: 405px; height: 291px; cursor: move; position: absolute; top: 30px; left: 30px; background-color: #FFF; border: 1px solid #CCCCCC; -webkit-box-shadow: 10px 10px 25px #ccc; -moz-box-shadow: 10px 10px 25px #ccc; box-shadow: 10px 10px 25px #ccc; } .item2 { width: 200px; height: 100px; cursor: move; position: absolute; top: 400px; left: 100px; background-color: #FFF; border: 1px solid #CCCCCC; -webkit-box-shadow: 10px 10px 25px #ccc; -moz-box-shadow: 10px 10px 25px #ccc; box-shadow: 10px 10px 25px #ccc; line-height: 100px; text-align: center; } body { background-color: #F3F3F3; } </style> </head> <body> <div class="resize-item item1"> <img src="images/dog.png" width="100%" height="100%"> </div> <div class="resize-item item2"> 你是我的小小狗 </div> <script src="js/jquery.min.js"></script> <script type="text/javascript" src='js/jquery.ZResize.js'></script> <script type="text/javascript"> new ZResize(); </script> </body> </html>
相应的js插件jquery.ZResize.js为:
(function($) { /** * 默认参数 */ var defaultOpts = { stage: document, //舞台 itemClass: 'resize-item', //可缩放的类名 }; /** * 定义类 */ var ZResize = function(options) { this.options = $.extend({}, defaultOpts, options); this.init(); } ZResize.prototype = { init: function() { this.initResizeBox(); }, /** * 初始化拖拽item */ initResizeBox: function() { var self = this; $('.' + self.options.itemClass).each(function() { //创建面板 var width = $(this).width(); var height = $(this).height(); var resizePanel = $('<div class"resize-panel"></div>'); resizePanel.css({ width: width, height: height, top: 0, left: 0, position: 'absolute', 'background-color': 'rgba(0,0,0,0.5)', cursor: 'move', display: 'none' }); self.appendHandler(resizePanel, $(this)); /** * 创建控制点 */ var n = $('<div class="n"></div>');//北 var s = $('<div class="s"></div>');//南 var w = $('<div class="w"></div>');//西 var e = $('<div class="e"></div>');//东 var ne = $('<div class="ne"></div>');//东北 var nw = $('<div class="nw"></div>');//西北 var se = $('<div class="se"></div>');//东南 var sw = $('<div class="sw"></div>');//西南 //添加公共样式 self.addHandlerCss([n, s, w, e, ne, nw, se, sw]); //添加各自样式 n.css({ 'top': '-4px', 'margin-left': '-4px', 'left': '50%', 'cursor': 'n-resize' }); s.css({ 'bottom': '-4px', 'margin-left': '-4px', 'left': '50%', 'cursor': 's-resize' }); e.css({ 'top': '50%', 'margin-top': '-4px', 'right': '-4px', 'cursor': 'e-resize' }); w.css({ 'top': '50%', 'margin-top': '-4px', 'left': '-4px', 'cursor': 'w-resize' }); ne.css({ 'top': '-4px', 'right': '-4px', 'cursor': 'ne-resize' }); nw.css({ top: '-4px', 'left': '-4px', 'cursor': 'nw-resize' }); se.css({ 'bottom': '-4px', 'right': '-4px', 'cursor': 'se-resize' }); sw.css({ 'bottom': '-4px', 'left': '-4px', 'cursor': 'sw-resize' }); // 添加项目 self.appendHandler([n, s, w, e, ne, nw, se, sw], resizePanel); //绑定拖拽缩放事件 self.bindResizeEvent(resizePanel, $(this)); //绑定触发事件 self.bindTrigger($(this)); }); self.bindHidePanel(); }, //控制点公共样式 addHandlerCss: function(els) { for(var i = 0; i < els.length; i++) { el = els[i]; el.css({ position: 'absolute', width: '8px', height: '8px', background: '#ff6600', margin: '0', 'border-radius': '2px', border: '1px solid #dd5500', }); } }, /** * 插入容器 */ appendHandler: function(handlers, target) { for(var i = 0; i < handlers.length; i++) { el = handlers[i]; target.append(el); } }, /** * 显示拖拽面板 */ triggerResize: function(el) { var self = this; el.siblings('.' + self.options.itemClass).children('div').css({ display: 'none' }); el.children('div').css({ display: 'block' }); }, /** * 拖拽事件控制 包含8个缩放点 和一个拖拽位置 */ bindResizeEvent: function(el) { var self = this; var ox = 0; //原始事件x位置 var oy = 0; //原始事件y位置 var ow = 0; //原始宽度 var oh = 0; //原始高度 var oleft = 0; //原始元素位置 var otop = 0; var org = el.parent('div'); //东 var emove = false; el.on('mousedown','.e', function(e) { ox = e.pageX;//原始x位置 ow = el.width(); emove = true; }); //南 var smove = false; el.on('mousedown','.s', function(e) { oy = e.pageY;//原始x位置 oh = el.height(); smove = true; }); //西 var wmove = false; el.on('mousedown','.w', function(e) { ox = e.pageX;//原始x位置 ow = el.width(); wmove = true; oleft = parseInt(org.css('left').replace('px', '')); }); //北 var nmove = false; el.on('mousedown','.n', function(e) { oy = e.pageY;//原始x位置 oh = el.height(); nmove = true; otop = parseInt(org.css('top').replace('px', '')); }); //东北 var nemove = false; el.on('mousedown','.ne', function(e) { ox = e.pageX;//原始x位置 oy = e.pageY; ow = el.width(); oh = el.height(); nemove = true; otop = parseInt(org.css('top').replace('px', '')); }); //西北 var nwmove = false; el.on('mousedown','.nw', function(e) { ox = e.pageX;//原始x位置 oy = e.pageY; ow = el.width(); oh = el.height(); otop = parseInt(org.css('top').replace('px', '')); oleft = parseInt(org.css('left').replace('px', '')); nwmove = true; }); //东南 var semove = false; el.on('mousedown','.se', function(e) { ox = e.pageX;//原始x位置 oy = e.pageY; ow = el.width(); oh = el.height(); semove = true; }); //西南 var swmove = false; el.on('mousedown','.sw', function(e) { ox = e.pageX;//原始x位置 oy = e.pageY; ow = el.width(); oh = el.height(); swmove = true; oleft = parseInt(org.css('left').replace('px', '')); }); //拖拽 var drag = false; el.on('mousedown', function(e) { ox = e.pageX;//原始x位置 oy = e.pageY; otop = parseInt(org.css('top').replace('px', '')); oleft = parseInt(org.css('left').replace('px', '')); drag = true; }); $(self.options.stage).on('mousemove', function(e) { if(emove) { var x = (e.pageX - ox); el.css({ width: ow + x }); org.css({ width: ow + x }); } else if(smove) { var y = (e.pageY - oy); el.css({ height: oh + y }); org.css({ height: oh + y }); } else if(wmove) { var x = (e.pageX - ox); el.css({ width: ow - x, // left: oleft + x }); org.css({ width: ow - x, left: oleft + x }); } else if(nmove) { var y = (e.pageY - oy); el.css({ height: oh - y, // top: otop + y }); org.css({ height: oh - y, top: otop + y }); } else if(nemove) { var x = e.pageX - ox; var y = e.pageY - oy; el.css({ height: oh - y, // top: otop + y, width: ow + x }); org.css({ height: oh - y, top: otop + y, width: ow + x }); } else if(nwmove) { var x = e.pageX - ox; var y = e.pageY - oy; el.css({ height: oh - y, // top: otop + y, width: ow - x, // left: oleft + x }); org.css({ height: oh - y, top: otop + y, width: ow - x, left: oleft + x }); } else if(semove) { var x = e.pageX - ox; var y = e.pageY - oy; el.css({ width: ow + x, height: oh + y }); org.css({ width: ow + x, height: oh + y }); } else if(swmove) { var x = e.pageX - ox; var y = e.pageY - oy; el.css({ width: ow - x, // left: oleft + x, height: oh + y }); org.css({ width: ow - x, left: oleft + x, height: oh + y }); } else if(drag) { var x = e.pageX - ox; var y = e.pageY - oy; org.css({ left: oleft + x, top: otop + y }); } }).on('mouseup', function(e) { emove = false; smove = false; wmove = false; nmove = false; nemove = false; nwmove = false; swmove = false; semove = false; drag = false; }); }, /** * 点击item显示拖拽面板 */ bindTrigger: function(el) { var self = this; el.on('click', function(e) { e.stopPropagation(); self.triggerResize(el); }); }, /** * 点击舞台空闲区域 隐藏缩放面板 */ bindHidePanel: function(el) { var stage = this.options.stage; var itemClass = this.options.itemClass; $(stage).bind('click', function() { $('.' + itemClass).children('div').css({ display: 'none' }); }) } } window.ZResize = ZResize; })(jQuery);
源码链接:https://github.com/zengwenfu/z-resize
作者:小虫巨蟹
链接:https://www.jianshu.com/p/822afede7489
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。