1 /** 2 * @fileOverview auto scroll for drag object's container 3 * @author yiminghe@gmail.com 4 */ 5 KISSY.add("dd/scroll", function (S, DDM, Base, Node, DOM) { 6 7 var TAG_DRAG = "__dd-scroll-id-", 8 win = S.Env.host, 9 stamp = S.stamp, 10 RATE = [10, 10], 11 ADJUST_DELAY = 100, 12 DIFF = [20, 20], 13 DESTRUCTORS = "__dd_scrolls"; 14 15 /** 16 * @name Scroll 17 * @memberOf DD 18 * @class 19 * make parent node scroll while dragging 20 */ 21 function Scroll() { 22 var self = this; 23 Scroll.superclass.constructor.apply(self, arguments); 24 self[DESTRUCTORS] = {}; 25 } 26 27 Scroll.ATTRS = 28 /** 29 * @lends DD.Scroll# 30 */ 31 { 32 /** 33 * node to be scrolled while dragging 34 * @type {window|String|HTMLElement} 35 */ 36 node:{ 37 // value:window:不行,默认值一定是简单对象 38 valueFn:function () { 39 return Node.one(win); 40 }, 41 setter:function (v) { 42 return Node.one(v); 43 } 44 }, 45 /** 46 * adjust velocity. default:[10,10]. larger faster 47 * @type Number[] 48 */ 49 rate:{ 50 value:RATE 51 }, 52 /** 53 * the margin to make node scroll. default: [20,20]. 54 * easier to scroll for node if larger. 55 * @type number[] 56 */ 57 diff:{ 58 value:DIFF 59 } 60 }; 61 62 63 var isWin = S.isWindow; 64 65 S.extend(Scroll, Base, 66 /** 67 * @lends DD.Scroll# 68 */ 69 { 70 /** 71 * @private 72 * @param node 73 */ 74 getRegion:function (node) { 75 if (isWin(node[0])) { 76 return { 77 width:DOM.viewportWidth(), 78 height:DOM.viewportHeight() 79 }; 80 } else { 81 return { 82 width:node.outerWidth(), 83 height:node.outerHeight() 84 }; 85 } 86 }, 87 88 /** 89 * @private 90 * @param node 91 */ 92 getOffset:function (node) { 93 if (isWin(node[0])) { 94 return { 95 left:DOM.scrollLeft(), 96 top:DOM.scrollTop() 97 }; 98 } else { 99 return node.offset(); 100 } 101 }, 102 103 /** 104 * @private 105 * @param node 106 */ 107 getScroll:function (node) { 108 return { 109 left:node.scrollLeft(), 110 top:node.scrollTop() 111 }; 112 }, 113 114 /** 115 * @private 116 * @param node 117 */ 118 setScroll:function (node, r) { 119 node.scrollLeft(r.left); 120 node.scrollTop(r.top); 121 }, 122 123 /** 124 * make node not to scroll while this drag object is dragging 125 * @param {DD.Draggable} drag 126 */ 127 detachDrag:function (drag) { 128 var tag, 129 destructors = this[DESTRUCTORS]; 130 if (!(tag = stamp(drag, 1, TAG_DRAG)) || 131 !destructors[tag] 132 ) { 133 return; 134 } 135 destructors[tag].fn(); 136 delete destructors[tag]; 137 }, 138 139 /** 140 * make node not to scroll at all 141 */ 142 destroy:function () { 143 var self = this, 144 destructors = self[DESTRUCTORS]; 145 for (var d in destructors) { 146 self.detachDrag(destructors[d].drag); 147 } 148 }, 149 150 /** 151 * make node to scroll while this drag object is dragging 152 * @param {DD.Draggable} drag 153 */ 154 attachDrag:function (drag) { 155 var self = this, 156 node = self.get("node"), 157 tag = stamp(drag, 0, TAG_DRAG), 158 destructors = self[DESTRUCTORS]; 159 160 if (destructors[tag]) { 161 return; 162 } 163 164 var rate = self.get("rate"), 165 diff = self.get('diff'), 166 event, 167 /* 168 目前相对 container 的偏移,container 为 window 时,相对于 viewport 169 */ 170 dxy, 171 timer = null; 172 173 // fix https://github.com/kissyteam/kissy/issues/115 174 // dragDelegate 时 可能一个 dragDelegate对应多个 scroll 175 // check container 176 function checkContainer() { 177 if (isWin(node[0])) { 178 return 0; 179 } 180 // 判断 proxyNode,不对 dragNode 做大的改变 181 var mousePos = drag.mousePos, 182 r = DDM.region(node); 183 184 if (!DDM.inRegion(r, mousePos)) { 185 clearTimeout(timer); 186 timer = 0; 187 return 1; 188 } 189 return 0; 190 } 191 192 function dragging(ev) { 193 // 给调用者的事件,框架不需要处理 194 // fake 也表示该事件不是因为 mouseover 产生的 195 if (ev.fake) { 196 return; 197 } 198 199 if (checkContainer()) { 200 return; 201 } 202 203 // 更新当前鼠标相对于拖节点的相对位置 204 event = ev; 205 dxy = S.clone(drag.mousePos); 206 var offset = self.getOffset(node); 207 dxy.left -= offset.left; 208 dxy.top -= offset.top; 209 if (!timer) { 210 checkAndScroll(); 211 } 212 } 213 214 function dragEnd() { 215 clearTimeout(timer); 216 timer = null; 217 } 218 219 drag.on("drag", dragging); 220 221 drag.on("dragstart", function () { 222 DDM.cacheWH(node); 223 }); 224 225 drag.on("dragend", dragEnd); 226 227 destructors[tag] = { 228 drag:drag, 229 fn:function () { 230 drag.detach("drag", dragging); 231 drag.detach("dragend", dragEnd); 232 } 233 }; 234 235 function checkAndScroll() { 236 if (checkContainer()) { 237 return; 238 } 239 240 var r = self.getRegion(node), 241 nw = r.width, 242 nh = r.height, 243 scroll = self.getScroll(node), 244 origin = S.clone(scroll), 245 diffY = dxy.top - nh, 246 adjust = false; 247 248 if (diffY >= -diff[1]) { 249 scroll.top += rate[1]; 250 adjust = true; 251 } 252 253 var diffY2 = dxy.top; 254 255 if (diffY2 <= diff[1]) { 256 scroll.top -= rate[1]; 257 adjust = true; 258 } 259 260 var diffX = dxy.left - nw; 261 262 if (diffX >= -diff[0]) { 263 scroll.left += rate[0]; 264 adjust = true; 265 } 266 267 var diffX2 = dxy.left; 268 269 if (diffX2 <= diff[0]) { 270 scroll.left -= rate[0]; 271 adjust = true; 272 } 273 274 if (adjust) { 275 self.setScroll(node, scroll); 276 timer = setTimeout(checkAndScroll, ADJUST_DELAY); 277 // 不希望更新相对值,特别对于相对 window 时,相对值如果不真正拖放触发的 drag,是不变的, 278 // 不会因为程序 scroll 而改变相对值 279 280 // 调整事件,不需要 scroll 监控,达到预期结果:元素随容器的持续不断滚动而自动调整位置. 281 event.fake = true; 282 if (isWin(node[0])) { 283 // 当使 window 自动滚动时,也要使得拖放物体相对文档位置随 scroll 改变 284 // 而相对 node 容器时,只需 node 容器滚动,拖动物体相对文档位置不需要改变 285 scroll = self.getScroll(node); 286 event.left += scroll.left - origin.left; 287 event.top += scroll.top - origin.top; 288 } 289 // 容器滚动了,元素也要重新设置 left,top 290 if (drag.get("move")) { 291 drag.get("node").offset(event); 292 } 293 drag.fire("drag", event); 294 } else { 295 timer = null; 296 } 297 } 298 299 } 300 }); 301 302 // for compatibility 303 var ScrollPrototype = Scroll.prototype; 304 ScrollPrototype.attach = ScrollPrototype.attachDrag; 305 ScrollPrototype.unAttach = ScrollPrototype.detachDrag; 306 return Scroll; 307 }, { 308 requires:['./ddm', 'base', 'node', 'dom'] 309 });