スクロールをアニメーション化するためのきちんとしたDIYソリューション


Update 4.10.19 A recent comment below blew my mind name dropping the Intersection Observer API. This was essentially built for exactly what this article's about, allowing you to fire a callback whenever an element is in a certain position on the screen! The latter parts of the article about centralizing logic with a store are still totally applicable to this, but intersection observers clean up things a great deal over requestAnimationFrame while also being more performant. However, note there is no IE support and Safari support only just recently. Alrighty, enjoy the rest of this post 😊


インスピレーションのためのワールドワイドウェブの周りを見て、私は多くのサイトを愛している私は好きな多くのサイトは、“私は特定の要素にスクロールするたびに”アニメーションを明らかに少し組み込まれている.微妙な、これらの余分なタッチは、ページをはるかに静的でより敏感な感じてください.問題は…だ.これを実行する最良の方法は何ですか?
ちょうどcodepen例を通してスクロールして、私は見つかりましたtime and time again その人々は彼らのためにそれを扱うことができるすべてのライブラリをキャッチするために達している.そこにスクロールをアニメーション化のための数え切れないほどのオプションがあり、最も一般的な名前であることAOS . 私自身が望んでいた🌶 いくつかのスクロールアニメーションで私のサイトを開くので、私は当然このためにAOSのライブラリになると思いました.しかし、私の実装がますます専門的になったので、私はそれをスクロールするまでこのフレームをロードしないようにしますか?私は不思議に思った.
私は、ちょうど私自身を建設することができません?

たぶん。見ましょう


ちょうど基本的な、バニラJSとフレームワークから始まって、アプローチは実はかなり単純です.必要なのはonScroll ハンドラーと我々が実際にアニメーション化したいどんな要素でも.基本から始めて、特定のIDの要素を持っていると言います.ご存知のように、私たちは、DOMonScroll イベントは、私たちの要素が画面上にあるときに、いつでも、スクロールしてください.
window.onScroll = ({target}) => {
    const element = document.getElementById('animate-me')
    const elementTop = element.getBoundingClientRect().top
    if (elementTop < document.body.clientHeight) {
        element.classList.add('scrolled-to')
    }
}
入れ子になったオブジェクトの属性はいくつかあります.まず、要素の上部が画面上にあるピクセル値を取得する必要があります.これを見つけるいくつかの有効な方法がありますが、迅速なインターネット検索を通してgetBoundingClientRect() はブラウザ全体で最も信頼性の高い方法です.
これにより、ドキュメントの固定高さと比較する必要があります.これは基本的にブラウザウィンドウの高さですclientHeight . 我々の要素の上部がこの高さより少ないならば、それの一部はスクリーンになければなりません.今、私たちはちょうど我々のキーフレームを我々のCSSに加えます.animate-me.scrolled-to そして、我々は行くのが良いです👍

大丈夫、私たちは基本的にMDNヘルプページの例を再現しました。


そのようにして、現実世界でこれを実際に利用しましょう.まず、あなたが好奇心を持って、投げたならばconsole.log あなたがスクロールホイールをひねったときはいつでも、あなたはたぶんこれを得たでしょう.

これは、すべてのスクロールイベントを実際に分析する方法を反映しています.我々はスクロールするすべてのピクセルに対して関数を実行しており、この機能をより堅牢にし始めるので、遅れや吃音を引き起こすことができます.
これを解決する一つの方法はrequestAnimationFrame コールバックが発生したときに決定する.これは別のウィンドウレベル関数で、ブラウザのコールバックを待ちます.それはあなたのスクロールの経験を平滑化せずに、これらの機能を実行する準備ができていると感じたとき、それはそれらをオフに発射されます.ありがたいことに、このアプローチはrelatively high browser adoption . 我々が必要とするすべては、我々のあたりのラッパーですonScroll ハンドラrequestAnimationFrame , に沿ってboolean フラグを実行します.
let waitingOnAnimRequest = false

const animChecker = (target) => {
    // Our old handler
    const element = document.getElementById('animate-me')
    const elementTop = element.getBoundingClientRect().top
    if (elementTop < document.body.clientHeight) {
        element.classList.add('scrolled-to')
    }
}

window.onScroll = ({target}) => {
    if (!waitingOnAnimRequest) {
        window.requestAnimationFrame(() => {
            animChecker(target)
            waitingOnAnimRequest = false
        })
        waitingOnAnimRequest = true
    }
}
すごい!現在、我々の呼び出しは少しより効率的でなければなりません.しかし、より重要な問題に対処しましょう:我々はどのように我々はスクロールでアニメーション化することがありますドキュメント内の任意の要素のために働くか?
確かに、必要な各IDやクラス名にコールバックを追加し続けるのは意味がないので、集中配列を作成するだけでなく、すべての要素セレクタを追加することができます.

いくつかのループの時間


この追加はかなり簡単なレバレッジですquerySelectorAll . ただ、アニメーション化する必要があるすべてのセレクタを持つグローバル配列を作成します.
let animationSelectors = ['#ID-to-animate', '.class-to-animate']

const animChecker = (target) => {
    // Loop over our selectors
    animationSelectors.forEach(selector => {
        // Loop over all matching DOM elements for that selector
        target.querySelectorAll(selector).forEach(element => {
            const elementTop = element.getBoundingClientRect().top
            if (elementTop < bodyHeight) {
                 element.classList.add('scrolled-to')
            }
        })
    })
}
...
今我々のスクロールアニメーションチェッカーは、我々はそれをスローする任意の要素を処理することができます!

