これがどうやって来たのかわかりません。しかし、それは物語です。この記事では、コンセプトを驚かせることについての詳細です。これは、アニメーションについて別の方法で考えるのに役立つでしょう。この特定の例は、無限のスクロール、特にそれらのいずれかを複製することなく、カードのデッキ用の「完璧な」無限のスクロールを備えていることがあります。
なぜ私はここにいるのですか?まあ、これはすべてツイートから始まりました。レイアウトとサイドスクロールコンテンツについて考えさせられたツイート。
私はそのコンセプトを取り、私のサイトでそれを使用しました。そして、それはまだ執筆時点で動作しています。
それから私はギャラリーの景色とサイドスクロールの概念についてもっと考えるようになりました。私たちはライブストリームに飛び乗り、古いリンゴの「カバーフロー」パターンのようなものを作ろうとすることにしました。覚えていますか?
これを作ることについての私の最初の考えは、これを作るので、上記のデモのように、「プログレッシブエンハンスメント」を使用する方法でJavaScriptなしで機能します。私はグリーンソックとスクロールトリガーをつかみました、そして私たちは行きました。私はその仕事からかなり失望しました。私は何かを持っていましたが、無限のスクロールを得ることができませんでした。 「次」と「前」のボタンはボールをプレーしたくありませんでした。ここで見ることができ、水平スクロールが必要です。
そこで、Greensockフォーラムに新しいスレッドを開きました。私は真剣な学習に自分自身を開放しようとしていたことをほとんど知りませんでした!ボタンで問題を解決しました。しかし、私であるため、私は何か他のものが可能かどうか尋ねなければなりませんでした。無限のスクロールを行う「きれいな」方法はありましたか?ストリームで何かを試しましたが、運はありませんでした。私は興味がありました。 Scrolltriggerリリース用に作成したこのペンで使用されたようなテクニックを試しました。
最初の答えは、やるのはちょっと難しいということでした:
スクロール上の無限のことについての難しい部分は、スクロールバーが制限されている一方で、あなたが望んでいる効果はそうではないことです。したがって、実際に実際のスクロール位置を使用する代わりに、このデモ(Scrolltigger Demosセクションにある)のようなスクロール位置をループするか、スクロール関連のナビゲーションイベント(ホイールイベントなど)に直接フックする必要があります。
私はそれが事実であると考え、それを「As-and」に残して喜んでいた。数日が経過し、ジャックは私がそれを掘り始めたときに私の心を吹き飛ばした返事を落としました。そして今、それを経験した後、私はあなたとテクニックを共有するためにここにいます。
GSAPで見落とされることが多いことの1つは、ほとんど何でもアニメーション化できることです。これは、アニメーション、つまり何かの実際の物理的な動きについて考えるとき、視覚的なものが思い浮かぶものだからです。私たちの最初の考えは、そのプロセスをメタレベルに引き継いで、一歩後退からアニメーション化することではありません。
ただし、アニメーションが大規模に機能することを考えてから、レイヤーに分解します。たとえば、漫画を演奏します。漫画は作曲のコレクションです。各構成はシーンです。そして、YouTube上にあるかどうか、テレビのリモコンなどを使用するかどうかにかかわらず、リモコンを使用して、その構成のコレクションをスクラブする力があります。何が起こっているかにはほぼ3つのレベルがあります。
これは、さまざまな種類の無限ループを作成するために必要なトリックです。これがここでの主な概念です。タイムラインでタイムラインのプレイヘッド位置をアニメーション化します。そして、スクロール位置でそのタイムラインをスクラブすることができます。
それが混乱しているように聞こえても心配しないでください。私たちはそれを分解するつもりです。
例から始めましょう。いくつかのボックスを左から右に移動するTweenを作成します。ここにあります。
左から右に進み続ける10個のボックス。 Greensockでは非常に簡単です。ここでは、アニメーションを維持するために繰り返して繰り返します。しかし、各反復の開始時にギャップがあります。また、Staggerを使用してムーブメントを展示しています。これは、私たちが続行するにつれて重要な役割を果たすものです。
gsap.fromto('。box '、{ Xpercent:100 }、{ Xpercent:-200、 よろめ:0.5、 期間:1、 繰り返し:-1、 使いやすさ:「なし」、 })
今、楽しい部分が来ます。トゥイーンを一時停止し、変数に割り当てましょう。次に、それを再生するトゥイーンを作成しましょう。これを行うことができます。これにより、Tweenの合計時間をTweenで行うことができます。これにより、TweenのPlayhead Tweenを取得または設定することができます。
const shift = gsap.fromto('。box '、{ Xpercent:100 }、{ 一時停止:本当、 Xpercent:-200、 よろめ:0.5、 期間:1、 繰り返し:-1、 使いやすさ:「なし」、 }) const duration = shift.duration() gsap.to(shift、{ 合計時間:期間、 繰り返し:-1、 期間:期間、 使いやすさ:「なし」、 })
これは私たちの最初の「メタ」トゥイーンです。まったく同じように見えますが、別のレベルのコントロールを追加しています。元のレイヤーに影響を与えることなく、このレイヤーの物事を変更できます。たとえば、Tweenの容易さをPower4.inに変更できます。これにより、アニメーションが完全に変わりますが、基礎となるアニメーションに影響することはありません。私たちはフォールバックで自分自身を保護しています。
それだけでなく、タイムラインの特定の部分のみを繰り返すことを選択する場合があります。このように、他の人と一緒にそれを行うことができました:
そのためのコードはこのようなものです。
gsap.fromto(shift、{ 合計時間:2、 }、{ 合計時間:期間-1、 繰り返し:-1、 期間:期間、 使いやすさ:「なし」 })
これがどこに向かっているのかわかりますか?そのトゥイーンを見てください。ループを続けていますが、数字は各繰り返しをひっくり返します。しかし、ボックスは正しい位置にあります。
元の例に戻ると、各繰り返しの間に顕著なギャップがあります。
ここにトリックがあります。すべてを解き放つ部分。完璧なループを構築する必要があります。
シフトを3回繰り返すことから始めましょう。繰り返しを使用することに等しい:3。Tweenから繰り返し:-1を削除した方法に注意してください。
const getshift =()=> gsap.fromto('。box '、{ Xpercent:100 }、{ Xpercent:-200、 よろめ:0.5、 期間:1、 使いやすさ:「なし」、 }) const loop = gsap.timeline() .add(getshift()) .add(getshift()) .add(getshift())
最初のTweenをTweenを返す関数に変え、3回新しいタイムラインに追加しました。そして、これは私たちに以下を与えます。
わかりました。しかし、まだギャップがあります。これで、これらのトゥイーンを追加および配置するための位置パラメーターを持ち込むことができます。シームレスにしたい。それは、前のものが終了する前に各セットの各セットを挿入することを意味します。これは、ずらして要素の量に基づいた値です。
const stagger = 0.5 //シフトのトゥイーンで使用されています const boxes = gsap.utils.toarray('。box ') const loop = gsap.timeline({ 繰り返し:-1 }) .add(getshift()、0) .add(getshift()、boxes.length * stagger) .add(getshift()、boxes.length * stagger * 2)
タイムラインを更新して繰り返して視聴すると(それが物事にどのように影響するかを確認するためによろめきを調整しながら)…
真ん中に「シームレスな」ループを作成する窓があることに気付くでしょう。私たちが時間を操作した以前のスキルを思い出しますか?それがここで行う必要があることです。ループが「シームレス」である時間のウィンドウをループします。
ループのそのウィンドウを通して合計時間をトゥーイーンすることを試みることができます。
const loop = gsap.timeline({ 一時停止:本当、 繰り返し:-1、 }) .add(getshift()、0) .add(getshift()、boxes.length * stagger) .add(getshift()、boxes.length * stagger * 2) gsap.fromto(loop、{ 合計時間:4.75、 }、 { TotalTime: '= 5'、 期間:10、 使いやすさ:「なし」、 繰り返し:-1、 })
ここでは、合計時間を4.75からTweenと言って、サイクルの長さを追加しています。サイクルの長さは5です。それがタイムラインの中央の窓です。 GSAPのnifty =を使用してそれを行うことができます。
そこで何が起こっているのかを消化してください。これは、頭を包むのが最も難しい部分かもしれません。タイムラインで時間の窓を計算しています。視覚化するのはちょっと難しいですが、私は試しました。
これは、手に12秒かかる時計のデモです。それは繰り返しで無限にループされています:-1そして、私たちは特定の期間で特定の時間ウィンドウをアニメーション化するために使用します。時間ウィンドウを減らして2と6を言うと、持続時間を1に変更すると、手は2時から6時まで繰り返します。しかし、私たちは基礎となるアニメーションを決して変えませんでした。
値を構成して、それが物事にどのように影響するかを確認してください。
この時点で、ウィンドウの位置の式をまとめることをお勧めします。また、各ボックスが移行するためにかかる期間に変数を使用することもできます。
const持続時間= 1 const Cycle_duration = boxes.length * stagger const start_time = cycle_duration(duration * 0.5) const end_time = start_time cycle_duration
3つの積み重ねられたタイムラインを使用する代わりに、位置を計算する必要がないという利点を得るために3回の要素をループすることができます。しかし、これを3つの積み重ねられたタイムラインとして視覚化することは、コンセプトを把握するためのきちんとした方法であり、主なアイデアを理解するのに役立つ良い方法です。
実装を変更して、最初から1つの大きなタイムラインを作成しましょう。
const stagger = 0.5 const boxes = gsap.utils.toarray('。box ') const loop = gsap.timeline({ 一時停止:本当、 繰り返し:-1、 }) const shifts = [... boxes、... boxes、... boxes] shifts.foreach((box、index)=> { loop.fromto(box、{ Xpercent:100 }、{ Xpercent:-200、 期間:1、 使いやすさ:「なし」、 }、index * stagger) })
これは簡単にまとめられ、同じウィンドウを提供します。しかし、数学について考える必要はありません。次に、3セットのボックスをループして、スティガーに応じて各アニメーションを配置します。
私たちがよろめきを調整すれば、それはどのように見えますか?箱をより近くに押しつけます。
しかし、今では合計が出ているので、窓が壊れています。ウィンドウを再計算する必要があります。以前に計算した式をプラグインするのに良い時期です。
const持続時間= 1 const Cycle_duration = stagger * boxes.length const start_time = cycle_duration(duration * 0.5) const end_time = start_time cycle_duration gsap.fromto(loop、{ TotalTime:start_time、 }、 { TotalTime:end_time、 期間:10、 使いやすさ:「なし」、 繰り返し:-1、 })
修理済み!
開始位置を変更したい場合は、「オフセット」を導入することもできます。
const stagger = 0.5 const offset = 5 * stagger const start_time =(cycle_duration(stagger * 0.5))offset
今、私たちの窓は別の位置から始まります。
それでも、これは両端でこれらの厄介なスタックを提供するので、これは素晴らしいことではありません。その効果を取り除くには、ボックスの「物理的な」ウィンドウについて考える必要があります。または、それらがどのようにシーンを出入りするかを考えてください。
document.bodyを例のウィンドウとして使用します。ボックスのトゥイーンを更新して、ボックスが出口で入力して下降してスケールアップする個々のタイムラインにしましょう。ヨーヨーを使用して繰り返すことができます:1は、入場と出口を達成します。
shifts.foreach((box、index)=> { const box_tl = gsap .timeline() .fromto( 箱、 { Xpercent:100、 }、 { Xpercent:-200、 期間:1、 使いやすさ:「なし」、 }、0 )) .fromto( 箱、 { スケール:0、 }、 { スケール:1、 繰り返し:1、 ヨーヨ:本当、 使いやすさ:「なし」、 期間:0.5、 }、 0 )) loop.add(box_tl、index * stagger) })
タイムライン期間を1のタイムライン期間を使用しているのはなぜですか?それは物事を追いかけやすくします。ボックスが中点にある時間は0.5であることがわかっています。緩和は私たちがここで普段考える効果がないことに注意する価値があります。実際、緩和は実際にボックスがどのように自分自身を配置するかに関与します。たとえば、簡単に移動する前に、箱を右に束ねます。
上記のコードはこれを与えてくれます。
ほとんど。しかし、私たちの箱は真ん中でしばらく消えます。これを修正するには、即時のプロパティを紹介しましょう。 Animation-Fill-Mode:Noneのように機能します。 GSAPに、ボックスに設定されているスタイルを保持または事前に記録したくないことを伝えています。
shifts.foreach((box、index)=> { const box_tl = gsap .timeline() .fromto( 箱、 { Xpercent:100、 }、 { Xpercent:-200、 期間:1、 使いやすさ:「なし」、 即時者:false、 }、0 )) .fromto( 箱、 { スケール:0、 }、 { スケール:1、 繰り返し:1、 Zindex:boxes.length 1、 ヨーヨ:本当、 使いやすさ:「なし」、 期間:0.5、 即時者:false、 }、 0 )) loop.add(box_tl、index * stagger) })
その小さな変更は私たちのために物事を修正します! z-index:boxes.lengthも含めた方法に注意してください。これにより、Z-Indexの問題に対して私たちを保護するはずです。
そこに私たちはそれを持っています!私たちの最初の無限のシームレスループ。重複する要素と完全な継続はありません。私たちは時間を曲げています!あなたがここまで来たら、背中を軽くたたいてください! ?
一度により多くの箱を見たい場合は、タイミング、よろめき、簡単にいじくり回すことができます。ここでは、0.2のよろめきで、ミックスに不透明度も導入しました。
ここでの重要な部分は、不透明な遷移がスケールよりも速くなるようにrepeatdelayを利用できることです。 0.25秒以上フェードします。 0.5秒待ちます。 0.25秒以上フェードアウトします。
.fromto( 箱、 { 不透明度:0、 }、{ 不透明度:1、 期間:0.25、 繰り返し:1、 Repeatdelay:0.5、 即時者:false、 使いやすさ:「なし」、 ヨーヨ:本当、 }、0)
いいね!私たちは、それらの内外での移行を使用して、私たちが望むことは何でもできます。ここでの主なことは、無限のループを提供する時間の窓があるということです。
シームレスなループができたので、スクロールに添付しましょう。このためには、GSAPのScrolltriggerを使用できます。これには、ループウィンドウをスクラブするための余分なトゥイーンが必要です。ループを今一時停止するように設定した方法に注意してください。
const loop_head = gsap.fromto(loop、{ TotalTime:start_time、 }、 { TotalTime:end_time、 期間:10、 使いやすさ:「なし」、 繰り返し:-1、 一時停止:本当、 }) const scrub = gsap.to(loop_head、{ 合計時間:0、 一時停止:本当、 期間:1、 使いやすさ:「なし」、 })
ここでのトリックは、スクラブの合計時間を更新して、スクロールトリガーを使用してループのプレイヘッドをスクラブすることです。このスクロールをセットアップできるさまざまな方法があります。容器に水平または縛られることができます。しかし、私たちがやろうとしていることは、ボックスを.boxes要素に包み、それをビューポートに固定することです。 (これにより、ビューポートでの位置が修正されます。)垂直スクロールにも固執します。デモをチェックして、ビューポートのサイズに物事を設定する.boxesのスタイリングを確認してください。
'https://cdn.skypack.dev/gsap/scrolltriggerからscrolltriggerをインポート gsap.registerplugin(scrolltrigger) scrolltrigger.create({ 開始:0、 終了: '= 2000'、 水平:false、 ピン: '.boxes'、 onupdate:self => { scrub.vars.totaltime = loop_head.duration() * self.progress scrub.invalidate()。restart() } })
重要な部分は、onupdate内です。そこで、スクロールの進行状況に基づいてTweenの合計時間を設定します。無効なコールは、スクラブの内部記録された位置をフラッシュします。再起動は、設定した新しい合計時間に位置を設定します。
試してみてください!タイムラインを行き来して、ポジションを更新できます。
それはどれほどクールですか?スクロールして、タイムラインのウィンドウであるタイムラインをスクラブするタイムラインをスクラブすることができます。それがここで起こっているので、それを一瞬消化します。
これまで、私たちは時間を操作してきました。今、私たちはタイムトラベルに行きます!
これを行うには、他のGSAPユーティリティを使用しますが、loop_headの合計時間をスクラブすることはもうありません。代わりに、プロキシを介して更新します。これは、「メタ」GSAPに移行するもう1つの素晴らしい例です。
Playheadの位置をマークするプロキシオブジェクトから始めましょう。
const playhead = {position:0}
これで、スクラブを更新してポジションを更新できます。同時に、GSAPのラップユーティリティを使用して、loop_headの持続時間の周りに位置値をラップします。たとえば、期間が10であり、値11を提供する場合、1を取り戻します。
const position_wrap = gsap.utils.wrap(0、loop_head.duration()) const scrub = gsap.to(playhead、{ 位置:0、 onupdate:()=> { loop_head.totaltime(position_wrap(playhead.position)) }、 一時停止:本当、 期間:1、 使いやすさ:「なし」、 })
最後になりましたが、スクラブ上の正しい変数を更新するように、Scrolltriggerを修正する必要があります。 TotalTimeの代わりに、それが位置です。
scrolltrigger.create({ 開始:0、 終了: '= 2000'、 水平:false、 ピン: '.boxes'、 onupdate:self => { scrub.vars.position = loop_head.duration() * self.progress scrub.invalidate()。restart() } })
この時点で、プロキシに切り替えましたが、変更は見られません。
スクロールするときは、無限のループが必要です。私たちの最初の考えは、スクロールの進行状況を完了したときにスタートまでスクロールすることかもしれません。そして、それはまさにそれを行い、バックバックします。それは私たちがやりたいことですが、プレイヘッドが後方にスクラブしたくありません。これはTotalTimeが入るところです。覚えていますか?繰り返しや繰り返しの遅延を含む全体の総合に応じて、プレイヘッドの位置を取得または設定します。
たとえば、ループヘッドの持続時間が5で、そこに着いたと言って、私たちは0に戻りません。代わりに、ループヘッドを10にスクラブし続けます。その間、私たちはスクラブのどこにいるかを示すため、反復変数を追跡します。また、進行状況のしきい値に達したときにのみ反復を更新することを確認します。
反復変数から始めましょう:
イテレーション= 0とします
それでは、Scrolltriggerの実装を更新しましょう。
const trigger = scrolltrigger.create({ 開始:0、 終了: '= 2000'、 水平:false、 ピン: '.boxes'、 onupdate:self => { const scroll = self.scroll() if(scroll> self.end -1){ //時間内に進みます ラップ(1、1) } else if(scroll <p>現在、ポジションの計算に反復を考慮していることに注意してください。これはスクラバーで包まれることを忘れないでください。また、巻物の限界に達したときに検出します。それが私たちが包むポイントです。この関数は適切な反復値を設定し、新しいスクロール位置を設定します。</p><pre rel="JavaScript" data-line=""> const wrap =(iterationdelta、scrollto)=> { Iteration = IterationDelta trigger.scroll(scrollto) trigger.update() }
無限のスクロールがあります!あなたが解き放つことができるスクロールホイールを備えたそれらの派手なマウスのいずれかを持っているなら、それを試してみてください!楽しい!
現在の反復と進捗を表示するデモは次のとおりです。
私たちはそこにいます。しかし、このような機能に取り組んでいるとき、常に「持っているのはいい」ことがあります。スクロールスナップから始めましょう。 GSAPは、他の依存関係なしでgsap.utils.snapを使用できるため、これを簡単にします。それは、ポイントを提供する時にスナップを処理します。 0〜1の間のステップを宣言し、例に10個のボックスがあります。つまり、0.1のスナップが私たちのために働くことを意味します。
const snap = gsap.utils.snap(1 / box.length)
そして、それは私たちの位置値をスナップするために使用できる関数を返します。
巻物が終了したら、スナップしたいです。そのために、Scrolltriggerでイベントリスナーを使用できます。スクロールが終了すると、特定の位置にスクロールします。
scrolltrigger.addeventlistener( 'scrollend'、()=> { Scrolltoposition(scrub.vars.position) })
そして、ここに巻物視があります:
const scrolltoposition = position => { const snap_pos = snap(position) Const Progress = (snap_pos -loop_head.duration() * iteration) / loop_head.duration() const scroll = progressToscroll(Progress) trigger.scroll(scroll) }
ここで何をしているの?
const ProgressToScroll = Progress => gsap.utils.clamp(1、trigger.end -1、gsap.utils.wrap(0、1、progress) * trigger.end)
この関数は、進行状況値を取得し、最大のスクロール距離にマッピングします。ただし、クランプを使用して、値が0または2000になることはないことを確認します。これは重要です。これらの値へのスナップを保護しています。これは、無限のループになります。
そこに少し取り入れる必要があります。各スナップに更新された値を表示するこのデモをご覧ください。
なぜ物事はずっと気味が悪いのですか?スクラブの期間と容易さが変更されました。期間が短く、パンチが容易になり、スナップが得られます。
const scrub = gsap.to(playhead、{ 位置:0、 onupdate:()=> { loop_head.totaltime(position_wrap(playhead.position)) }、 一時停止:本当、 期間:0.25、 使いやすさ: 'Power3'、 })
しかし、そのデモでプレイした場合、問題があることに気付くでしょう。時々、スナップの中で包むと、プレイヘッドが飛び回ります。スナップ時にラップすることを確認することで、それを説明する必要がありますが、それが必要なときだけです。
const scrolltoposition = position => { const snap_pos = snap(position) Const Progress = (snap_pos -loop_head.duration() * iteration) / loop_head.duration() const scroll = progressToscroll(Progress) if(Progress> = 1 || Progress <p>そして今、私たちはスナップで無限のスクロールを持っています!</p><h3>次は何ですか?</h3><p>固体の無限精基の基礎を完了しました。それを活用して、コントロールやキーボード機能などのものを追加できます。たとえば、これは「次の」ボタンと「以前の」ボタンとキーボードコントロールを接続する方法かもしれません。私たちがしなければならないのは、時間を操作することだけですよね?</p><pre rel="JavaScript" data-line=""> const next =()=> scrolltoposition(scrub.vars.position-(1 / boxes.length))) const prev =()=> scrolltoposition(scrub.vars.position(1 / box.length))) //左および右矢印とaおよびd document.addeventlistener( 'keydown'、event => { if(event.keycode === 37 || event.keycode === 65)next() if(event.keycode === 39 || event.keycode === 68)prev() }) document.QuerySelector('。NEXT ')。ADDEVENTLISTENER(' Click '、next) document.queryselector( '。prev')。addeventlistener( 'click'、prev)
それは私たちにこのようなものを与えるかもしれません。
巻物視関数を活用し、必要に応じて値を上げることができます。
それを見ますか? GSAPは要素以上のアニメーション化できます!ここでは、ほぼ完璧な無限のスライダーを作成するために時間をかけて操作しました。重複する要素、混乱、柔軟性が良好ではありません。
私たちがカバーしたものを要約しましょう:
これで時間を操作できます! ?
「メタ」GSAPに進むという概念は、さまざまな可能性を開きます。他に何をアニメーションできますか?オーディオ?ビデオ? 「カバーフロー」デモに関しては、ここにありました!
以上が「Meta GSAP」に行く:「完璧な」無限のスクロールの探求の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。