說明
由于Unity自帶的Scroll View控件不能靈活的滿足自己實(shí)際開發(fā)鼻忠,因此想到自己制作一個(gè)滑動展示頁面教硫,且該控件不依賴Unity的其他UI控件奥务,但該控件存在局限性泄私,僅供參考
該篇文章著重介紹原理和結(jié)構(gòu)房揭,不會過多討論代碼的編寫,需要查看源碼可以訪問一下地址
實(shí)際效果預(yù)覽:
結(jié)構(gòu)設(shè)計(jì)
該控件滑動周期由三個(gè)部分組成 :
1 滑動時(shí) : 隨觸摸點(diǎn)(或鼠標(biāo))滑動
2 退出滑動時(shí) : 滑動衰減
3 滑動結(jié)束時(shí) : 位置校正
滑動展示欄由一個(gè)控制器和多個(gè)滑塊組成晌端,使用時(shí)需要設(shè)置控制器的參數(shù)配置捅暴,內(nèi)部的滑動item需要有FScrollItem組件或者繼承自FScrollItem的組件,其中FScrollItem開放了點(diǎn)擊和被選中事件咧纠,可以在兩個(gè)接口中寫自定義邏輯蓬痒。
-
滑動控制器 -- FScrollPage
-
滑動對象 -- FScrollItem
FScrollPage
滑動控制器FScrollPage 負(fù)責(zé)整理FScrollitem的位置和大小,用戶觸摸交互漆羔,監(jiān)聽滑動事件梧奢,執(zhí)行滑動特效以及傳遞選中事件給子物體FScrollitem。
考慮到易用性演痒,在FScrollPage的初始化分為兩種亲轨,第一種是自動檢查指定節(jié)點(diǎn)下的子對象進(jìn)行標(biāo)記id和排序整理m默認(rèn)選中第一個(gè)被標(biāo)記的item,第二種方式是傳遞一個(gè)map<itemID , item> 以及默認(rèn)選中id鸟顺。
FScrollItem
滑動對象FScrollItem 提供了點(diǎn)擊惦蚊、被選中回調(diào)事件,當(dāng)被點(diǎn)擊時(shí)自動將該Item滑動到中心并向FScrollPage通知并選中到該Item讯嫂。
位置整理
統(tǒng)一錨點(diǎn)布局方式蹦锋。
按指定配置設(shè)置item的位置、大小欧芽、間距莉掂。
將獲取到的子對象map<itemID,item>進(jìn)行遍歷千扔,生成對應(yīng)的FScroillObject對象并設(shè)置統(tǒng)一的布局方式(居中),初始化lastPostion和Postion (FScrollObject除了有RectTransfrom屬性外還有一個(gè)Postion屬性和lastPostion屬性憎妙,分別用來紀(jì)錄當(dāng)前(或正在前往)的位置和上一次位置信息)库正。用于后續(xù)的滑動和位置校正。
滑動監(jiān)聽
滑動監(jiān)聽原理時(shí)通過檢測有觸摸或按下事件到滑動條上時(shí)激活滑動檢測尚氛,當(dāng)移動速度大于閾值時(shí)則判定當(dāng)前正在滑動中诀诊,當(dāng)檢測到手指或鼠標(biāo)離開滑動條時(shí)結(jié)束滑動監(jiān)聽。
這里有一點(diǎn)需要注意阅嘶,如果觸摸滑動條觸摸到了item這會被攔截點(diǎn)擊事件,滑動條的OnPointerDown事件不會被觸發(fā)载迄,解決辦法我開始參考的是一篇滲透UI點(diǎn)擊事件文章 : 滲透UI點(diǎn)擊事件 讯柔,但是使用后發(fā)現(xiàn)多個(gè)UI重疊時(shí)會出現(xiàn)棧內(nèi)存溢出,導(dǎo)致編輯器卡死护昧,后來想了一個(gè)更簡單的辦法魂迄,在FScrollItem中同樣實(shí)現(xiàn)點(diǎn)擊事件接口,當(dāng)被按下或抬起時(shí)直接將事件繼續(xù)傳遞給控制器的對應(yīng)事件惋耙。
public void OnPointerDown(PointerEventData eventData)
{
//傳遞事件
scrollPage.OnPointerDown(eventData);
//后續(xù)邏輯
lastPointer = eventData.position;
}
滑動效果以及位置校正
滑動時(shí)根據(jù)滑動速度來控制item的移動捣炬,滑動結(jié)束后使用速度線性遞減,當(dāng)速度低于一定閾值時(shí)執(zhí)行位置校正绽榛,其中彈動的效果使用的時(shí)一個(gè)緩動函數(shù)BackEaseOut湿酸。
static float BackEaseOut(float t, float b = 0, float c = 1, float d = 1)
{
return c * ((t = t / d - 1) * t * ((1.70158f + 1) * t + 1.70158f) + 1) + b;
}
位置校正時(shí)先查詢距離中心點(diǎn)最近的item,記錄并傳遞該item的ID到滑動方法中灭美,然后通過滑動方法使Item移動到正常位置推溃。
源碼已知存在的缺陷
如果你需要使用源碼你需要了解以下的缺陷,你可以對缺陷進(jìn)行修改或者修復(fù)
滑動到邊界時(shí)沒有約束
錨點(diǎn)固定為居中届腐,可能會與你的項(xiàng)目有沖突铁坎,也可能導(dǎo)致自適應(yīng)UI出現(xiàn)問題
點(diǎn)擊判定過于僵硬(按下坐標(biāo)和抬起坐標(biāo)的Distance小于指定閾值則觸發(fā)點(diǎn)擊)
滑動衰弱的時(shí)間和位置校正時(shí)間是固定的
其他
如果你有疑問,請到我的源碼地址下留言評論犁苏,該控件主要是技術(shù)上的研究硬萍,目前不夠完善,建議根據(jù)項(xiàng)目需要修改源碼围详。