最近逛網站時看到一個簡單卻不錯的效果
使用滾輪想讓網頁上下移動時,畫面卻是左右移動,這是怎麼做到的呢?
其實網頁還是確實有在上下移動,只是螢幕被當前畫面佔滿,此時畫面配合滑鼠滾動來平移,造成一種滑鼠上下滾畫面卻左右動的錯覺
今天來試著寫這個功能並且元件化
1.原理
2.props
3.scroll函式
4.position的改變
1.原理
先來個示意圖
紅色的框框是螢幕的可視範圍,綠色的則是螢幕上會出現的畫面的全部,藍色的則是元件的最外層container(用途為製造滾動的空間)
當紅色區塊向下移動(滑鼠向下滾動)時綠色區塊也要漸漸左移,滾動到底部時綠色的區塊的末端要剛好抵住container的右邊
實際上如何計算往下滾動時畫面要左移多少?
橫向的綠線螢幕上出現的畫面總共的寬度,縱向的藍線代表container的高度,橫向的紅線代表傳入的元件左右移動的距離,縱向的紅線代表可視範圍上下移動的距離
這裡可以看得出來幾點
1.左右移動的全部距離為綠色的寬度減去一個螢幕寬
2.上下移動的距離為container的高度減去一個螢幕高
3.兩條紅線的長度要大於0才有我們期待的效果
(也就是這個區塊內要能滾動,而且要有超過一個螢幕寬的畫面供平移)
4.綠色區塊的寬度是出現的畫面總共的寬度,那藍色區塊的高度是怎麼 決定的?根據2的條件,藍色區塊的高度其實只需大於一個螢幕高就可以了,也就是要多高是可以自己設定的,根據上面那張圖可以看得出來,起點跟終點綠色區塊的位置是固定的,所以藍色高度會影響的,其實是綠色區塊的移動速度,如果太高就會動得很慢,太低則反之。
2.props
如果可以的話當然希望可以這樣用
想歸想,還是該來考慮一下預設條件
props1:橫著移動的區塊
這個錯覺的成立,就是看不到上下滾動的背景,所以首先要有一個高度是剛好一個螢幕高,寬度超過一個螢幕的畫面
由於要計算左右移動的幅度,所以需要獲取傳入的displayed區塊的寬度,所以在這裡可以在外面包覆一層wrapper並且設置ref
這樣便可以抓取元素的寬度
props2:container的高度
剛剛提到過這個值會影響滾動速度,應該要可以被設定,不設定時也該有預設值
一般人看到height時,基於css的習慣,應該會比較習慣100px,100vh之類的,所以這裡預設高度props的型態為string
預設值的話,我採取的作法是計算元素寬度時給予相對螢幕同比例的高度
例如說傳入的displayed區塊有2.5個螢幕寬高度就是2.5個螢幕高
並將container高度以style方式寫入
3.scroll事件
先設置一個state來儲存滾動進度,以及計算進度的函式
由於使用的區塊不一定會擺在網頁的頂端,所以progress=0的點應該要是容器的頂點,而抓取容器的頂點則需要使用方才設置的container的ref
這裡由於傳入props的的height是個字串無法直接用來運算,所以用container的ref來抓取height
滾動的進度即為目前畫面的scrollTop距離container頂點的高度除以總共能滾動的長度,這裡要注意上下progress=0~100的滾動範圍為container高度減去一個螢幕高
因此計算式為(目前螢幕的頂點-container頂點)/(container高度-一個螢幕高)
有了滾動進度後就可以讓displayed區塊左右移動,移動的距離共為displayed區塊寬度減去一個螢幕寬
在wrapper的style上計算該移動的百分比
這裡要注意displayed區塊能移動的距離並非displayed的寬度,尚須減去一個螢幕寬,也就是translateX(…)中的最大值並非100%
所以移動的百分比要乘上 (wrapperWidth — innerWidth) / wrapperWidth這個修正量
4.position的改變
在還沒完全滑進這區塊時,畫面應該是會隨著滾動條移動的狀態(static),直到完全滑入(即畫面佔滿螢幕後)後切換為fixed
這裡要注意當往下滑出這個元件的區塊時,不能像原本滑入前設定為position:static,因為會讓displayed區塊彈回上面去
此時該讓displayed區塊置於container的底部,這裡使用position:absolute+bottom:0
最後的結果如下
用codepen來跑跑看
觀念不難,主要是要搞清楚如何將滾動的距離轉換成畫面橫移的幅度
像筆者一開始寫的時候沒有畫圖,一直沒想到移動的距離需扣掉一個螢幕,導致寫出來的元件沒達到預期的效果(displayed畫面還沒屏移完就滾出這區塊之類的問題)
希望能給觀看的人當作參考!