We override the zk.Draggable#_updateDrag method to implement it,
var oldUpdateDrag = zk.Draggable.prototype._updateDrag;
zk.afterLoad("zk", function () {
zk.Draggable.prototype._updateDrag = function (pt, evt) {
// call original _updateDrag method
oldUpdateDrag.apply(this, arguments);
var control = this.control, // original listitem
node = this.node; // cloned listitem, the drag node
if (control.$instanceof(zul.sel.Listitem)) {
// get original listbox
var listbox = control.getListbox(),
dir;
// start scroll if has a direction,
// or clear the scroll timer
if (dir = shouldScroll(jq(node), jq(listbox)))
startScroll(dir, listbox);
else
clearScroll(listbox);
}
}
});
This is the major part, we call the original _updateDrag method at first, then do scroll as need.
// return the scroll direction if have to scroll,
// return null otherwise.
function shouldScroll ($node, $listbox) {
var top = $listbox.offset().top,
itemTop = $node.offset().top;
if (itemTop < top)
return 'up';
else {
var bottom = top + $listbox.height(),
itemBottom = itemTop + $node.height();
return itemBottom > bottom? 'down' : null;
}
}
This method check the position of drag node,
return:
'up' if the drag node is higher then listbox,
'down' if the drag node is lower then listbox,
null otherwise.
// create scroll timer with the specified direction
function startScroll (dir, listbox) {
if (!listbox._scrollStarted)
listbox._scrollStarted = setInterval(function () {
var body = listbox.$n('body'),
oldValue = body.scrollTop;
body.scrollTop += dir == 'down'? 20 : (-20);
// can not scroll any more
if (body.scrollTop == oldValue)
clearScroll(listbox);
}, 50);
}
This method start a scroll timer based on the direction,
the scroll timer will clear it self if can not scroll any more.
// clear scroll timer
function clearScroll(listbox) {
if (listbox._scrollStarted) {
clearInterval(listbox._scrollStarted);
listbox._scrollStarted = null;
}
}
This method clear the scroll timer.
The full zul is as below
<zk>
<script type="text/javascript"><![CDATA[
var oldUpdateDrag = zk.Draggable.prototype._updateDrag;
zk.afterLoad("zk", function () {
zk.Draggable.prototype._updateDrag = function (pt, evt) {
// call original _updateDrag method
oldUpdateDrag.apply(this, arguments);
var control = this.control, // original listitem
node = this.node; // cloned listitem, the drag node
if (control.$instanceof(zul.sel.Listitem)) {
// get original listbox
var listbox = control.getListbox(),
dir;
// start scroll if has a direction,
// or clear the scroll timer
if (dir = shouldScroll(jq(node), jq(listbox)))
startScroll(dir, listbox);
else
clearScroll(listbox);
}
}
});
// return the scroll direction if have to scroll,
// return null otherwise.
function shouldScroll ($node, $listbox) {
var top = $listbox.offset().top,
itemTop = $node.offset().top;
if (itemTop < top)
return 'up';
else {
var bottom = top + $listbox.height(),
itemBottom = itemTop + $node.height();
return itemBottom > bottom? 'down' : null;
}
}
// create scroll timer with the specified direction
function startScroll (dir, listbox) {
if (!listbox._scrollStarted)
listbox._scrollStarted = setInterval(function () {
var body = listbox.$n('body'),
oldValue = body.scrollTop;
body.scrollTop += dir == 'down'? 20 : (-20);
// can not scroll any more
if (body.scrollTop == oldValue)
clearScroll(listbox);
}, 50);
}
// clear scroll timer
function clearScroll(listbox) {
if (listbox._scrollStarted) {
clearInterval(listbox._scrollStarted);
listbox._scrollStarted = null;
}
}
]]></script>
<div height="15px" />
<listbox model="${model}" height="120px" width="200px" droppable="true">
<listitem draggable="true" droppable="true">
<listcell label="Item 1" />
</listitem>
<listitem draggable="true" droppable="true">
<listcell label="Item 2" />
</listitem>
<listitem draggable="true" droppable="true">
<listcell label="Item 3" />
</listitem>
<listitem draggable="true" droppable="true">
<listcell label="Item 4" />
</listitem>
<listitem draggable="true" droppable="true">
<listcell label="Item 5" />
</listitem>
<listitem draggable="true" droppable="true">
<listcell label="Item 6" />
</listitem>
<listitem draggable="true" droppable="true">
<listcell label="Item 7" />
</listitem>
<listitem draggable="true" droppable="true">
<listcell label="Item 8" />
</listitem>
<listitem draggable="true" droppable="true">
<listcell label="Item 9" />
</listitem>
<listitem draggable="true" droppable="true">
<listcell label="Item 10" />
</listitem>
<listitem draggable="true" droppable="true">
<listcell label="Item 11" />
</listitem>
<listitem draggable="true" droppable="true">
<listcell label="Item 12" />
</listitem>
<listitem draggable="true" droppable="true">
<listcell label="Item 13" />
</listitem>
<listitem draggable="true" droppable="true">
<listcell label="Item 14" />
</listitem>
<listitem draggable="true" droppable="true">
<listcell label="Item 15" />
</listitem>
</listbox>
</zk>
Download
The full resource with a demo flash can be downloaded from github
listbox_autoscroll_while_dragging.zul
and
listbox_autoscroll_while_dragging_se.zul
under WebContent at
https://github.com/benbai123/ZK_Practice/tree/master/Components/projects/Components_Practice/
listbox_autoscroll_while_dragging.swf is at
https://github.com/benbai123/ZK_Practice/tree/master/Components/demos
Hi! Great job!
ReplyDeleteI've testing your code. It's working when I use on single page (Tabbox). When I open the second Tab, I get javascript error: 'Too much recursion' on oldUpdateDrag.apply(this, arguments); line.
Thank you!
Forget about previous message, I got solution put the script in js file and add on main.zul. Problem solved! Thanks!
ReplyDeleteThat's great :)
Delete