私はカスタムのフルスクリーンレイアウトを実際に日常的に頻繁に使用しています。通常、これらのレイアウトは、かなりの量のインタラクションとアニメーションを意味します。トランジションのタイムトリガーされた複雑なタイムラインであろうと、スクロールベースのユーザー主導のイベントセットであろうと、ほとんどの場合、UIには、いくつかの調整と変更を加えた、すぐに使用できるプラグインソリューションを使用するだけでは不十分です。 。一方で、たくさん見かけます JavaScript開発者 タスクが特定のプラグインが提供するすべてのベルやホイッスルを必要としない場合でも、仕事を簡単にするためにお気に入りのJSプラグインに手を伸ばす傾向があります。
免責事項: もちろん、そこにある多くのプラグインの1つを使用すると、その特典があります。多くのコーディングを行うことなく、ニーズに合わせて調整するために使用できるさまざまなオプションを利用できます。また、ほとんどのプラグイン作成者は、コードを最適化し、クロスブラウザーおよびクロスプラットフォーム互換にするなどしています。しかし、それでも、プロジェクトに含まれるフルサイズのライブラリは、1つまたは2つの異なるものだけで提供されます。サードパーティのプラグインを使用することが当然悪いことだと言っているわけではありません。プロジェクトでは毎日使用しています。一般的に、各アプローチの長所と短所をそのまま比較検討することをお勧めします。コーディングの良い習慣。この方法で独自のことを行う場合、探しているものを知るには、コーディングの知識と経験が少し必要になりますが、最終的には、1つのことと1つのことだけを行うコードを取得する必要があります。あなたはそれをしたいです。
この記事は、カスタムコンテンツアニメーションを使用してフルスクリーンのスクロールトリガースライダーレイアウトを開発する際の純粋なCSS / JSアプローチを示すことを目的としています。この縮小されたアプローチでは、CMSバックエンドの最新のCSSから提供されると予想される基本的なHTML構造について説明します( SCSS )レイアウト手法、および完全な対話性のためのバニラJavaScriptコーディング。この概念は必要最低限のものであるため、大規模なプラグインに簡単に拡張したり、コアに依存関係のないさまざまなアプリケーションで使用したりできます。
私たちが作成しようとしているデザインは、各プロジェクトの注目の画像とタイトルを備えたミニマルな建築家ポートフォリオのショーケースです。アニメーション付きの完全なスライダーは次のようになります。
あなたはデモをチェックすることができます ここに 、そしてあなたは私のにアクセスすることができます Githubリポジトリ 詳細については。
購入者に関する次の機能のうち、購入者が高い交渉力を持っていることを示しているのはどれですか?
使用する基本的なHTMLは次のとおりです。
hero-slider
IDが
のdiv私たちのメインホルダーです。内部では、レイアウトはセクションに分割されています。 #64 Paradigm
スライドショーのセクションに焦点を当てましょう。それがこの記事の関心の対象です。ここには2つの部分があります— メイン そして に 。 Mainは注目の画像を含むdivで、auxは画像のタイトルを保持します。これら2つのホルダー内の各スライドの構造はかなり基本的です。メインホルダーの内側に画像スライドがあります。
#hero-slider { position: relative; height: 100vh; display: flex; background: $dark-color; } #slideshow { position: relative; flex: 1 1 $main-width; display: flex; align-items: flex-end; padding: $offset; } #info { position: relative; flex: 1 1 $side-width; padding: $offset; background-color: #fff; }
インデックスデータ属性は、スライドショーのどこにいるかを追跡するために使用するものです。興味深いトランジション効果を作成するために使用するabs-maskdivと、slide-imagedivには特定の注目画像が含まれています。画像は、CMSから直接取得されたかのようにインラインでレンダリングされ、エンドユーザーによって設定されます。
同様に、タイトルはAuxホルダーの内側にスライドします。
#slideshow { position: relative; flex: 1 1 $main-width; display: flex; align-items: flex-end; padding: $offset; } #slides-main { @extend %abs; &:after { content: ''; @extend %abs; background-color: rgba(0, 0, 0, .25); z-index: 100; } .slide-image { @extend %abs; background-position: center; background-size: cover; z-index: -1; } } #slides-aux { position: relative; top: 1.25rem; width: 100%; .slide-title { position: absolute; z-index: 300; font-size: 4vw; font-weight: 700; line-height: 1.3; @include outlined(#fff); } }
各スライドタイトルは、対応するデータ属性とそのプロジェクトの単一ページにつながることができるリンクを持つH2タグです。
HTMLの残りの部分も非常に単純です。上部にロゴ、ユーザーがどのページを表示しているかを示す静的情報、説明、スライダーの現在/合計インジケーターがあります。
ソースCSSコードはで書かれています SCSS 、ブラウザが解釈できる通常のCSSにコンパイルされるCSSプリプロセッサ。 SCSSには、変数、ネストされた選択、ミックスイン、その他の優れた機能を使用できるという利点がありますが、ブラウザーにコードを正しく読み取らせるには、CSSにコンパイルする必要があります。このチュートリアルの目的のために、私は使用しました スカウト-アプリ 最小限のツールが必要だったので、コンパイルを処理します。
フレックスボックスを使用して、基本的なサイドバイサイドレイアウトを処理しました。アイデアは、片側にスライドショーを、反対側に情報セクションを配置することです。
background-size: cover
ポジショニングについて詳しく見ていきましょう。もう一度、スライドショーのセクションに注目してください。
%abs { position: absolute; top: 0; left: 0; height: 100%; width: 100%; }
@mixin outlined($color: $dark-color, $size: 1px) { color: transparent; -webkit-text-stroke: $size $color; }
を使用して、メインスライダーを絶対位置に設定し、背景画像を領域全体に拡大しました。プロパティ。スライドのタイトルとのコントラストを高めるために、オーバーレイとして機能する絶対疑似要素を設定しました。スライドのタイトルを含む補助スライダーは、画面の下部と画像の上部に配置されています。
一度に表示されるスライドは1つだけなので、各タイトルも絶対値に設定し、カットオフがないことを確認するためにJSを介してホルダーサイズを計算しますが、それについては次のセクションの1つで詳しく説明します。ここでは、extendingと呼ばれるSCSS機能の使用法を見ることができます。
.slider-title-wrapper { position: absolute; top: $offset; left: calc(100% - #{$offset}); transform-origin: 0% 0%; transform: rotate(90deg); @include outlined; }
私は絶対測位を頻繁に使用したので、このCSSを拡張可能にプルして、さまざまなセレクターで簡単に使用できるようにしました。また、タイトルとメインスライダーのタイトルをスタイリングするときにDRYアプローチを提供するために、「アウトライン」と呼ばれるミックスインを作成しました。
transform-origin
このレイアウトの静的な部分に関しては、複雑なことは何もありませんが、ここでは、通常のフローではなくY軸上にある必要があるテキストを配置するときに興味深い方法を見ることができます。
#logo:after { transform: scaleY(0); transform-origin: 50% 0; transition: transform .35s $easing; } .logo-text { display: block; transform: translate3d(120%, 0, 0); opacity: 0; transition: transform .8s .2s, opacity .5s .2s; } .current, .sep:before { opacity: 0; transition: opacity .4s 1.3s; } #info { transform: translate3d(100%, 0, 0); transition: transform 1s $easing .6s; } .line { transform-origin: 0% 0; transform: scaleX(0); transition: transform .7s $easing 1s; } .slider-title { overflow: hidden; >span { display: block; transform: translate3d(0, -100%, 0); transition: transform .5s 1.5s; } }
transform
に注目したいのですがこのタイプのレイアウトでは実際には十分に活用されていないことがわかったので、プロパティ。この要素の配置方法は、アンカーが要素の左上隅にとどまり、回転ポイントを設定し、さまざまな画面サイズに関して問題なく、テキストがそのポイントから下に向かって継続的に流れるようにすることです。
より興味深いCSSの部分である初期読み込みアニメーションを見てみましょう。
通常、この種の同期されたアニメーションの動作は、ライブラリを使用して実現されます- GSAP たとえば、は、優れたレンダリング機能を提供し、使いやすく、開発者がプログラムで要素の遷移を相互に連鎖できるようにするタイムライン機能を備えた、最高の製品の1つです。
ただし、これは純粋なCSS / JSの例であるため、ここでは本当に基本的なものにすることにしました。そのため、各要素はデフォルトで開始位置に設定されています。変換または不透明度によって非表示になり、JSによってトリガーされるスライダーの読み込み時に表示されます。すべてのトランジションプロパティは手動で調整され、自然で興味深いフローを確保します。各トランジションは別のトランジションに続き、快適な視覚体験を提供します。
transform
ここで見てほしいことが1つあるとすれば、それはheroSlider
の使用です。プロパティ。 HTML要素を移動するときは、トランジションであろうとアニメーションであろうと、utils
を使用することをお勧めします。プロパティ。余白やパディング、さらにはオフセット(上、左など)を使用する傾向があり、レンダリングに関しては適切な結果が得られない人がたくさんいます。
インタラクティブな動作を追加するときにCSSを使用する方法をより深く理解するために、私はお勧めできませんでした 次の記事 足りる。
ChromeエンジニアのPaulLewisによるもので、CSSであろうとJSであろうと、ウェブでのピクセルレンダリングについて知っておくべきことのほとんどすべてをカバーしています。
JavaScriptファイルは2つの異なる機能に分かれています。
init
ここで必要なすべての機能を処理する関数、およびresize
再利用可能なユーティリティ関数をいくつか追加した関数。 プロジェクトでそれらを再利用しようとしている場合にコンテキストを提供するために、これらのユーティリティ関数のそれぞれにコメントしました。
main関数は、次の2つのブランチを持つようにコーディングされています。init
およびheroSlider
。これらのブランチは、main関数の戻りを介して利用可能であり、必要に応じて呼び出されます。 const slider = { hero: document.querySelector('#hero-slider'), main: document.querySelector('#slides-main'), aux: document.querySelector('#slides-aux'), current: document.querySelector('#slider-nav .current'), handle: null, idle: true, activeIndex: -1, interval: 3500 };
はメイン関数の初期化であり、ウィンドウの読み込みイベントでトリガーされます。同様に、サイズ変更ブランチはウィンドウのサイズ変更時にトリガーされます。サイズ変更機能の唯一の目的は、タイトルのフォントサイズが異なる可能性があるため、ウィンドウのサイズ変更時にタイトルのスライダーサイズを再計算することです。
handle
で関数、必要なすべてのデータとセレクターを含むスライダーオブジェクトを提供しました。
idle
補足として、このアプローチは、たとえばReactを使用している場合、データを状態で保存したり、新しく追加されたフックを使用したりできるため、簡単に適応できます。要点を維持するために、ここでのKey-Valueペアのそれぞれが表すものを見てみましょう。
activeIndex
プロパティは、自動再生機能を開始および停止するために使用されます。interval
プロパティは、スライドの移行中にユーザーが強制的にスクロールできないようにするフラグです。setHeight(slider.aux, slider.aux.querySelectorAll('.slide-title')); loadingAnimation();
現在アクティブなスライドを追跡できるようになりますsetHeight
スライダーの自動再生間隔を示しますスライダーの初期化時に、2つの関数を呼び出します。
処理で描画する方法
const loadingAnimation = function () { slider.hero.classList.add('ready'); slider.current.addEventListener('transitionend', start, { once: true }); }
const start = function () { autoplay(true); wheelControl(); window.innerWidth <= 1024 && touchControl(); slider.aux.addEventListener('transitionend', loaded, { once: true }); }
関数はユーティリティ関数にアクセスして、最大タイトルサイズに基づいて補助スライダーの高さを設定します。このようにして、適切なサイズが提供され、コンテンツが2行に落ちてもスライドタイトルが途切れないようにします。
LoadingAnimation関数は、CSSクラスを要素に追加してイントロCSSトランジションを提供します。
loadingAnimation
スライダーインジケーターはCSS遷移タイムラインの最後の要素であるため、遷移が終了するのを待って、開始関数を呼び出します。オブジェクトとして追加のパラメーターを提供することにより、これが1回だけトリガーされるようにします。
開始関数を見てみましょう。
const autoplay = function (initial) { slider.autoplay = true; slider.items = slider.hero.querySelectorAll('[data-index]'); slider.total = slider.items.length / 2; const loop = () => changeSlide('next'); initial && requestAnimationFrame(loop); slider.handle = utils().requestInterval(loop, slider.interval); }
したがって、レイアウトが終了すると、最初の遷移はslideChange
によってトリガーされます。関数と開始関数が引き継ぎます。次に、自動再生機能をトリガーし、ホイールコントロールを有効にし、タッチデバイスとデスクトップデバイスのどちらを使用しているかを判断し、タイトルスライドの最初の遷移を待って適切なCSSクラスを追加します。
このレイアウトのコア機能の1つは、自動再生機能です。対応する関数を見てみましょう。
requestAnimationFrame
まず、自動再生フラグをtrueに設定して、スライダーが自動再生モードになっていることを示します。このフラグは、ユーザーがスライダーを操作した後に自動再生を再トリガーするかどうかを決定するときに役立ちます。次に、すべてのスライダーアイテム(スライド)を参照します。アクティブなクラスを変更し、2つの同期されたスライダーレイアウト(メインと補助)があるため、すべてのアイテムを合計して2で割ることにより、スライダーの合計反復回数を計算します。しかし、両方を同時に変更する「スライダー」自体は1つだけです。
ここでのコードの最も興味深い部分は、ループ関数です。 requestAnimationFrame
を呼び出して、1分で通過するスライド方向を提供しますが、ループ関数は数回呼び出されます。理由を見てみましょう。
最初の引数がtrueと評価された場合、ループ関数を次のように呼び出します。 requestInterval
折り返し電話。これは、スライドの即時変更をトリガーする最初のスライダーのロード時にのみ発生します。 setInterval
を使用する次のフレームの再描画の直前に、提供されたコールバックを実行します。
デザインパターン定義の原則
ただし、自動再生モードでスライドを読み続けたいので、この同じ関数を繰り返し呼び出します。これは通常、setIntervalで実現されます。ただし、この場合、ユーティリティ関数の1つであるrequestInterval
を使用します。 requestAnimationFrame
うまくいくでしょう、slider.handle
cancelAnimationFrame
に依存する高度な概念ですよりパフォーマンスの高いアプローチを提供します。これにより、ブラウザのタブがアクティブな場合にのみ機能が再トリガーされます。
この素晴らしい記事のこの概念の詳細については、 CSSのトリック 。この関数からの戻り値をslideChange
に割り当てることに注意してください。プロパティ。関数が返すこの一意のIDは利用可能であり、後で使用して自動再生をキャンセルするために使用します const changeSlide = function (direction) { slider.idle = false; slider.hero.classList.remove('prev', 'next'); if (direction == 'next') { slider.activeIndex = (slider.activeIndex + 1) % slider.total; slider.hero.classList.add('next'); } else { slider.activeIndex = (slider.activeIndex - 1 + slider.total) % slider.total; slider.hero.classList.add('prev'); } //reset classes utils().removeClasses(slider.items, ['prev', 'active']); //set prev const prevItems = [...slider.items] .filter(item => { let prevIndex; if (slider.hero.classList.contains('prev')) { prevIndex = slider.activeIndex == slider.total - 1 ? 0 : slider.activeIndex + 1; } else { prevIndex = slider.activeIndex == 0 ? slider.total - 1 : slider.activeIndex - 1; } return item.dataset.index == prevIndex; }); //set active const activeItems = [...slider.items] .filter(item => { return item.dataset.index == slider.activeIndex; }); utils().addClasses(prevItems, ['prev']); utils().addClasses(activeItems, ['active']); setCurrent(); const activeImageItem = slider.main.querySelector('.active'); activeImageItem.addEventListener('transitionend', waitForIdle, { once: true }); }
。
wheelControl
関数は、概念全体の主要な関数です。自動再生によるか、ユーザートリガーによるかを問わず、スライドを変更します。スライダーの方向を認識し、ループを提供するため、最後のスライドに到達したときに最初のスライドに進むことができます。これが私がそれをコーディングした方法です:
touchControl
アイデアは、HTMLから取得したデータインデックスに基づいてアクティブなスライドを決定することです。各ステップについて説明しましょう。
setCurrent
またはwaitForIdle
。const wheelControl = function () { slider.hero.addEventListener('wheel', e => { if (slider.idle) { const direction = e.deltaY > 0 ? 'next' : 'prev'; stopAutoplay(); changeSlide(direction); } }); }
activeIndexに基づいてスライダーインジケーターを更新するコールバックです。stopAutoplay
をトリガーするために、アクティブな画像スライドの遷移が終了するのを待ちます。以前にユーザーによって中断された場合に自動再生を再開するコールバック。画面サイズに基づいて、ホイールとタッチの2種類のユーザーコントロールを追加しました。ホイールコントロール:
stopAutoplay
ここでは、ホイールをリッスンし、スライダーが現在アイドルモード(現在はスライドの変更をアニメーション化していない)の場合、ホイールの方向を決定し、cancelRequestInterval
を呼び出します。自動再生機能が進行中の場合は停止し、方向に基づいてスライドを変更します。 const stopAutoplay = function () { slider.autoplay = false; utils().clearRequestInterval(slider.handle); }
関数は、自動再生フラグをfalse値に設定し、wheelControl
を呼び出すことによって間隔をキャンセルする単純な関数に他なりません。適切なハンドルを渡すユーティリティ関数:
touchControl
const touchControl = function () { const touchStart = function (e) { slider.ts = parseInt(e.changedTouches[0].clientX); window.scrollTop = 0; } const touchMove = function (e) { slider.tm = parseInt(e.changedTouches[0].clientX); const delta = slider.tm - slider.ts; window.scrollTop = 0; if (slider.idle) { const direction = delta <0 ? 'next' : 'prev'; stopAutoplay(); changeSlide(direction); } } slider.hero.addEventListener('touchstart', touchStart); slider.hero.addEventListener('touchmove', touchMove); }
と同様に、touchstart
があります。タッチジェスチャを処理します。
touchmove
2つのイベントをリッスンします:slideChange
およびslideChange
。次に、差を計算します。負の値が返された場合、ユーザーが右から左にスワイプすると、次のスライドに移動します。一方、値が正の場合、つまりユーザーが左から右にスワイプした場合は、div
をトリガーします。 「前」として渡された方向で。どちらの場合も、自動再生機能は停止します。
これは非常に単純なユーザージェスチャーの実装です。これに基づいて、前/次のボタンを追加してabs-mask
をトリガーできます。クリックするか、箇条書きを追加して、インデックスに基づいてスライドに直接移動します。
これで、最新のトランジション効果を使用して非標準のスライダーレイアウトをコーディングする純粋なCSS / JSの方法がわかりました。
このアプローチが考え方として役立ち、必ずしも従来の設計ではなかったプロジェクトをコーディングするときに、フロントエンドプロジェクトで同様の何かを使用できることを願っています。
画像遷移効果に興味のある方のために、次の数行でこれについて説明します。
イントロセクションで提供したスライドのHTML構造をもう一度見ると、各画像スライドにdiv
があることがわかります。 overflow:hidden
のCSSクラスでその周り。なにこれ&.prev { z-index: 5; transform: translate3d(-100%, 0, 0); transition: 1s $easing; .abs-mask { transform: translateX(80%); transition: 1s $easing; } }
abs-mask
を使用して、表示されている画像の一部を一定量非表示にします。画像とは異なる方向にオフセットします。たとえば、前のスライドのコーディング方法を見ると、次のようになります。
前のスライドのX軸は-100%オフセットされており、現在のスライドの左側に移動していますが、内側の
divは80%右に変換され、より狭いビューポートを提供します。これは、アクティブなスライドのzインデックスを大きくすることと組み合わせて、一種のカバー効果をもたらします。アクティブな画像は前の画像をカバーすると同時に、全体像を提供するマスクを移動することで表示領域を拡張します。アニメーション化できる最も標準的なCSSプロパティは、変換、不透明度、色、背景色、高さ、幅などです。完全なリストは、Mozillaの技術文書にあります。
CSSキーフレームアニメーションは、指定された期間に選択された要素で発生する必要があるすべての遷移の0〜100%の時間表現です。このようにして、複数の遷移を組み合わせてシームレスな視覚的表現にすることができます。
遷移は、CSSプロパティを2つの値の間で遷移できるようにするプロパティです。たとえば、要素にカーソルを合わせて、不透明度を0から1に遷移させます。
特異性を解釈することにより、ブラウザはどのCSSルールを解釈するかを決定します。 CSSの特異性は、セレクターのタイプ(タイプセレクター、クラスセレクター、IDセレクター)によって異なります。複数のセレクターを組み合わせたり、兄弟と子のコンビネーターを追加したりすると、特異性も操作されます。
CSSでスタイル設定されたHTMLコンテンツを取得するには、ブラウザは次の方法を使用します。 HTMLをロードすると、そのコンテンツが提供されたスタイル情報と組み合わされ、DOMツリーが作成され、最後にそのコンテンツが表示されます。