說(shuō)明
由于Unity自帶的Scroll View控件不能靈活的滿足自己實(shí)際開(kāi)發(fā),因此想到自己制作一個(gè)滑動(dòng)展示頁(yè)面,且該控件不依賴Unity的其他UI控件,但該控件存在局限性,僅供參考
該篇文章著重介紹原理和結(jié)構(gòu),不會(huì)過(guò)多討論代碼的編寫,需要查看源碼可以訪問(wèn)一下地址
實(shí)際效果預(yù)覽:
橫向滑動(dòng)展示欄
縱向滑動(dòng)展示欄
結(jié)構(gòu)設(shè)計(jì)
該控件滑動(dòng)周期由三個(gè)部分組成 :
1 滑動(dòng)時(shí) : 隨觸摸點(diǎn)(或鼠標(biāo))滑動(dòng)
2 退出滑動(dòng)時(shí) : 滑動(dòng)衰減
3 滑動(dòng)結(jié)束時(shí) : 位置校正
滑動(dòng)展示欄由一個(gè)控制器和多個(gè)滑塊組成,使用時(shí)需要設(shè)置控制器的參數(shù)配置,內(nèi)部的滑動(dòng)item需要有FScrollItem組件或者繼承自FScrollItem的組件,其中FScrollItem開(kāi)放了點(diǎn)擊和被選中事件,可以在兩個(gè)接口中寫自定義邏輯。
-
滑動(dòng)控制器 -- FScrollPage
FScrollPage -
滑動(dòng)對(duì)象 -- FScrollItem
FScrollItem
FScrollPage
滑動(dòng)控制器FScrollPage 負(fù)責(zé)整理FScrollitem的位置和大小,用戶觸摸交互,監(jiān)聽(tīng)滑動(dòng)事件,執(zhí)行滑動(dòng)特效以及傳遞選中事件給子物體FScrollitem。
考慮到易用性,在FScrollPage的初始化分為兩種,第一種是自動(dòng)檢查指定節(jié)點(diǎn)下的子對(duì)象進(jìn)行標(biāo)記id和排序整理m默認(rèn)選中第一個(gè)被標(biāo)記的item,第二種方式是傳遞一個(gè)map<itemID , item> 以及默認(rèn)選中id。
FScrollItem
滑動(dòng)對(duì)象FScrollItem 提供了點(diǎn)擊、被選中回調(diào)事件,當(dāng)被點(diǎn)擊時(shí)自動(dòng)將該Item滑動(dòng)到中心并向FScrollPage通知并選中到該Item。
位置整理
統(tǒng)一錨點(diǎn)布局方式。
按指定配置設(shè)置item的位置、大小、間距。
將獲取到的子對(duì)象map<itemID,item>進(jìn)行遍歷,生成對(duì)應(yīng)的FScroillObject對(duì)象并設(shè)置統(tǒng)一的布局方式(居中),初始化lastPostion和Postion (FScrollObject除了有RectTransfrom屬性外還有一個(gè)Postion屬性和lastPostion屬性,分別用來(lái)紀(jì)錄當(dāng)前(或正在前往)的位置和上一次位置信息)。用于后續(xù)的滑動(dòng)和位置校正。
滑動(dòng)監(jiān)聽(tīng)
滑動(dòng)監(jiān)聽(tīng)原理時(shí)通過(guò)檢測(cè)有觸摸或按下事件到滑動(dòng)條上時(shí)激活滑動(dòng)檢測(cè),當(dāng)移動(dòng)速度大于閾值時(shí)則判定當(dāng)前正在滑動(dòng)中,當(dāng)檢測(cè)到手指或鼠標(biāo)離開(kāi)滑動(dòng)條時(shí)結(jié)束滑動(dòng)監(jiān)聽(tīng)。
這里有一點(diǎn)需要注意,如果觸摸滑動(dòng)條觸摸到了item這會(huì)被攔截點(diǎn)擊事件,滑動(dòng)條的OnPointerDown事件不會(huì)被觸發(fā),解決辦法我開(kāi)始參考的是一篇滲透UI點(diǎn)擊事件文章 : 滲透UI點(diǎn)擊事件 ,但是使用后發(fā)現(xiàn)多個(gè)UI重疊時(shí)會(huì)出現(xiàn)棧內(nèi)存溢出,導(dǎo)致編輯器卡死,后來(lái)想了一個(gè)更簡(jiǎn)單的辦法,在FScrollItem中同樣實(shí)現(xiàn)點(diǎn)擊事件接口,當(dāng)被按下或抬起時(shí)直接將事件繼續(xù)傳遞給控制器的對(duì)應(yīng)事件。
public void OnPointerDown(PointerEventData eventData)
{
//傳遞事件
scrollPage.OnPointerDown(eventData);
//后續(xù)邏輯
lastPointer = eventData.position;
}
滑動(dòng)效果以及位置校正
滑動(dòng)時(shí)根據(jù)滑動(dòng)速度來(lái)控制item的移動(dòng),滑動(dòng)結(jié)束后使用速度線性遞減,當(dāng)速度低于一定閾值時(shí)執(zhí)行位置校正,其中彈動(dòng)的效果使用的時(shí)一個(gè)緩動(dòng)函數(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到滑動(dòng)方法中,然后通過(guò)滑動(dòng)方法使Item移動(dòng)到正常位置。
源碼已知存在的缺陷
如果你需要使用源碼你需要了解以下的缺陷,你可以對(duì)缺陷進(jìn)行修改或者修復(fù)
滑動(dòng)到邊界時(shí)沒(méi)有約束
錨點(diǎn)固定為居中,可能會(huì)與你的項(xiàng)目有沖突,也可能導(dǎo)致自適應(yīng)UI出現(xiàn)問(wèn)題
點(diǎn)擊判定過(guò)于僵硬(按下坐標(biāo)和抬起坐標(biāo)的Distance小于指定閾值則觸發(fā)點(diǎn)擊)
滑動(dòng)衰弱的時(shí)間和位置校正時(shí)間是固定的
其他
如果你有疑問(wèn),請(qǐng)到我的源碼地址下留言評(píng)論,該控件主要是技術(shù)上的研究,目前不夠完善,建議根據(jù)項(xiàng)目需要修改源碼。

