htbzwd 发表于 2015-5-14 10:52:06

js 实现win7任务栏拖动效果

前言
  在某个时刻, 我认识了一个朋友.
  此人在我的教唆下, 踏上了js的不归路.
  前天他问我, Win7任务栏拖动效果怎么实现.
  我随口就跟他说, 这简单的一逼.
  在我一晚上的折腾之后, 一份潦草的代码总算实现了功能.
  PS: 我是搞C++的, js略懂一二..
  
  

源码
  话不多说, 上源码.



1 //    常量
2 var CELL_WIDTH    = 100;
3 var CELL_HEIGHT = 50;
4
5 var Utils = {
6   pixelToInt: function(str)
7   {
8         return parseInt( str.replace("px", "") );
9   },
10   getTagLeft: function($tag)
11   {
12         return this.pixelToInt( $tag.css("left") );
13   },
14   getTagTop: function($tag)
15   {
16         return this.pixelToInt( $tag.css("top") );
17   },
18   getTagWidth: function($tag)
19   {
20         return this.pixelToInt( $tag.css("width") );
21   },
22   getTagHeight: function($tag)
23   {
24         return this.pixelToInt( $tag.css("height") );
25   },
26   setTagLeft: function($tag, x)
27   {
28         $tag.css("left", x + "px");
29   },
30   setTagTop: function($tag, y)
31   {
32         $tag.css("top", y + "px");
33   },
34   setTagWidth: function($tag, width)
35   {
36         $tag.css("width", width + "px");
37   },
38   setTagHeight: function($tag, height)
39   {
40         $tag.css("left", height + "px");
41   },
42   swapNode: function(ary, idx1, idx2)
43   {
44         var t = ary;
45         ary = ary;
46         ary = t;
47   }
48 };
49
50 function Taskbar()
51 {
52   this._cells = [];
53   this._frameTag = null;
54   this._cellWidth = 0;
55   this._cellHeight = 0;
56   this._selNode = null;
57   this._selIndex = -1;
58
59   this._swapQueue = [];
60   //    考虑优化.
61   this._offsetPoint = {"x": 0, "y": 0};
62 }
63
64 Taskbar.prototype.getTag = function()
65 {
66   return this._frameTag;
67 }
68
69 Taskbar.prototype.init = function(x, y, width, height, rgb)
70 {
71   this._frameTag = $("");
72   this.setPosition(x, y);
73   this.setContentSize(width, height);
74   this.setBackgroundColor(rgb);
75
76   var self = this;
77   this._frameTag.bind("mousedown", {"bar": self}, this.mouseDown);
78   this._frameTag.bind("mouseup", {"bar": self}, this.mouseUp);
79   this._frameTag.bind("mousemove", {"bar": self}, this.mouseMove);
80   // this._frameTag.bind("mouseout", {"bar": self}, this.mouseOut);
81 }
82
83 Taskbar.prototype.setPosition = function(x, y)
84 {
85   this._frameTag.css("position", "absolute");
86   this._frameTag.css("left", x + "px");
87   this._frameTag.css("top", y + "px");
88 }
89
90 Taskbar.prototype.setContentSize = function(width, height)
91 {
92   this._frameTag.css("width", width + "px");
93   this._frameTag.css("height", height + "px");
94 }
95
96 Taskbar.prototype.setBackgroundColor = function(rgb)
97 {
98   //    rgb => "rgb(0, 0, 0)".
99   this._frameTag.css("background", rgb);
100 }
101
102 Taskbar.prototype.appendNode = function($node)
103 {
104   var frameWidth = Utils.getTagWidth( this._frameTag );
105   var frameHeight = Utils.getTagHeight( this._frameTag );
106   var length = this._cells.length + 1;
107   this._cellWidth = frameWidth / length;
108   this._cellHeight = frameHeight;
109   this._cells.push($node);
110   $node.appendTo( this._frameTag );
111
112   for ( var i = 0; i != length; ++i )
113   {
114         Utils.setTagLeft( this._cells, i * this._cellWidth );
115         Utils.setTagWidth( this._cells, this._cellWidth);
116   }   
117 }
118
119 Taskbar.prototype.mouseDown = function(e)
120 {
121   var bar = e.data["bar"];
122
123   if ( bar._selNode )
124   {
125         return ;
126   }
127
128   var index = bar.hitTest(e.clientX, e.clientY);
129   if ( !bar.isInvalidIndex(index) )
130   {
131         //    激活.
132         bar._selIndex = index;
133         bar._selNode = bar._cells[ index ];
134         bar._selNode.css("z-index", 99);
135         bar._cells[ index ] = null;
136
137         //    保存偏移量, 保持鼠标拖动.
138         var point = bar.converPoint(e.clientX, e.clientY);
139         bar._offsetPoint.x = point.x - index * bar._cellWidth;
140         bar._offsetPoint.y = point.y;
141         console.log("down");
142   }
143
144 }
145
146 Taskbar.prototype.mouseUp = function(e)
147 {
148   var bar = e.data["bar"];
149
150   if ( bar._selNode )
151   {
152         //    加入交换.
153         bar.appendSwap(bar._selNode, bar._selIndex);
154
155         //    鼠标抬起后, 把选中的节点复位.
156         // bar._cells[ bar._selIndex ] = bar._selNode;
157         bar._cells[ bar._selIndex ].css("z-index", 1);
158         bar._selIndex = -1;
159         bar._selNode = null;
160         console.log("up");
161   }
162 }
163
164 Taskbar.prototype.mouseOut = function(e)
165 {
166   var bar = e.data["bar"];
167   bar.mouseUp(e);
168   console.log("mouseout");
169 }
170
171 Taskbar.prototype.mouseMove = function(e)
172 {
173   var bar = e.data["bar"];
174   if ( bar._selNode )
175   {
176         var point = bar.converPoint(e.clientX, e.clientY);
177         var moveX = point.x - bar._offsetPoint.x;
178
179         //    防止位置溢出.
180         bar.noOverflow( bar._selNode, moveX );
181
182         //    挤开旁边的 float block.
183         var curX = Utils.getTagLeft(bar._selNode),
184             width = Utils.getTagWidth(bar._selNode),
185             testX = curX + width / 2,
186             hitIndex = bar.hitTest(testX, 0);
187         if ( bar._selIndex != hitIndex )
188         {
189             bar.appendSwap(bar._cells, bar._selIndex);
190             bar._selIndex = hitIndex;
191         }
192   }
193 }
194
195 Taskbar.prototype.appendSwap = function($node, index)
196 {
197   this._cells = $node;
198
199   this._swapQueue.push({"node": $node, "index": index});
200   this.resetNode();
201 }
202
203 Taskbar.prototype.noOverflow = function($node, moveX)
204 {
205   var width = Utils.getTagWidth( $node ),
206         frameWidth = Utils.getTagWidth( this._frameTag );
207
208   if (moveX < 0)
209         moveX = 0;
210   else if ( moveX + width > frameWidth )
211         moveX = frameWidth - width;
212
213   Utils.setTagLeft( $node, moveX );
214 }
215
216 Taskbar.prototype.resetNode = function()
217 {
218   var self = this;
219   var call = function($node, index)
220   {
221         var oldX = Utils.getTagLeft($node),
222             newX = index * self._cellWidth,
223             diff = newX - oldX,
224             stepCount = 10,
225             step = diff / stepCount,
226             curX = oldX;
227         (
228             function call()
229             {
230               if ( stepCount != 0 )
231               {
232                     curX += step;
233                     Utils.setTagLeft($node, curX);
234                     setTimeout(call, 10);
235               }
236               else
237               {
238                     $node.css("z-index", 0);
239                     Utils.setTagLeft($node, newX);
240               }
241               --stepCount;
242             }
243         )();
244   };
245
246   for (var i in this._swapQueue)
247   {
248         call(this._swapQueue.node, this._swapQueue.index);
249   }
250   this._swapQueue = [];
251 }
252
253 Taskbar.prototype.hitTest = function(x, y)
254 {
255   //    y参数完全是个酱油.
256   var point = this.converPoint(x, y);
257   return parseInt(point.x / this._cellWidth);
258 }
259
260 Taskbar.prototype.converPoint = function(x, y)
261 {
262   var frameX = Utils.getTagLeft( this._frameTag );
263         frameY = Utils.getTagTop( this._frameTag );
264   return {
265         "x": x -= frameX,
266         "y": y -= frameY
267   };
268 }
269
270 Taskbar.prototype.isInvalidIndex = function(index)
271 {
272   return index < 0 || index >= this._cells.length;
273 }
274
275 function init()
276 {
277   var getCell = function(cls, left, top, name)
278   {
279         return $(
280             ""
281             .replace("_cls", cls)
282             .replace("_left", left)
283             .replace("_top", top)
284             .replace("_name", name)
285             .replace("_width", CELL_WIDTH)
286             .replace("_height", CELL_HEIGHT) );
287   };
288
289      for (var i = 0; i != 5; ++i)
290      {
291          var taskbar = new Taskbar();
292          taskbar.init(0, i * 60, 500, 50, "rgb(0, 0, 0)");
293          taskbar.getTag().appendTo( $("body") );
294          for (var j = 0; j != i + 5; ++j)
295          {
296            taskbar.appendNode( getCell("cell", 0, 0, 0) );
297          }
298      }
299 }
300
301 $(document).ready(init);
  这个思路其实很简单.
  创建一个Taskbar对象, 这个对象设定好坐标, 尺寸, 背景色.
  随后往这个对象appendChild, 子节点会自动适配大小.
  
  我们用一个 作业队列 来保存需要移动的任务.
  这个队列保存需要被移动的节点, 和被动到哪个位置上.
  随后会触发一个交换的动作, 这个动作是持续性的, 因此可以看到节点平滑移动.
  
  在我们down下操作之后, 被down下的那个节点位置设置null.
  随后我们用一个 _selNode 保存这个节点.
  同时用 _selIndex 保存这个节点本应该属于的位置.(这句话很难形容, 但是我想不出怎么说!)
  随后在move操作下, 判断这个 _selNode 是否"越线", 一旦越线则push一个交换作业.
  随后就像上面所说, 触发交换动作.
  
  在up操作触发之后, 只需要把 _selNode和_selIndex push到作业队列即可.
  
  因为所有的mouse响应都在背景的div里, 因此鼠标超出范围则不会响应..
  例如, 我down之后, 鼠标移出范围再up, 此时的up将不会被响应..
  我在down中加了一条判断来解决此bug..
  在超出范围up, 回到范围之后, 依然是down状态.
  

html 文件内容









* {
margin: 0;
border: 0;
}
.cell {
position: absolute;
width: 100px;
height: 50px;
border: solid;
background: rgb(21, 98, 231);
}






  之前写的匆忙, 只把脚本部分贴了上来, 漏掉了html文件.
页: [1]
查看完整版本: js 实现win7任务栏拖动效果