在Web領域我們已經經常能夠看到捲動視差(Parallax Scrolling)的效果了,尤其是在一些著陸頁(Landing Page),經常會用這種效果。
圖片類的應用除了瀑布流的排版(Masonry Layout)以外,這個也是個不錯的排版效果,接下來讓我們來實現一下。
捲動視差效果
動手做捲動視差動畫
視差效果原理
<img class="alignnone wp-image-659" src="https://ios.devdon.com/wp-content/uploads/2017/03/parallax-life-1.png" alt="" width="567" height="266" srcset="https://ios.devdon.com/wp-content/uploads/2017/03/parallax-life-1.png 862w, https://ios.devdon.com/wp-content/uploads/2017/03/parallax-life-1-300x141.png 300w, https://ios.devdon.com/wp-content/uploads/2017/03/parallax-life-1-768x360.png 768w" sizes="(max-width: 567px) 100vw, 567px" />
<img class="alignnone wp-image-660" src="https://ios.devdon.com/wp-content/uploads/2017/03/parallax-life-2.png" alt="" width="565" height="265" srcset="https://ios.devdon.com/wp-content/uploads/2017/03/parallax-life-2.png 862w, https://ios.devdon.com/wp-content/uploads/2017/03/parallax-life-2-300x141.png 300w, https://ios.devdon.com/wp-content/uploads/2017/03/parallax-life-2-768x360.png 768w" sizes="(max-width: 565px) 100vw, 565px" />
想像一下,現在我們的「眼睛」透過一面「窗戶」來看「風景」,當至少其中一個進行上下移動的時候,可以看到風景的部分就會產生變化。
<img class="alignnone size-full wp-image-658" src="https://ios.devdon.com/wp-content/uploads/2017/03/scrolling-parallax-1.png" alt="" width="800" height="277" srcset="https://ios.devdon.com/wp-content/uploads/2017/03/scrolling-parallax-1.png 800w, https://ios.devdon.com/wp-content/uploads/2017/03/scrolling-parallax-1-300x104.png 300w, https://ios.devdon.com/wp-content/uploads/2017/03/scrolling-parallax-1-768x266.png 768w" sizes="(max-width: 800px) 100vw, 800px" />
而為了在手機畫面上能實現這樣的效果,我們需要準備一張比Cell還大的一張圖片
<img class="alignnone size-large wp-image-657" src="https://ios.devdon.com/wp-content/uploads/2017/03/parallax-3-1024x316.png" alt="" width="640" height="198" srcset="https://ios.devdon.com/wp-content/uploads/2017/03/parallax-3-1024x316.png 1024w, https://ios.devdon.com/wp-content/uploads/2017/03/parallax-3-300x92.png 300w, https://ios.devdon.com/wp-content/uploads/2017/03/parallax-3-768x237.png 768w, https://ios.devdon.com/wp-content/uploads/2017/03/parallax-3.png 1402w" sizes="(max-width: 640px) 100vw, 640px" />
屏幕相當於我們的眼睛,而屏幕是不能移動的,所以我們可以通過移動Cell或者ImageView來產生視差效果。
而ImageView大於Cell的這個30px(140px – 110 px),其實就是視差範圍,畢竟我們希望不出現圖片以外的內容。
實現步驟
<img class="alignnone size-large wp-image-675" src="https://ios.devdon.com/wp-content/uploads/2017/03/UICollectionView-Parallax-Scrolling-1024x624.png" alt="" width="640" height="390" srcset="https://ios.devdon.com/wp-content/uploads/2017/03/UICollectionView-Parallax-Scrolling-1024x624.png 1024w, https://ios.devdon.com/wp-content/uploads/2017/03/UICollectionView-Parallax-Scrolling-300x183.png 300w, https://ios.devdon.com/wp-content/uploads/2017/03/UICollectionView-Parallax-Scrolling-768x468.png 768w, https://ios.devdon.com/wp-content/uploads/2017/03/UICollectionView-Parallax-Scrolling.png 1275w" sizes="(max-width: 640px) 100vw, 640px" />
假設我們使用的是UICollectionView,希望在滑動的時候讓cell中的圖片呈現視差捲動的效果。
我們以View的中心為標準,
- 當Cell在CollectionView的中間時,ImageView的中心也在Cell的中心。
- 當Cell在CollectionView的頂部時,ImageView也在頂部。
- 當Cell在CollectionView的底下時,ImageView也在底下。
UIScrollViewDelegate
要達到這樣的效果,首先我們需要實現UIScrollViewDelegate的方法,當畫面滑動的時候通知所有在畫面上的cell。
// MARK: UIScrollViewDelegate
extension HomeViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// 當UICollectionView被拖動的時候,通知出現在畫面上的cell
for cell in aCollectionView.visibleCells {
if let homeCell = cell as? HomeCell {
homeCell.cellOnTableView(collectionView: aCollectionView, didScrollOnView: view)
}
}
}
}
Cell
在Cell中提供一個方法,可以計算出cell位於View(CollectionView的superView)的座標系統中的距離中心點的位移情況。
根據這個位移的情況來計算視差的的移動比例,進而改變現在imageView的frame。
func cellOnTableView(collectionView:UICollectionView, didScrollOnView view:UIView) {
// 取得cell在collectionView中,相對view座標系統的frame
let rectInSuperview = collectionView.convert(self.frame, to: view)
// 位移相對中心點的距離
let distanceFromCenter = view.frame.height/2 - rectInSuperview.minY
// 圖片大於cell高度的部分,也就是視差的高度
let parallaxHeight = imageView.frame.height - frame.height
// 以cell相對view中心點移動的距離,來計算視差的移動距離
let move = (distanceFromCenter / view.frame.height) * parallaxHeight
// 先將imageView向上移動一半的視差高度(difference/2),然後根據move程度變化y的位置
var imageRect = imageView.frame
imageRect.origin.y = -(parallaxHeight/2) + move
// 給予imageView一個新的frame,達到視差效果。
imageView.frame = imageRect
}
而為了讓畫面第一次呈現時就讓imageView擁有正確的frame,我們在畫面第一次顯示的時候就觸發cell計算一次frame。
override func viewDidAppear(_ animated: Bool) {
// 畫面第一次出現時就先計算cell中的offset
scrollViewDidScroll(aCollectionView)
}
推薦與參考
- 歡迎分享本文「Swift 實現視差捲動效果(Parallax Scrolling)」 – https://ios.devdon.com/archives/643
- 進一步瞭解UIView的座標系統及其轉換方法。
- 本文在Gihub上的SourceCode – 「Swift實現捲動視差效果」