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();
    }

二、生成控制面板

控制面板
  1. 面板:为了尽可能少的影响到拖拽项,就不直接在拖拽项上生成控制点,而是为拖拽项增加一个半透明层作为控制面板,控制面板尺寸位置和拖拽项一致,将控制面板插入拖拽项(由于都是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));
  1. 控制点:为控制面板增加东、南、西、北、东北、西北、西南、东南控制点,为每个控制点设置对应的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' //西南

三、缩放

  1. 每个控制点影响的属性不一样
    北(上):
        高度(height) 
        位置y(拖拽上边,应该保持下边固定,所以高度变化之后位置也要变化)
    南(下):
        高度(height)
    东(右):
        宽度(width)
    西(左):
        宽度(width)
        位置x(拖拽左边,应该保持右边固定,所以宽度变化之后位置也要变化)

    东北、西北、东南、西南  以上两两结合
  1. 监听控制点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;
    });
  1. 监听控制面板的mousedown,同样定义一个标志,用于拖动(drag)
  2. 监听舞台(stage)的mousemove(注意这里不是监听控制点的mousemove,因为控制点实在是太小,无法灵敏的拖动),判断各个控制点以及控制面板mousedown的标志分别对宽高和位置进行处理。处理的逻辑为:touchmove过程中的e.pageX/e.pageY - ox/oy(原始) = 偏移量,对于第一点中提到的每个控制点影响的属性,根据偏移量进行变化
监听
  1. 监听舞台(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内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<!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为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
(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
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
posted @   sweeeper  阅读(1171)  评论(1编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示