1 --
2 -- 滑动惯性.
3 --
4 Control.SlideInertia = Control.SlideInertia or class("Control.SlideInertia", function()
5 return cc.Node:create();
6 end );
7
8 function Control.SlideInertia:init()
9 self.schId = 0;
10 end
11
12 function Control.SlideInertia:destroy()
13 self.schId = Utils:unSchedule(self.schId);
14 end
15
16 function Control.SlideInertia:setMark(x, y)
17 self.time = os.time();
18 self.mark = cc.p(x, y);
19 self.schId = Utils:unSchedule(self.schId);
20 end
21
22 function Control.SlideInertia:run(x, y, frameCallFunc, endCallFunc)
23 frameCallFunc = frameCallFunc or function() cclog("default frameCallFunc") end;
24 endCallFunc = endCallFunc or function() cclog("default endCallFunc") end;
25
26 local curTime = os.time();
27 local diffTime = math.max(curTime - self.time, 1);
28 local stepX = (x - self.mark.x) / diffTime;
29 local stepY = (y - self.mark.y) / diffTime;
30 local function call()
31 frameCallFunc(stepX, stepY);
32 stepX = stepX * 0.8;
33 stepY = stepY * 0.8;
34 if math.abs(stepX) < 1 and math.abs(stepY) < 1 then
35 endCallFunc();
36 self.schId = Utils:unSchedule(self.schId);
37 end
38 end
39 if math.abs(stepX) > 0 or math.abs(stepY) > 0 then
40 self.schId = Utils:unSchedule(self.schId);
41 self.schId = Utils:runSchedule(call, 0.01);
42 end
43 end
44
45
46 --
47 -- 滚动层.
48 --
49 Control.ScrollView = Control.ScrollView or class("Control.ScrollView", function()
50 return cc.Node:create();
51 end );
52
53 --[[
54 params = {
55 ["count"] = 0,
56 ["cellWidth"] = 0,
57 ["cellHeight"] = 0,
58 ["triggerLen"] = 0,
59 ["scaleXY"] = 0,
60 ["maxOffsetY"] = 0,
61 ["newCallFunc"] = nil, => newCallFunc(i)
62 ["selCallFunc"] = nil, => selCallFunc(cell)
63 ["enterCallFunc"] = nil, => enterCallFunc(cell)
64 ["leaveCallFunc"] = nil, => leaveCallFunc(cell)
65 }
66 ]]
67
68 function Control.ScrollView:init(params)
69 local count = params["count"];
70 local cellWidth = params["cellWidth"];
71 local cellHeight = params["cellHeight"];
72 local triggerLen = params["triggerLen"];
73 local scaleXY = params["scaleXY"];
74 local maxOffsetY = params["maxOffsetY"] or 0;
75 local newCallFunc = params["newCallFunc"];
76 local selCallFunc = params["selCallFunc"] or function(cell) cclog("sel cell: %s", cell); end;
77 local enterCallFunc = params["enterCallFunc"] or function(cell) cclog("enter cell: %s", cell); end;
78 local leaveCallFunc = params["leaveCallFunc"] or function(cell) cclog("leave cell: %s", cell); end;
79 local selCell = nil;
80 local curCell = nil;
81 local triggerScale = triggerLen / cellWidth;
82 local downPosX;
83 local downPosY;
84 local cellScale = 1;
85 local elements = {};
86 local nodePosX = triggerLen * 0.5 + cellWidth * 0.5;
87 local isTouch = false;
88 local isLoadEnd = false;
89 local skipFrame = math.floor(count / 50) + 1;
90 local skipCount = 0;
91 local viewSize = cc.Director:getInstance():getVisibleSize();
92
93 local function in2Value(value, minValue, maxValue)
94 return value >= minValue and value <= maxValue;
95 end
96
97 -- 移动所有节点.
98 local function moveCells(offsetX)
99 nodePosX = nodePosX + offsetX;
100 for i = 1, count do
101 local cell = elements[i];
102 if math.abs(nodePosX + i * cellWidth) < viewSize.width + cellWidth / 2 then
103 cell:setVisible(true);
104 -- 移动.
105 (function()
106 local curPosX = nodePosX + (i - 1) * cellWidth;
107 local movetoX = curPosX;
108 local movetoY = 0;
109 local maxX = triggerLen / 2;
110 local minX = maxX - cellWidth;
111 if curPosX <= maxX and curPosX >= minX then
112 if not cell.isEnter then
113 leaveCallFunc(selCell);
114 selCell.isEnter = false;
115 selCell = cell;
116 selCell.isEnter = true;
117 enterCallFunc(cell);
118 end
119 movetoX = curPosX + (curPosX - maxX) * (triggerScale - 1);
120 local centerOffsetY = (maxX - cellWidth / 2);
121 local offsetWidth = (maxX - minX) / 2;
122 movetoY = (1 - math.abs(curPosX - centerOffsetY) / offsetWidth) * maxOffsetY;
123 else
124 movetoX = curPosX + (curPosX < minX and -cellWidth * (triggerScale - 1) or 0);
125 end
126 cell:setPositionX(movetoX);
127 cell:setPositionY(movetoY);
128 end)();
129 -- 缩放.
130 (function()
131 local distance = math.abs(cell:getPositionX());
132 local scale = 0;
133 local maxDistance = triggerLen * 0.5;
134 if distance <= maxDistance then
135 scale = (1 - distance / maxDistance) * (scaleXY - cellScale);
136 end
137 cell:setScale(cellScale + scale);
138 end)();
139 else
140 cell:setVisible(false);
141 end
142 end
143 end
144
145 -- 调整位置.
146 local function adjustPosition()
147 local function call()
148 local cell = curCell and curCell or selCell;
149 local pointX = cell:getPositionX();
150 local step = -pointX * 0.1;
151 if math.abs(step) < 0.1 then
152 curCell = nil;
153 selCallFunc(cell);
154 self.schSlideId = Utils:unSchedule(self.schSlideId);
155 end
156 moveCells(step);
157 end
158 self.schSlideId = Utils:unSchedule(self.schSlideId);
159 self.schSlideId = Utils:runSchedule(call, 0.01);
160 end
161
162 local function isTouchRect(touchPoint)
163 local worldPoint = self:convertToWorldSpace(cc.p(0, 0));
164 return in2Value(touchPoint.y, worldPoint.y, worldPoint.y + cellHeight);
165 end
166
167 local function onTouchUp(touch)
168 local function frameCallFunc(stepX, stepY)
169 moveCells(stepX);
170 end
171 local function endCallFunc()
172 adjustPosition();
173 end
174 local touchPoint = touch:getLocation();
175 self.slide:run(touchPoint.x, touchPoint.y, frameCallFunc, endCallFunc);
176 end
177
178 local function onTouchBegan(touch, event)
179 skipCount = 0;
180 local touchPoint = touch:getLocation();
181 local result = isTouchRect(touchPoint);
182 curCell = nil;
183 downPosX = touchPoint.x;
184 downPosY = touchPoint.y;
185 if result then
186 self.slide:setMark(downPosX, downPosY);
187 self.schSlideId = Utils:unSchedule(self.schSlideId);
188 end
189 return result;
190 end
191
192 local function onTouchMoved(touch, event)
193 if skipCount % skipFrame == 0 then
194 local touchPoint = touch:getLocation();
195
196 -- 调整拖动灵敏度.
197 if math.abs(downPosX - touchPoint.x) > 5 then
198 isTouch = false;
199 end
200
201 self.slide:setMark(downPosX, downPosY);
202 moveCells(touchPoint.x - downPosX);
203 downPosX = touchPoint.x;
204 downPosY = touchPoint.y;
205 end
206 skipCount = skipCount + 1;
207 end
208
209 local function onTouchEnded(touch, event)
210 onTouchUp(touch);
211 end
212
213 local function onTouchCancelled(touch, event)
214 onTouchUp(touch);
215 end
216
217 local function setCurCell(cell)
218 if isTouch == true then
219 curCell = cell;
220 adjustPosition();
221 end
222 end
223
224 local function loadEnd()
225 isLoadEnd = true;
226
227 self.schLoadId = Utils:unSchedule(self.schLoadId);
228
229 -- touch 响应层.
230 local touchLayer = cc.Layer:create();
231 local listener = cc.EventListenerTouchOneByOne:create();
232 listener:registerScriptHandler( onTouchBegan, cc.Handler.EVENT_TOUCH_BEGAN );
233 listener:registerScriptHandler( onTouchMoved, cc.Handler.EVENT_TOUCH_MOVED );
234 listener:registerScriptHandler( onTouchEnded, cc.Handler.EVENT_TOUCH_ENDED );
235 listener:registerScriptHandler( onTouchCancelled, cc.Handler.EVENT_TOUCH_CANCELLED );
236 touchLayer:getEventDispatcher():addEventListenerWithSceneGraphPriority(listener, touchLayer);
237 self:addChild(touchLayer);
238
239 -- touch吞噬层.
240 local listener = cc.EventListenerTouchOneByOne:create();
241 listener:registerScriptHandler(
242 function(touch, event)
243 return isTouchRect(touch:getLocation());
244 end, cc.Handler.EVENT_TOUCH_BEGAN );
245 listener:setSwallowTouches(true);
246 self:getEventDispatcher():addEventListenerWithSceneGraphPriority(listener, self);
247
248 adjustPosition();
249 end
250
251 self.schSlideId = 0;
252 self.schLoadId = 0;
253 self.slide = Utils:createCocos2dObject(Control.SlideInertia);
254 self:addChild(self.slide);
255 self:setAnchorPoint(cc.p(0.5, 0.0));
256
257 -- 异步加载.
258 local num = 0;
259 self.schLoadId = Utils:runSchedule(function()
260 local cell = newCallFunc(num + 1);
261 self:addChild(cell);
262 cell:setAnchorPoint(cc.p(0.5, 0.0));
263 cell:setPosition(nodePosX + num * cellWidth, 0.0);
264 cell.isEnter = false;
265 cell:setTouchEnabled(true);
266 cell.onTouchBegan = function() if isLoadEnd then isTouch = true; end end;
267 cell.onTouchEnded = function() setCurCell(cell); end;
268 Utils:registerButtonEvent(cell);
269 table.insert(elements, cell);
270 cellScale = cell:getScale();
271 selCell = selCell and selCell or cell;
272
273 num = num + 1;
274 if num == count then
275 loadEnd();
276 end
277 end, 0.016);
278 end
279
280 function Control.ScrollView:destroy()
281 self.schSlideId = Utils:unSchedule(self.schSlideId);
282 self.schLoadId = Utils:unSchedule(self.schLoadId);
283 end
复制代码
该控件实质是一个cc.Node.
内部没有使用cc.ScrollView.
因为引擎自带的控件相当不好扩充. 最主要的是, 我喜欢造轮子.
该控件内部有一个 滑动对象, 该对象用于控制惯性.
该控件通过
newCallFunc 创造子节点.
SelCallFunc 节点最大化时回调.
enterCallFunc 节点开始变大时回调.
leaveCallFunc 节点恢复原状时回调.
移动子节点时, 需要对子节点逐个遍历移动.
这里占用了很大的性能开销.
不过, 这是不可能的.
在超出范围的子节点将被忽略, 并且隐藏.
并且内部使用了跳帧技巧.
根据节点数量变化跳帧数.
300个骨骼动画拖动时, 最低可在30帧以上, 平均45帧左右.