|
- -- 智能滚动界面
- --[[
- initCount ? : number, // 初始加载initCount个UIItem
- loadCount ? : number, // 滚动到增长边界时则加载loadCount个item
- initIndex ? : number, // 初始化索引位置,从1开始
- isPlayAnime ? : boolean, // 是否在第一次更新数据时播放动画
- isWheelStyle ? : boolean, // 是否滚轮风格
- focusPercent ? : number, // 从0到1,表示从下到上,从左到右,聚焦的百分比位置
- radiusRate ? : number, // 滚轮效果显著程度参数,越大越不明显
- coverRate ? : number, // ui条目覆盖效果,越大覆盖越明显
- autoScrollToFocus ? : boolean, // 自动将最近的ui回滚到焦点
- scrollDisScale ? : number, //
- --]]
- local SmartScrollView = class("SmartScrollView");
-
- function SmartScrollView:ctor(scrollView, param)
- self.initCount = (param and param.initCount) or 8;
- self.loadCount = (param and param.loadCount) or 3;
- self.initIndex = (param and param.initIndex) or 1;
- self.isPlayAnime = param and param.isPlayAnime;
- self.isWheelStyle = param and param.isWheelStyle;
- self.focusPercent = (param and param.focusPercent) or 0.5;
- self.radiusRate = (param and param.radiusRate) or app.config.Rule.SCROLL_VIEW_WHEEL_RADIUS_RATE.value;
- self.coverRate = (param and param.coverRate) or app.config.Rule.SCROLL_VIEW_WHEEL_COVER_RATE.value;
- self.autoScrollToFocus = param and param.autoScrollToFocus;
- self.scrollDisScale = (param and param.scrollDisScale) or 1;
- self.FocusRadius = 5; -- 焦点范围误差距离
- self.orderArray = {}; -- 数据必须连续有效,且每个数据皆可创建有效ui
- self.scrollItems = {}; -- Map<number , {layoutNode : cc.Widget , showObj : any}>(); 所有UIItem Map
- self.headDataIdx = 0;
- self.tailDataIdx = 1;
- self.dataChangeOnce = true;
- self.playAnimeOnce = true;
-
- self.scrollView = scrollView
-
- self:init();
- end
-
- -- 数据数组
- function SmartScrollView:getOrderArray()
- return self.orderArray;
- end
-
- -- 赋值触发初始化
- function SmartScrollView:setOrderArray(arr)
- -- 初始化数据获取方法,若数据变化,则UI重新加载
- local me = self
- me.orderArray = arr;
- if me.dataChangeOnce then
- me.dataChangeOnce = false;
- if not me.scrollView or tolua.isnull(me.scrollView) then
- print("SmartScrollView wasn't assigned UIScrollView!");
- return
- end
- me.scrollView:runDelay(0, function()
- me.dataChangeOnce = true;
- me:onDataChanged();
- end);
- end
- end
-
- function SmartScrollView:init()
- local me = self;
- local innerContainer = me.scrollView:getInnerContainer();
- innerContainer:setAutoSize(true);
- -- me.scrollView:setScrollDisScale(me.scrollDisScale);
- -- 隐藏进度条
- me.scrollView:getHBar():setVisible(false);
- me.scrollView:getVBar():setVisible(false);
-
- -- 滚轮风格初始化
- if me.isWheelStyle then
- local oldDoLayout = innerContainer.doLayout;
- innerContainer.doLayout = function()
- for k,v in ipairs(me.scrollItems) do
- v.layoutNode.LocalZOrder = k;
- end
- oldDoLayout(innerContainer);
- me.onScrollWheel();
- end
- -- 解决分辨率变化等导致的界面混乱问题
- me.scrollView:setWidgetEventListener(function(node, eventType)
- if eventType == cc.WidgetEvent.SIZE_CHANGED then
- me.scrollView.runDelay(0 , function()
- me.doLayout();
- end);
- end
- end);
- end
-
- -- 监听滚到顶部和底部(LocalZOrder大的,布局时排在下面)
- me.scrollView:addEventListener(function(target , eventType)
- if eventType == cc.ScrollviewEventType.scrollToTop or eventType == cc.ScrollviewEventType.scrollToLeft then
- if me.headDataIdx < 1 then
- return;
- end
- for i = 1 , me.loadCount do
- me:addItem(true);
- end
- if me.headDataIdx < 1 and me.isWheelStyle and #me.scrollItems > 0 then
- me:fillStart();
- end
- if me.scrollView:getDirection() == cc.ScrollViewDir.horizontal then
- if not cc.pEqual(innerContainer:getAnchorPoint() , cc.p(1 , 0)) then
- innerContainer:setPositionX(innerContainer:getPositionX() + (1 - innerContainer:getAnchorPoint().x) * innerContainer:getSize().width);
- innerContainer:setPositionY(innerContainer:getPositionY() - innerContainer:getAnchorPoint().y * innerContainer:getSize().height);
- innerContainer:setAnchorPoint(cc.p(1 , 0));
- end
- else
- if not cc.pEqual(innerContainer:getAnchorPoint() , cc.p(0 , 0)) then
- innerContainer:setPositionX(innerContainer:getPositionX() - innerContainer:getAnchorPoint().x * innerContainer:getSize().width);
- innerContainer:setPositionY(innerContainer:getPositionY() - innerContainer:getAnchorPoint().y * innerContainer:getSize().height);
- innerContainer:setAnchorPoint(cc.p(0 , 0));
- end
- end
- me:doLayout();
- elseif eventType == cc.ScrollviewEventType.scrollToBottom or eventType == cc.ScrollviewEventType.scrollToRight then
- if me.tailDataIdx > #me.orderArray then
- return;
- end
- for i = 1 , me.loadCount do
- me:addItem(false);
- end
- if me.tailDataIdx > #me.orderArray and me.isWheelStyle and #me.scrollItems > 0 then
- me:fillEnd();
- end
- if me.scrollView:getDirection() == cc.ScrollViewDir.horizontal then
- if not cc.pEqual(innerContainer:getAnchorPoint() , cc.p(0 , 0)) then
- innerContainer:setPositionX(innerContainer:getPositionX() - innerContainer:getAnchorPoint().x * innerContainer:getSize().width);
- innerContainer:setPositionY(innerContainer:getPositionY() - innerContainer:getAnchorPoint().y * innerContainer:getSize().height);
- innerContainer:setAnchorPoint(cc.p(0 , 0));
- end
- else
- if not cc.pEqual(innerContainer:getAnchorPoint() , cc.p(0 , 1)) then
- innerContainer:setPositionX(innerContainer:getPositionX() - innerContainer:getAnchorPoint().x * innerContainer:getSize().width);
- innerContainer:setPositionY(innerContainer:getPositionY() + (1 - innerContainer:getAnchorPoint().y) * innerContainer:getSize().height);
- innerContainer:setAnchorPoint(cc.p(0 , 1));
- end
- end
- me:doLayout();
- end
- if eventType == cc.ScrollviewEventType.scrolling then
- if me.isWheelStyle then
- me:onScrollWheel();
- end
- --[[ elseif eventType == cc.ScrollviewEventType.scrollingEnded then
- -- 自动回滚
- if me.isWheelStyle and me.autoScrollToFocus then
- local minDis;
- local minOffset
- for i,v in ipairs(me.scrollItems) do
- local node = v.layoutNode;
- local pos = me.scrollView:convertToNodeSpace2D(node:getWorldPosition());
- -- ui相对于scrollView的center坐标
- local center;
- local offset;
- local dis = 0;
- if me.scrollView:getDirection() == cc.ScrollViewDir.horizontal then
- center = pos.x + node:getContentSize().width * (0.5 - node:getAnchorPoint().x);
- if center > me.scrollView:getSize().width or center < 0 then
- return;
- end
- offset = center - me.scrollView:getSize().width * me.focusPercent;
- dis = math.abs(offset);
- else
- center = pos.y + node:getContentSize().height * (0.5 - node:getAnchorPoint().y);
- if center > me.scrollView:getSize().height or center < 0 then
- return;
- end
- offset = center - me.scrollView:getSize().height * me.focusPercent;
- dis = math.abs(offset);
- end
- if not minDis then
- minDis = dis;
- minOffset = offset;
- return;
- end
- if dis < minDis then
- minDis = dis;
- minOffset = offset;
- end
- end;
- if #me.scrollItems > 0 and minDis > me.FocusRadius then
- local vec2 = cc.p(0 , 0);
- if me.scrollView:getDirection() == cc.ScrollViewDir.horizontal then
- vec2.x = -minOffset / me.scrollDisScale;
- else
- vec2.y = -minOffset / me.scrollDisScale;
- end
- me.scrollView:scrollBy(vec2 , 0.5 / me.scrollDisScale , true);
- me:onScrollWheel();
- end
- end
- --]]
- end
- end);
- end
-
- function SmartScrollView:doLayout()
- self.scrollView:getInnerContainer():requestDoLayout();
- self.scrollView:getInnerContainer():doLayout();
- end
-
- function SmartScrollView:removeAllChildren()
- self.scrollView:removeAllChildren();
- self.scrollItems = {};
- end
-
- -- @param index 对应数组下标,从1开始
- function SmartScrollView:getUIItem(index)
- return self.scrollItems[index].showObj;
- end
-
- -- 将index 对应的ui 节点滚动到focus处,从1开始
- function SmartScrollView:scrollToIndex(index , onEnd)
- local me = self;
- if index < 1 or index > #me.orderArray then
- return;
- end
- if #me.scrollItems <= 0 then
- return;
- end
- while index <= me.headDataIdx or index >= me.tailDataIdx do
- me:addItem(index <= me.headDataIdx);
- end
- me:doLayout();
- local item = me.scrollItems[index].layoutNode;
- if not item then
- return;
- end
- local pos = me.scrollView:convertToNodeSpace2D(item:getWorldPosition());
- local center;
- local offset;
- local speed = 1800;
- if me.scrollView:getDirection() == cc.ScrollViewDir.horizontal then
- center = pos.x + item:getContentSize().width * (0.5 - item:getAnchorPoint().x);
- offset = center - me.scrollView:getSize().width * me.focusPercent;
- me.scrollView:scrollBy(cc.p(-offset , 0) , math.max(offset / speed , 0.5) , true);
- else
- center = pos.y + item:getContentSize().height * (0.5 - item:getAnchorPoint().y);
- offset = center - me.scrollView:getSize().height * me.focusPercent;
- me.scrollView:scrollBy(cc.p(0 , -offset) , math.max(offset / speed , 0.5) , true);
- end
- if onEnd then
- me.scrollView:runDelay(math.max(offset / speed , 0.5) , onEnd);
- end
- end
-
- -- index从1开始
- function SmartScrollView:jumpToIndex(index)
- local me = self;
- local innerContainer = me.scrollView:getInnerContainer();
- if index < 1 and index > #me.orderArray then
- return;
- end
- if #me.scrollItems <= 0 then
- return;
- end
- while index <= me.headDataIdx or index >= me.tailDataIdx do
- me:addItem(index <= me.headDataIdx);
- end
- me:doLayout();
- local item = me.scrollItems[index].layoutNode;
- if not item or not item:isVisible() then
- me.scrollView:jumpToTop();
- return;
- end
- local pos = me.scrollView:convertToNodeSpace2D(item:getWorldPosition());
- local center;
- local offset;
- if me.scrollView:getDirection() == cc.ScrollViewDir.horizontal then
- center = pos.x + item:getContentSize().width * (0.5 - item:getAnchorPoint().x);
- offset = center - me.scrollView:getSize().width * me.focusPercent;
- local finalPosX = innerContainer:getPositionX() - offset;
- innerContainer:setPositionX(finalPosX);
- local finalRightX = finalPosX + (1 - innerContainer:getAnchorPoint().x) * innerContainer:getSize().width;
- local finalLeftX = finalPosX - innerContainer:getAnchorPoint().x * innerContainer:getSize().width;
- if finalRightX < me.scrollView:getSize().width then
- innerContainer:setPositionX(finalPosX + me.scrollView:getSize().width - finalRightX);
- end
- if finalLeftX > 0 then
- innerContainer:setPositionX(finalPosX - finalLeftX);
- end
- else
- center = pos.y + item:getContentSize().height * (0.5 - item:getAnchorPoint().y);
- offset = center - me.scrollView:getSize().height * me.focusPercent;
- local finalPosY = innerContainer:getPositionY() - offset;
- innerContainer:setPositionY(finalPosY);
- local finalTopY = finalPosY + (1 - innerContainer:getAnchorPoint().y) * innerContainer:getSize().height;
- local finalBottomY = finalPosY - innerContainer:getAnchorPoint().y * innerContainer:getSize().height;
- if finalBottomY > 0 then
- innerContainer:setPositionY(finalPosY - finalBottomY);
- end
- if finalTopY < me.scrollView:getSize().height then
- innerContainer:setPositionY(finalPosY + me.scrollView:getSize().height - finalTopY);
- end
- end
- end
-
-
- -- orderArray更新后调用
- function SmartScrollView:onDataChanged()
- local me = self;
- me:removeAllChildren();
- -- 修正initIndex
- if me.initIndex < 1 then
- me.initIndex = 1;
- end
- if me.initIndex > #me.orderArray then
- me.initIndex = #me.orderArray;
- end
-
- -- 修正headDataIdx 的初始位置
- me.headDataIdx = me.initIndex + math.floor(me.initCount / 2);
- if me.headDataIdx < 1 then
- me.headDataIdx = 1;
- end
- if me.headDataIdx > #me.orderArray then
- me.headDataIdx = #me.orderArray;
- end
- local count = #me.orderArray > me.initCount and me.initCount or #me.orderArray;
- for i = 1 , count do
- me:addItem(true);
- end
- me.tailDataIdx = me.headDataIdx + #me.scrollItems + 1;
- if me.isWheelStyle and me.headDataIdx < 1 and #me.scrollItems > 0 then
- me:fillStart();
- end
- if me.isWheelStyle and me.tailDataIdx > #me.orderArray and #me.scrollItems > 0 then
- me:fillEnd();
- end
- me:doLayout();
- -- 第一次更新数据,播放动画
- if me.isPlayAnime and me.playAnimeOnce then
- me.playAnimeOnce = false;
- local delayTime = 0.5 --app.config.Rule.get("SCROLL_VIEW_ANI_DELAY_TIME").value;
- local playTime = 0.5 --app.config.Rule.get("SCROLL_VIEW_ANI_PLAY_TIME").value;
- local time = 0;
- for i = me.headDataIdx + 1 , me.tailDataIdx - 1 do
- local node = me.scrollItems[i].layoutNode;
- if node and node:isVisible() then
- time = time + delayTime;
- local offset = node:getContentSize().width;
- for ii,vv in pairs(node:getChildren()) do
- if vv:isVisible() then
- vv:setPositionX(vv:getPositionX() - offset);
- vv:runAction(cc.Sequence.create(cc.DelayTime:create(time)
- , cc.MoveBy:create(playTime , cc.vec3(offset,0,0))
- ));
- end
- end
- end
- end
- end
- me:jumpToIndex(me.initIndex);
- if me.isWheelStyle then
- me:onScrollWheel();
- end
- end
-
- -- 在开头填空
- function SmartScrollView:fillStart()
- local me = self;
- local node = me.scrollItems[1].layoutNode;
- local startNullNode = cc.Widget.create();
- startNullNode:setAutoSize(false);
- if me.scrollView:getDirection() == cc.ScrollViewDir.horizontal then
- startNullNode:setSize(cc.size(me.scrollView:getSize().width * me.focusPercent - node:getContentSize().width / 2
- , node:getContentSize().height
- ));
- else
- startNullNode:setSize(cc.size(node:getContentSize().width
- , me.scrollView:getSize().height * (1 - me.focusPercent) - node:getContentSize().height / 2
- ));
- end
- startNullNode:setAutoSize(true);
- me.scrollView:addChild(startNullNode , -100);
- end
-
- -- 在结尾填空
- function SmartScrollView:fillEnd()
- local me = self;
- local node = me.scrollItems[#me.orderArray].layoutNode;
- local endNullNode = cc.Widget.create();
- endNullNode:setAutoSize(false);
- if me.scrollView:getDirection() == cc.ScrollViewDir.horizontal then
- endNullNode:setSize(cc.size(
- me.scrollView:getSize().width * (1 - me.focusPercent) - node:getContentSize().width / 2 ,
- node:getContentSize().height
- ));
- else
- endNullNode:setSize(cc.size(
- node:getContentSize().width ,
- me.scrollView:getSize().height * me.focusPercent - node:getContentSize().height / 2
- ));
- end
- endNullNode:setAutoSize(true);
- me.scrollView:addChild(endNullNode , #me.orderArray + 100);
- end
-
- -- 滚轮效果
- function SmartScrollView:onScrollWheel()
- local me = self;
- for i,v in pairs(me.scrollItems) do
- local node = v.layoutNode;
- local pos = me.scrollView:convertToNodeSpace2D(node:getWorldPosition());
- -- ui相对于scrollView的center坐标
- local center;
- local dis;
- local range;
- if me.scrollView:getDirection() == cc.ScrollViewDir.horizontal then
- center = pos.x + node:getContentSize().width * (0.5 - node:getAnchorPoint().x);
- local a = center > me.scrollView:getSize().width + node:getContentSize().width * 2;
- local b = center < -node:getContentSize().width * 2;
- if a or b then
- return;
- end
- dis = math.abs(center - me.scrollView:getSize().width * me.focusPercent);
- range = (me.scrollView:getSize().width + node:getContentSize().width) / 2 * me.radiusRate;
- else
- center = pos.y + node:getContentSize().height * (0.5 - node:getAnchorPoint().y);
- local a = center > me.scrollView:getSize().height + node:getContentSize().height * 2;
- local b = center < -node:getContentSize().height * 2;
- if a or b then
- return;
- end
- dis = math.abs(center - me.scrollView:getSize().height * me.focusPercent);
- range = (me.scrollView:getSize().height / 2 + node:getContentSize().height / 2) * me.radiusRate;
- end
- -- 可视范围内的
- local item = node:getChildren()[0];
- local percent = (range - dis) / range;
- if percent < 0.001 then
- percent = 0.001;
- end
- -- 置透明度
- item:setOpacity(255 * percent);
- -- 置缩放大小
- item:setScale(percent);
- -- 置渲染秩序
- node:setLocalZOrder(range - dis);
- end
- end
-
- function SmartScrollView:nextDataIdx(isAddToHead)
- local me = self;
- if isAddToHead then
- me.headDataIdx = me.headDataIdx - 1;
- else
- me.tailDataIdx = me.tailDataIdx + 1;
- end
- end
-
- -- @param isAddToHead 是否向数组小端取数据
- function SmartScrollView:addItem(isAddToHead)
- local me = self;
- local curDataIdx = isAddToHead and me.headDataIdx or me.tailDataIdx;
- if not me.orderArray[curDataIdx] then
- if curDataIdx >= 1 and curDataIdx <= #me.orderArray then
- me:nextDataIdx(isAddToHead);
- end
- return false;
- end
-
- assert(me.createUI , "SmartScrollView wasn't assigned ui create function!");
- local item = me:createUI(me.orderArray[curDataIdx], curDataIdx);
- if not item then
- me:nextDataIdx(isAddToHead);
- return false;
- end
- local ui = item.ui or item;
- local node = cc.Widget:create();
- node:setAutoSize(false);
- if me.scrollView:getDirection() == cc.ScrollViewDir.horizontal then
- node:setSize(cc.size(ui:getSize().width - me.coverRate , ui:getSize().height));
- ui:setPositionX(ui:getAnchorPoint().x * ui:getSize().width - me.coverRate / 2);
- ui:setPositionY(ui:getAnchorPoint().y * ui:getSize().height);
- else
- node:setSize(cc.size(ui:getSize().width , ui:getSize().height - me.coverRate));
- ui:setPositionX(ui:getAnchorPoint().x * ui:getSize().width);
- ui:setPositionY(ui:getAnchorPoint().y * ui:getSize().height - me.coverRate / 2);
- end
- -- 节点可见性
- local oldValue = ui:isVisible();
- ui:setVisible(true);
- ui.setVisible = function(this , flag)
- node:setVisible(flag);
- end
- ui.isVisible = function()
- return node:isVisible();
- end
- node:addChild(ui);
- node:setAutoSize(true);
- node:setVisible(oldValue);
- me.scrollView:addChild(node , curDataIdx);
- me.scrollItems[curDataIdx] = {layoutNode = node , showObj = item};
- me:nextDataIdx(isAddToHead);
- return true;
- end
-
- return SmartScrollView;
|