ScrollCycleView = {} local this = ScrollCycleView local reverse = table.reverse local function playGoAnim(self, go) if self.isPlayAnim then go.transform.localScale = Vector3.zero go.transform:DOScale(Vector3.one*1, 0.2) end end --表循环移动算法 local function move(list, k) k = k%#list if k == 0 then return end reverse(list, 1, #list-k) reverse(list, #list-k+1, #list) reverse(list, 1, #list) end local function SetUpdate(self, index, item) if index <= self.dataCount then if not item.isActive then item.isActive = true item.go:SetActive(true) end self.updateFunc(index, item.go) self.goItemDataRefList[item.go] = index playGoAnim(self, item.go) else if item.isActive then item.isActive = false item.go:SetActive(false) end end end local function ItemListOffset(self, offset) local newIndex, order, round, curRound, go for j=1, self.fixedCount do order = math.abs(offset) % self.itemCount --当偏移量超过最大显示数量,进行取余计算,作多轮次偏移 round = math.floor(math.abs(offset) / self.itemCount) if offset > 0 then for i=1, self.itemCount do curRound = (i <= order and 1 or 0) + round if curRound > 0 then newIndex = self.itemCount * curRound + self.dataLine + i go = self.cellItemList[j][i] go.tran.anchoredPosition = go.tran.anchoredPosition + self.offsetV2 * curRound SetUpdate(self, j + (newIndex - 1) * self.fixedCount, go) end end else for i=1, self.itemCount do curRound = (i <= order and 1 or 0) + round if curRound > 0 then newIndex = 1 - self.itemCount * (curRound - 1) + self.dataLine - i i = self.itemCount - i + 1 -- print(j, i) go = self.cellItemList[j][i] go.tran.anchoredPosition = go.tran.anchoredPosition - self.offsetV2 * curRound SetUpdate(self, j + (newIndex - 1) * self.fixedCount, go) end end end move(self.cellItemList[j], -offset) end end local function SetItemIndex(self, curIndex) curIndex = math.clamp(curIndex, 0, self.maxOffset) if curIndex ~= self.dataLine then ItemListOffset(self, curIndex - self.dataLine) self.dataLine = curIndex end end local function SetPosition(self, dv2,isBack) local av2 = self.dragGOTran.anchoredPosition if self.dragType == 1 then dv2.x = 0 else dv2.y = 0 end --if not self.elastic then if self.itemDis > 0 then if self.dragType == 1 then if av2.y < (0 - self.maxOffSet) then self.isReachMax = 1 elseif av2.y > (self.itemDis + self.maxOffSet) then self.isReachMax = 2 else self.isReachMax = 0 end elseif self.dragType == 2 then if av2.x > (0 + self.maxOffSet) then self.isReachMax = 1 elseif av2.x < -(self.itemDis + self.maxOffSet) then self.isReachMax = 2 else self.isReachMax = 0 end end else return end self.realPos = av2 + dv2 if self.isReachMax ~= 0 then self.moveTween.MomentumAmount = 0 self.moveTween.Momentum = Vector3.zero self.moveTween.Strength = 10 self.moveTween:LerpMomentum(Vector3.zero) if self.dragType == 1 then if (self.realPos + dv2).y < self.realPos.y and self.isReachMax == 1 then dv2.y = 0 elseif (self.realPos + dv2).y > self.realPos.y and self.isReachMax == 2 then dv2.y = 0 end else if (self.realPos + dv2).x > self.realPos.x and self.isReachMax == 1 then dv2.x = 0 elseif (self.realPos + dv2).x < self.realPos.x and self.isReachMax == 2 then dv2.x = 0 end end else self.moveTween.Strength = 1 self.moveTween.MomentumAmount = 1 end local fv2 = av2 + dv2 -- --if not self.elastic then -- if self.itemDis > 0 then -- if self.dragType == 1 then -- if fv2.y < (0 - self.maxOffSet) then -- fv2.y = 0 - self.maxOffSet -- self.isReachMax = true -- elseif fv2.y > (self.itemDis + self.maxOffSet) then -- fv2.y = self.itemDis + self.maxOffSet -- self.isReachMax = true -- else -- self.isReachMax = false -- end -- elseif self.dragType == 2 then -- if fv2.x > (0 + self.maxOffSet) then -- fv2.x = (0 + self.maxOffSet) -- self.isReachMax = true -- elseif fv2.x < -(self.itemDis + self.maxOffSet) then -- fv2.x = -(self.itemDis + self.maxOffSet) -- self.isReachMax = true -- else -- self.isReachMax = false -- end -- end -- else -- return -- end self.dragGOTran.anchoredPosition = fv2 local curIndex if self.dragType == 1 then curIndex = math.floor(self.dragGOTran.anchoredPosition.y / self.itemHeight) if self.scrollBar then self.scrollBar.value = self.dragGOTran.anchoredPosition.y / self.itemDis end else curIndex = math.floor(-self.dragGOTran.anchoredPosition.x / self.itemWidth) if self.scrollBar then self.scrollBar.value = -self.dragGOTran.anchoredPosition.x / self.itemDis end end SetItemIndex(self, curIndex) end local function OnBeginDrag(self, Pointgo, data) if not self.isCanDrag then return end if not self.dataList then return end self.isBeginDrag = true self.moveTween.enabled = true self.moveTween.Momentum = Vector3.zero self.moveTween.IsUseCallBack = false self.isReachMax = 0 data:Use() self.moveTween.Strength = 1 self.realPos = self.dragGOTran.anchoredPosition end local function OnDrag(self, Pointgo, data) if not self.isBeginDrag then return end if not self.dataList then return end self.moveTween:LerpMomentum(data.delta) SetPosition(self, data.delta) data:Use() end local function OnEndDrag(self, Pointgo, data) if not self.isBeginDrag then return end if not self.dataList then return end SetPosition(self, data.delta,true) self.moveTween.IsUseCallBack = true data:Use() self.isBeginDrag = false self.isReachMax = 0 self.moveTween.Strength = 1 end local function OnMoveEnd(self) local fv2 = self.dragGOTran.anchoredPosition if self.dragType == 1 then if self.itemDis < 0 then self.tmpTween = self.dragGOTran:DOAnchorPosY(0,0.3,false) else if fv2.y < 0 and self.dataLine == 0 then self.tmpTween = self.dragGOTran:DOAnchorPosY(0,0.3,false) end if fv2.y > self.itemDis and self.dataLine == self.maxOffset then self.tmpTween = self.dragGOTran:DOAnchorPosY(self.itemDis,0.3,false) end end elseif self.dragType == 2 then if self.itemDis < 0 then self.tmpTween = self.dragGOTran:DOAnchorPosX(0,0.3,false) else if fv2.x > 0 and self.dataLine == 0 then self.tmpTween = self.dragGOTran:DOAnchorPosX(0,0.3,false) end if fv2.x < -self.itemDis and self.dataLine == self.maxOffset then self.tmpTween = self.dragGOTran:DOAnchorPosX(-self.itemDis,0.3,false) end end end end function ScrollCycleView:New(gameObject) local b = {} b.gameObject = gameObject b.transform = gameObject.transform setmetatable(b,{ __index = ScrollCycleView }) return b end --初始化组件(用于子类重写) function ScrollCycleView:InitComponent() this.spLoader = SpriteLoader.New() --界面关闭时调用(用于子类重写) self.OnBeginDragNew = function(p,d) OnBeginDrag(self,p,d) end --界面关闭时调用(用于子类重写) self.OnDragNew= function(p,d) OnDrag(self,p,d) end --界面关闭时调用(用于子类重写) self.OnEndDragNew= function(p,d) OnEndDrag(self,p,d) end self.trigger = Util.GetEventTriggerListener(self.gameObject) self.trigger.onBeginDrag = self.trigger.onBeginDrag + self.OnBeginDragNew self.trigger.onDrag = self.trigger.onDrag + self.OnDragNew self.trigger.onEndDrag = self.trigger.onEndDrag + self.OnEndDragNew --self.trigger = Util.GetEventTriggerListener(self.gameObject) self.isCanDrag = true self.isBeginDrag = false -- self.trigger.onBeginDrag = self.trigger.onBeginDrag + function (p,d) OnBeginDrag(self,p,d) end -- self.trigger.onDrag = self.trigger.onDrag + function (p,d) OnDrag(self,p,d) end -- self.trigger.onEndDrag = self.trigger.onEndDrag + function (p,d) OnEndDrag(self,p,d) end self.rectTransform = self.gameObject:GetComponent("RectTransform") self.dragGO = Util.GetGameObject(self.gameObject, "grid") self.dragGOTran = self.dragGO:GetComponent("RectTransform") self.moveTween = self.dragGO:AddComponent(typeof(UITweenSpring)) self.moveTween.enabled = false self.moveTween.OnUpdate = function (v2) SetPosition(self,v2) end self.moveTween.OnMoveEnd = function () OnMoveEnd(self) end self.moveTween.MomentumAmount = 0.5 --拖动灵敏度(越大越灵敏) self.moveTween.Strength = 8 --滑动阻力(越大阻力越大) self.elastic = true --支持超框拖动 end --绑定事件(用于子类重写) function ScrollCycleView:BindEvent() end --添加事件监听(用于子类重写) function ScrollCycleView:AddListener() end --移除事件监听(用于子类重写) function ScrollCycleView:RemoveListener() end --界面关闭时调用(用于子类重写) function ScrollCycleView:OnClose() self.trigger.onBeginDrag = self.trigger.onBeginDrag - self.OnBeginDragNew self.trigger.onDrag = self.trigger.onDrag - self.OnDragNew self.trigger.onEndDrag = self.trigger.onEndDrag - self.OnEndDragNew Util.ClearChild(self.dragGOTran.transform) self.cellItemList = {} self.goItemList = {} self.goItemDataRefList = {} self.dataList = {} if self.co then coroutine.stop(self.co) self.co = nil end end --界面销毁时调用(用于子类重写) function ScrollCycleView:OnDestroy() this.spLoader:Destroy() end --界面打开时调用(用于子类重写) function ScrollCycleView:OnOpen(itemGO, scrollBar, scrollSizeDeltaV2, dragType, fixedCount, spacingV2) self.gameObject:SetActive(true) self.item = itemGO --关联的预设 self.scrollBar = scrollBar --关联scrollBar组件 self.rectTransform.sizeDelta = scrollSizeDeltaV2 --滚动界面大小 self.dragType = dragType --1 竖 2 横 self.fixedCount = fixedCount --自动排布的行数或者列数 self.spacing = spacingV2 --左右,上下间距 self.cellSize = itemGO:GetComponent("RectTransform").sizeDelta self.itemWidth = self.spacing.x + self.cellSize.x self.itemHeight = self.spacing.y + self.cellSize.y if self.dragType == 1 then self.dragGOTran.anchorMin = Vector2.New(0.5, 1) self.dragGOTran.anchorMax = Vector2.New(0.5, 1) self.dragGOTran.pivot = Vector2.New(0.5, 1) self.dragGOTran.sizeDelta = Vector2.New(self.itemWidth * (self.fixedCount - 1), 0) self.itemCount = math.ceil(scrollSizeDeltaV2.y / self.itemHeight + 1) self.offsetV2 = Vector2.New(0, -self.itemHeight * self.itemCount) elseif self.dragType == 2 then self.dragGOTran.anchorMin = Vector2.New(0, 0.5) self.dragGOTran.anchorMax = Vector2.New(0, 0.5) self.dragGOTran.pivot = Vector2.New(0, 0.5) self.dragGOTran.sizeDelta = Vector2.New(0, self.itemHeight * (self.fixedCount - 1)) self.itemCount = math.ceil(scrollSizeDeltaV2.x / self.itemWidth + 1) self.offsetV2 = Vector2.New(self.itemWidth * self.itemCount, 0) end self.cellItemList = {} for j=1, self.fixedCount do local itemList = {} for i=1, self.itemCount do itemList[i] = {go=nil, tran=nil, isActive=false} end self.cellItemList[j] = itemList end self.dragGOTran.anchoredPosition = Vector2.New(0, 0) end function this:SetData(dataList, updateFunc, noTop, noAnim,maxOffSet) if not maxOffSet then if not self.elastic then self.maxOffSet = 0 else self.maxOffSet = 500 end else self.maxOffSet = maxOffSet end self.dataList = dataList --传入的数据列表 self.updateFunc = updateFunc --刷新回调,返回数据列表的索引和对应预设 self.dataCount = #dataList self.isPlayAnim = not noAnim --计算grid偏移长度和偏移item个数 if self.dragType == 1 then self.itemDis = self.itemHeight * math.ceil(self.dataCount / self.fixedCount) + self.spacing.y - self.rectTransform.sizeDelta.y self.maxOffset = math.max(math.ceil(self.dataCount / self.fixedCount) - self.itemCount, 0) elseif self.dragType == 2 then self.itemDis = self.itemWidth * math.ceil(self.dataCount / self.fixedCount) + self.spacing.x - self.rectTransform.sizeDelta.x self.maxOffset = math.max(math.ceil(self.dataCount / self.fixedCount) - self.itemCount, 0) end self.goItemList = {} self.goItemDataRefList = {} if self.tmpTween then self.tmpTween:Kill() self.tmpTween = nil end -- 判断是否回到顶部,否则保持不动 if noTop then local oldPosY = self.dragGOTran.anchoredPosition.y local oldPosX = self.dragGOTran.anchoredPosition.x local showIndex = self:CalShowIndex() self:SetShow(showIndex) self.dragGOTran.anchoredPosition = Vector2.New(oldPosX, oldPosY) else self:SetShow(1) end end -- 获取位置 function this:CalShowIndex() local index = 1 if self.dragType == 1 then index = math.abs(self.dragGOTran.anchoredPosition.y/self.itemHeight) * self.fixedCount elseif self.dragType == 2 then index = math.abs(self.dragGOTran.anchoredPosition.x/self.itemWidth) * self.fixedCount end return index end function this:SetShow(showIndex) -- 记录旧位置 local oldLine = self.dataLine or 0 -- 计算目标数据位置 local itemOffset = math.ceil(showIndex / self.fixedCount) - 1 self.dataLine = math.clamp(itemOffset, 0, self.maxOffset) self.showLine = self.dataLine -- 计算滚动列表的位置 if self.itemDis > 0 then if self.dragType == 1 then self.dragGOTran.anchoredPosition = Vector2.New(0, math.clamp(self.itemHeight * itemOffset, 0, self.itemDis)) elseif self.dragType == 2 then self.dragGOTran.anchoredPosition = Vector2.New(math.clamp(-self.itemWidth * itemOffset, -self.itemDis, 0), 0) end else self.dragGOTran.anchoredPosition = Vector2.New(0, 0) end -- 隐藏所有物体,并处理物体在数据结构中的位置 for j=1, self.fixedCount do for i=1, self.itemCount do local item = self.cellItemList[j][i] if item.go then item.isActive = false item.go:SetActive(false) end end move(self.cellItemList[j], oldLine - self.dataLine) end -- 刷新滑动条显示 self:InitDragBar() -- 分帧加载物体 local index, item, dataIndex if self.co then coroutine.stop(self.co) end if self.isPlayAnim then self.co = coroutine.start(function() -- 分帧加载期间不可拖动 self.isCanDrag = false for i=1, self.itemCount do -- local isNew = false for j=1, self.fixedCount do item = self.cellItemList[j][i] index = j+(i-1)*self.fixedCount dataIndex = j+(i-1 + self.dataLine)*self.fixedCount if IsNull(item.go) then local go = newObject(self.item) go.transform:SetParent(self.dragGO.transform) go.transform.localScale = Vector3.one go.transform.localPosition = Vector3.zero go:SetActive(false) local tran = go:GetComponent("RectTransform") tran.anchorMin = Vector2.New(0, 1) tran.anchorMax = Vector2.New(0, 1) item.go = go item.tran = tran -- coroutine.wait(0.01) -- isNew = true end if item.go then item.go.name = "item"..index if self.dragType == 1 then item.tran.anchoredPosition = Vector2.New(self.itemWidth * (j-1), (0.5-i - self.dataLine) * self.itemHeight) else item.tran.anchoredPosition = Vector2.New((i-0.5 + self.dataLine) * self.itemWidth, self.itemHeight * (1-j)) end self.goItemList[index] = item.go end SetUpdate(self, dataIndex, item) end -- if not isNew then coroutine.wait(0.01) -- end end -- 加载完成可以拖动了 self.isCanDrag = true end) else for i=1, self.itemCount do -- local isNew = false for j=1, self.fixedCount do item = self.cellItemList[j][i] index = j+(i-1)*self.fixedCount dataIndex = j+(i-1 + self.dataLine)*self.fixedCount if not item.go then local go = newObject(self.item) go.transform:SetParent(self.dragGO.transform) go.transform.localScale = Vector3.one go.transform.localPosition = Vector3.zero go:SetActive(false) local tran = go:GetComponent("RectTransform") tran.anchorMin = Vector2.New(0, 1) tran.anchorMax = Vector2.New(0, 1) item.go = go item.tran = tran -- coroutine.wait(0.01) -- isNew = true end if item.go then item.go.name = "item"..index if self.dragType == 1 then item.tran.anchoredPosition = Vector2.New(self.itemWidth * (j-1), (0.5-i - self.dataLine) * self.itemHeight) else item.tran.anchoredPosition = Vector2.New((i-0.5 + self.dataLine) * self.itemWidth, self.itemHeight * (1-j)) end self.goItemList[index] = item.go end SetUpdate(self, dataIndex, item) end end end end -- 初始化滑动条 function this:InitDragBar() if self.scrollBar then -- 大小和位置 if self.dragType == 1 then self.scrollBar.size = self.rectTransform.sizeDelta.y / (self.itemHeight * math.ceil(self.dataCount / self.fixedCount) + self.spacing.y) self.scrollBar.value = self.dragGOTran.anchoredPosition.y / self.itemDis elseif self.dragType == 2 then self.scrollBar.size = self.rectTransform.sizeDelta.x / (self.itemWidth * math.ceil(self.dataCount / self.fixedCount) + self.spacing.x) self.scrollBar.value = -self.dragGOTran.anchoredPosition.x / self.itemDis end -- 滑动监听 self.scrollBar.onValueChanged:AddListener(function(f) if not self.isCanDrag then return end local v2, curIndex if self.dragType == 1 then v2 = Vector2.Lerp(Vector2.zero, Vector2.New(0, self.itemDis), f) curIndex = math.floor(v2.y / self.itemHeight) elseif self.dragType == 2 then v2 = Vector2.Lerp(Vector2.zero, Vector2.New(-self.itemDis, 0), f) curIndex = math.floor(-v2.x / self.itemWidth) end self.dragGOTran.anchoredPosition = v2 SetItemIndex(self, curIndex) end) end end function this:ForeachItemGO(func) if not func or not self.goItemList then return end for i=1, math.min(#self.goItemList, self.dataCount) do func(i, self.goItemList[i]) end end --设置当前索引位置在最上层 function this:SetIndex(curIndex) Log("setIndex "..curIndex) -- if self.co then -- coroutine.stop(self.co) -- end -- local itemOffset = math.ceil(curIndex / self.fixedCount) - 1 -- if self.itemDis > 0 then -- if self.dragType == 1 then -- self.dragGOTran.anchoredPosition = Vector2.New(0, math.clamp(self.itemHeight * itemOffset, 0, self.itemDis)) -- elseif self.dragType == 2 then -- self.dragGOTran.anchoredPosition = Vector2.New(math.clamp(-self.itemWidth * itemOffset, -self.itemDis, 0), 0) -- end -- else -- self.dragGOTran.anchoredPosition = Vector2.New(0, 0) -- end -- SetItemIndex(self, itemOffset) self:SetShow(curIndex) end --通过item预设获取关联的数据项索引 function this:GetItemDataIndex(go) return self.goItemDataRefList[go] end function this:SetRectTransform(scrollSizeDeltaV2) self.rectTransform.sizeDelta = scrollSizeDeltaV2 end return ScrollCycleView