Swift 实现卷动视差效果(Parallax Scrolling)

2,753 阅读4分钟

<img class="alignnone wp-image-646 size-full" src="https://ios.devdon.com/wp-content/uploads/2017/03/Parallax-Gallery.png" alt="" width="1024" height="618" srcset="https://ios.devdon.com/wp-content/uploads/2017/03/Parallax-Gallery.png 1024w, https://ios.devdon.com/wp-content/uploads/2017/03/Parallax-Gallery-300x181.png 300w, https://ios.devdon.com/wp-content/uploads/2017/03/Parallax-Gallery-768x464.png 768w" sizes="(max-width: 1024px) 100vw, 1024px" />

在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)
    }

推薦與參考