きちんと!私はXフレームワークを使いますが、Yのために使うことができないと思います


今すぐそこにそれを保持します.私は、みんなの道具がquirksのセットを持っているのを理解します、それで、それらのいくつかに対処しようとしましょう.

私はコンポーネントシステムを使用するので、どのようにこのロジックを集中化するのですか?


クラスの簡潔なリストとIDを私たちがアニメーション化したいと思っているのはうれしいですが、コンポーネント、特にスコープされたCSSソリューションで、このリストを読みやすく拡張できるようにするのは難しくなります.
ありがたいことに、この解決法はただ一つの配列を必要としているので、それぞれのコンポーネントがアニメーション化したいDOMセレクタで更新することができます.私はSveletjs上に構築された最近のプロジェクトでこれを使用します.更新するanimationSelectors , 私はちょうど店としてそれを作成しました.
export const animationTriggers = writable({})
... のいずれかのコンポーネントからクラス名を追加します.
import { animationTriggers } from '../stores'

onMount(() => {
    animationTriggers.set([
      ...$animationTriggers,
      '.wackily-animated-class',
      '#section-id',
    ])
  })
これは同様にreduxのような一般的なグローバル州のソリューションのために動作し、同様にコンテキストを反応させる.Reduxの実装はミドルウェアによって大きく異なりますので、マルチファイルの例をここで割ります.
// store.js
...
const AnimationTriggerContext = React.createContext()

class StoreWrapper extends React.Component {
    constructor() {
        super()
        this.state = {
            selectors: []
        }
    }
    render() {
        return (
            // create a provider to wrap our components in at the parent level
            <AnimationTriggerContext.Provider value={{
                // make our array of selectors accessible from all children
                selectors: this.state.selectors,
                // add a helper function to update our array
                addSelector: (selector) => {
                    this.setState({
                        selectors: [...this.state.selectors, selector],
                    })
                }
            }}>
                {this.props.children}
            </AnimationTriggerContext.Provider>
        )
    }
}

//childManyLayersDeep.js
...
class Child extends React.Component {
    componentDidMount() {
        this.context.addSelector('special-class')
    }
    render() {
        return <div className="special-class"></div>
    }
}

//wrap the child with a 'withContext' so it can be accessed
export default withContext(Child)
当然、この方法はVuejs、RXJSオブザーバブルに拡張可能です、そして、基本的に他のどこで、あなたは世界的な店を使うかもしれません.

閉じるこの動画はお気に入りから削除されています。しかし、私は基本的なCSSセレクタを使用できません。これらはコンポーネントです!


OKフェアポイントこれはほとんどのコンポーネントベースのフレームワークで物事を複雑にすることができます.最も簡単な妥協は、DOM問い合わせを避けることができるように、クラス名の代わりに私たちの「add」機能で要素自体へのリファレンスを渡すことです.全体的に、謙虚さref クラスまたはIDセレクタではなく、反応またはVueの属性は、これのためにトリックをするべきです.

また、私はJSでCSSを使用しているし、むしろクラスの名前をアニメーション化をチェックしないでください。何が私のオプションですか?


これは最近ではかなり一般的なパターンであり、クラス名の切り替えでそれを渡すpropに依存する傾向があります.ありがたいことに、我々のストアに基づいてこれらの小道具を把握する場所のロジックのほとんどすべてを持っている.我々が必要とするすべては、我々が通るセレクタの余分のオブジェクト特質ですscrolledTo フラグを指定します.
このために、我々は我々の店にちょうどストリング(またはREF)からオブジェクトに行くために加えるものを修正します..
{
    selector: 'class-name',
    scrolledTo: false,
}
...スクロールしたときにフラグを更新します.
const animChecker = (target) => {
    ...
        if (elementTop < bodyHeight) {
            animationTriggers[currentIndex].scrolledTo = true
        }
    ...
}
今、我々はanimationtriggersの配列を購読することができます(または、あなたの実装に従い、文脈をつかむ)、そしてscrolledTo コンポーネントのスタイルへのプロップとしてのフラグ.

要約する


それで、あなたが抗議する前に、あなたがこの記事を読むために取った時に働く図書館であなたの大好きなアニメーションを得たことができました.私はそれを得る.しかし、私は楽しい小さな挑戦として自分自身を構築するためにこの機能を取るとどのように洗練された、効率的なDOMリスナーを理解するための超便利です.また、あなたのパッケージで心配する1つの以下の依存関係を持つことを意味しますので、新しい機能を追加するための変更と柔軟性の多くを破る!
アクションでこの解決策を見るために、それは我々のジョージア技術クラブのホームページの上ですべての場所で使われます:ゴールデン群れゲーム.サイトを訪問するhttps://gsg.surge.sh or the repo 我々のスクロールアニメーションがフードの下で働く方法を見るために.

少し何かを学ぶ?


スリック.あなたがそれを逃した場合はmy "web wizardry" newsletter このような知識ナゲットを探索する!
このことは"first principles" Web開発言い換えれば、すべてのJankyブラウザAPI、曲げられたCSS規則、およびすべてのWebプロジェクトをティックにするセミアクセス可能なHTMLは何ですか?あなたがフレームワークを越えて行くことを探しているならば、これはあなたのためです🔮
Subscribe away right here . 私はいつも教えて、決してスパム❤️