這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 為了更好的瞭解原神角色,我模仿官網做了一個角色切換效果,在做的過程當中也總結了一些技術點。 為了讓大家更好的體驗,我相容了 PC 端和移動端,建議在 PC 端查看效果更佳。接下來就為大家簡單的分享一下! 話不多說,原神啟動!! 效果 ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
前言
為了更好的瞭解原神角色,我模仿官網做了一個角色切換效果,在做的過程當中也總結了一些技術點。
為了讓大家更好的體驗,我相容了 PC 端和移動端,建議在 PC 端查看效果更佳。接下來就為大家簡單的分享一下!
話不多說,原神啟動!!
效果
先看一下官網的效果: ys.mihoyo.com/main/charac…
我寫的真實效果:chenyajun.fun/#/ysRoleSwi…
技術點
一、底部角色橫向滾動效果
1、原理
外層容器:就是底部角色可視視窗。
內層容器: 用來存放角色頭像,點擊左右鍵或者角色需要滾動的容器。
滾動元素: 就是我們看到的一個個角色頭像。
首先我們使用外層容器
來包裹所有的角色信息,同時使外層容器overflow:hidden。
其次我們通過點擊左右按鍵或者任意角色來確定當前所處的 index。
最後通過固定角色寬度 ✖ index 來控制內層容器
的left值,從而移動內層容器
位置,最終顯示當前激活的角色頭像。(註:移動端實現效果不同,下麵單獨講解)
2、要求
要求1:如果處於最左(右)側的位置,點擊左右鍵或者前三個角色頭像不進行位移移動
,只顯示角色切換激活狀態。
要求2:處於最左側並且點擊左鍵需要角色容器第一個為倒數第六個,也就是最後一欄。相反,處於最右側並且點擊右鍵需要移動到第一個,也就是第一欄。
要求3:點擊中間部分永遠保持在角色容器的第三個
激活狀態。
3、代碼實現
大家可以根據HTML結構去理解代碼
<div class="role-outer" :style="{ width: isPC ? '830px' : '320px' }"> <template> <div class="left-arrow" :style="{ backgroundImage: `url(${leftSwitchArrow})` }" @click="lastPage"></div> <div class="right-arrow" :style="{ backgroundImage: `url(${rightSwitchArrow})` }" @click="nextPage"></div> </template> <div class="role-contener"> //內部容器 <div class="role-inner" :class="{ activeTranstion: isCloseTranstion }" :style="{ left: isPC ? moveDistancePC : moveDistanceMobile }" ref="element" > //角色 <template> <div v-for="(item, index) in yuRoleMes" class="role-item-pc" :style="{ backgroundPosition: $index === index ? '0 -132px' : '', backgroundImage: `url(${bottomRoleBac})`, }" :key="index" @click="handleRoleSwitchPC(index)" > <img class="item-avater" :src="item.roleAvatar" /> <p class="item-name" :style="{ color: $index === index ? '#000' : '#fff' }">{{ item.roleName }}</p> </div> </template> </div> </div> </div> </div>
可以看到我們通過 moveDistancePC
變數來控制內層容器的移動位移,接下來主要分析一下怎麼控制 moveDistancePC !
1.點擊操作
通過點擊上一頁、下一頁以及角色信息來改變索引 $index。
// 上一頁 function lastPage() { if ($index.value === 0) { $index.value = yuRoleMes.value.length - 1 return } $index.value-- } // 下一頁 function nextPage() { // 到最後一頁 if ($index.value === yuRoleMes.value.length - 1) { $index.value = 0 return } $index.value++ } // 點擊角色操作 function handleRoleSwitchPC(index) { isCloseTranstion.value = false $index.value = index }
當處於第一個
並且點擊左鍵
時,直接將索引賦值為最後一個角色。
if ($index.value === 0) { $index.value = yuRoleMes.value.length - 1 return }
當處於最後一個
並且點擊右鍵
時,直接將索引賦值為0到第一個。
// 到最後一頁 if ($index.value === yuRoleMes.value.length - 1) { $index.value = 0 return }
2.索引控制移動位移
通過索引*角色寬度
來進行位置的移動,不過需要進行界限判斷,144為寬度(110px)+margin-right(34px)。
// 每次點擊移動距離 const moveDistance = computed(() => { const firstThree = $index.value < 3 if (firstThree) { return 0 } const lastThree = $index.value > yuRoleMes.value.length - 4 if (!lastThree) { return ($index.value - 2) * -144 } return (yuRoleMes.value.length - 6) * -144 }) //對容器進行位移賦值 const moveDistancePC = computed(() => { return moveDistance.value + 'px' })
情況一:
如果$index處於前三個,那麼位置不變永遠為0。
const firstThree = $index.value < 3 if (firstThree) { return 0 }
情況二:
如果$index處於中間部分,永遠保持為當前的第三個激活即可。
($index.value - 2) 意思為如果直接取 $index 那麼角色位置會在第一個,保持在第三個就往後面移動兩位,故減去兩個即可。
const lastThree = $index.value > yuRoleMes.value.length - 4 if (!lastThree) { return ($index.value - 2) * -145 }
情況三:
如果$index處於後三個,那麼位置不變,永遠處於倒數第六個。
return (yuRoleMes.value.length - 6) * -145
二、背景圖片放大縮小切換效果
我們可以看到每個場景下會有兩張背景圖片在不斷的切換,第一張結束之後第二張出現,兩張圖片迴圈播放,若隱若現。
代碼實現
這一部分是直接模仿的官方的樣式,直接說一下實現原理!
HTML
<!-- 背景-兩張切換 --> <div class="background-wrapper"> <!-- 第一張背景 --> <div class="role-background role-bg1" :style="{ backgroundImage: outerTwoBackground[0], }" ></div> <!-- 第二張背景 --> <div class="role-background role-bg2" :style="{ backgroundImage: outerTwoBackground[1], }" ></div> </div>
需要使第一張背景永遠處於存在且向外擴大的狀態,通過關鍵幀動畫
控制第二張的顯示隱藏,從而就達到了兩張照片的交替顯示隱藏,是不是很巧妙?
相關css代碼:
// 第一張永遠存在進行擴張 .role-bg1 { animation: breath 80s infinite linear; opacity: 1; } // 第二張也在擴張,只不過每次從顯示到隱藏需要15秒 .role-bg2 { animation: bg-change 15s infinite linear, breath 80s infinite linear; opacity: 0; } // 用於第二張的顯示隱藏 @keyframes bg-change { 48% { opacity: 0; } 50% { opacity: 1; } 98% { opacity: 1; } 100% { opacity: 0; } } // 用於向外擴張效果 @keyframes breath { 0% { transform: scale(1); } 50% { transform: scale(1.2); } 100% { transform: scale(1); } }
三、背景角色切換效果
到我們在切換角色的時候,第一個角色會移出,同時下一張照片會移入,並且伴有動畫,這個效果也是css實現的。
1、原理
我們將所有的圖片起初都定位到外面,預設顯示第一張,而當我們切換照片時,我們設置需要顯示的照片的 right
值,同時設置透明度
,在這個過程中增加過渡動畫
即可。
2、實現代碼
HTML結構
<!-- 角色背景 --> <div class="role-wrapper"> <div class="role-box"> <img v-for="(item, index) in yuRoleMes" class="role-picture" :class="[index === $index ? show-background-pc' : 'hide-background']" :key="index" :src="item.roleBackground" /> </div> </div>
我設置了兩個類show-background-pc,hide-background前者用來顯示切換的照片,index === $index時顯示點擊的照片,否則調整right值直接隱藏,兩個都加上過渡動畫,這樣顯示隱藏都會有效果了。
.show-background-pc { right: 0; opacity: 1; transition: all 0.3s; } .show-background-mobile { right: 445px; opacity: 1; transition: all 0.3s; }
移動端
1、底部角色滑動效果
2、點擊和觸摸移動事件衝突
我在做的過程中發現點擊事件和觸摸事件
發生了衝突,就是我既要滑動這個容器又要進行點擊。
我採取的辦法是結束位置減去起始位置是否為0
,如果為0則視為點擊事件!
moveDistanceX.value = endX.value - startX.value // 如果是點擊滑動 if (moveDistanceX.value === 0) { isClickMobile(e) return }
如果為true則處理點擊事件,否則處理移動事件即可,如果是點擊事件的話,邏輯和 PC 相似,這裡不再重覆講解,主要說一下滑動移動。
3、觸摸移動角色容器
我們在觸摸滑動容器時,容器需要跟著一起滑動
。
這裡有一些變數,大家可能需要看一下
const isCloseTranstion = ref(false) //控制是否顯示動畫效果 const startX = ref(0) //記錄開始位置 const endX = ref(0) //記錄結束位置 const moveDistanceX = ref(0) //按下抬起滑動距離 const recordLastMove = ref(0) //記錄上次滑動的距離,用於下次累加 const moveDistanceM = ref(0) //最終移動的距離
1.開始觸摸
// 觸摸開始 function handleTouchStart(e) { isCloseTranstion.value = true // 開始移動 關閉動畫 startX.value = e.touches[0].pageX || e.changedTouches[0].pageX }
2.觸摸移動
觸摸移動的話,下麵的容器需要實時跟著一起移動,也就是我們移動的位移需要實時的賦值上去,並且要累加上上次
的位置,比如第一次移動到10,下次移動就是從10的基礎上進行移動。
//最終移動的距離賦值為容器left值即可 const moveDistanceMobile = computed(() => { return moveDistanceM.value + 'px' }) // 觸摸移動 function handleTouchMove(e) { e.preventDefault() isCloseTranstion.value = true // 開始移動 關閉動畫 moveDistanceX.value = (e.changedTouches[0].pageX || e.touches[0].pageX) - startX.value // 計算移動距離 moveDistanceM.value = recordLastMove.value + moveDistanceX.value }
3.觸摸抬起
容器的最終位置這裡就需要分情況進行處理了,滿足相應的條件處理相應的邏輯即可。
情況一:
如果只有一欄照片或者直接往右邊移動,那就只進行實時同步移動,只不過移動結束還恢復到原來位置即可。
// 如果照片小於五張不進行位移移動 if (yuRoleMes.value.length < 5) { moveDistanceM.value = 0 recordLastMove.value = 0 return }
情況二:
已經滑到最後一欄繼續左滑,這時已經超出了最大界限,將最終位置定格在最大界限的位置
。
// 最後一欄的位置 const rightMaxValue = (yuRoleMes.value.length - 5) * -64 //在最後一欄繼續往左邊滑動 if (moveDistanceM.value < rightMaxValue) { moveDistanceM.value = rightMaxValue recordLastMove.value = rightMaxValue return }
情況三:
除了以上情況,也就是在中間正常滑動,我們進行賦值即可,使用 Math.round 來保證我們移動的都是每個角色的整數倍
即可。
// 其他情況 moveDistanceM.value = recordLastMove.value + Math.round(moveDistanceX.value / 64) * 64 recordLastMove.value = Math.round(moveDistanceM.value / 64) * 64
總結
聲明:以上所有信息材料均來之於原神官網ys.mihoyo.com/main/charac…
上面我將功能進行了分開描述,PC 端和移動端最終效果不一樣,所以代碼實現也不一樣,但其實實現邏輯相似,只不過需要對不同的情況進行處理。
另外就是每個情況需要考慮細緻,我在寫的過程中,總會有落下的問題,也就是測試用例不太全面,如果大家有發現不對的地方,可以在下方進行留言,我看到會進行及時更改的。
寫作不易,你的贊就是我最大的動力,覺得寫的不錯的,可以給點個贊呢~
源碼:
全部源碼已經同步上傳到了 GitHub
覺得寫的不錯了,可以給作者點一下star⭐
GitHub:chenyajun-create/ysRoleSwitch (github.com)