ここしばらくの間、私はCSSのみのスライドショーを実装しようとしてきました。
私が出会った他のほとんどのCSSスライドショーでは、これらすべてのボックスにチェックを入れていませんでした。
ありがたいことに、いくつかの「モダンな」CSS-ismsを使用していても、ブラウザー自体が大幅に改善されて、実際には非常に広く可能になるまで、私には長い時間がかかりました。万が一、他の人に役立つ場合に備えて、ここに投稿したいと思いました。
では、CSSと次のマークアップのみを使用して、ナビゲート可能なスライドショーを作成するにはどうすればよいでしょうか。
<ul class="css-slider">
<li class="slide"><img src="photos/a.jpg" /></li>
<li class="slide"><img src="photos/b.jpg" /></li>
<li class="slide"><img src="photos/c.jpg" /></li>
<li class="slide"><img src="photos/d.jpg" /></li>
<li class="slide"><img src="photos/e.jpg" /></li>
</ul>
UPDATE:Firefox 32(Mac)にはバグがあるようです これは、楕円がSVGマスクでレンダリングされないことを意味します。反射の失敗につながります...実装された例の画像のいずれかを足元に置くと、Chrome 37.0.2062.120(Mac)が何をしているのかを説明し始めません)この答えの。
— 2014年9月18日。
スライドは、ビジュアル部分とインタラクティブ部分の2つの主要部分から構成されています。インタラクティブな部分はほとんど静的なままで、視覚的な部分はアニメーション化されています。何度も遊んだ後、最初のHTML構造(上記を参照)を拡張して、スライドのコンテンツの周りにいくつかの追加のラッパーを組み込みました。これにより、個別のインタラクティブパーツとビジュアルパーツが可能になり、垂直方向のセンタリングや反射など、他の便利な機能の柔軟性も向上します。
<ul class="css-slider">
<li class="slide" tabindex="1" id="l1">
<span class="slide-outer">
<span class="slide-inner">
<span class="slide-gfx" id="s1">
<img src="photos/a.jpg" />
</span>
</span>
</span>
</li>
<li class="slide" tabindex="1" id="l2">
<span class="slide-outer">
<span class="slide-inner">
<span class="slide-gfx" id="s2">
<img src="photos/b.jpg" />
</span>
</span>
</span>
</li>
...
</ul>
システムを実際にスライドショーとして動作させるには、現在のスライドまたはフォーカスされたスライドを識別する方法が必要です。この場合、:focus
を使用してこの区別を処理します。上記のtabindex=“1”
の追加に気づいたかもしれませんが、これは:focus
などの予期しない要素に疑似クラス<li>
が適用されるためです。これは主にWebkitベースのブラウザーで必要でしたが、他のエージェントも助けるかもしれません。
つまり、簡単に言うと、すべてのスライドが上下に重ねられ、現在フォーカスされているスライドがビューポートの中央に配置され、最高のz-index
が与えられます。フォーカスされたスライドの後のスライドは、画面外で右に移動します。 xy変換はスライドの視覚的なコンテンツにのみ影響を与えることに注意してください。インタラクティブなパーツは、ビューポート領域全体を埋めるように重ねられます。
マウスやタッチを介して次のスライドや前のスライドにフォーカスが移動するためには、インタラクティブレイヤーの一部にユーザーがアクセスしてクリックできるようにする必要があるためです。 left
、right
、およびpadding
を組み合わせて使用すると、視覚要素を移動せずに正しい領域を表示できます。
つまり、現在フォーカスされているスライドが変わると、アクセス可能なインタラクティブレイヤーの領域も変わります。ユーザーが次または前の矢印をクリックするたびに、実際には、何らかのアクションを実行するリンクをクリックするのではなく、隣接する<li>
要素に注目しています。
これをWebkitブラウザーで機能させるには、tabindex
属性を使用して、:focus
疑似クラスがスライドを表すために使用する基本要素で機能するようにする必要があります。
採用されているトリックのため、スライドは逆順で再生されます。
タブを使用してスライドショーをナビゲートできますが、ポイント2により、後方にナビゲートします。Macを使用している場合は、タブが機能する前に OS設定を調整する が必要になる場合があります。
ナビゲーション矢印を表示するleft
、right
トリックのために、前方にナビゲートするときに少し視覚的なグリッチがあり、次の前の矢印がすばやく所定の場所に移動することがわかります。
このシステムは:focus
に基づいて機能するため、フォーカスが失われると、スライドショーは最初のスライドに戻ります。このため、JavaScriptとの相互作用を強化しない限り、サブリンクはスライド内で機能しません。
私のデモではSVGの背景画像を使用していますが、これらは明らかにオプションであり、古いブラウザでは機能しません。
IE7とIE8は
:last-child
または:nth-child
を理解することさえできません。そのため、それらに対しては機能しません。
以下のデモでは、JavaScriptの繁栄がいくつかあることがわかります。これらは、コンストラクトが何ができるかを示すのに役立つか、徐々に強化されます。デモ専用のチェックボックスで、特定の機能を有効または無効にできます。これらの機能は、単純なクラスによって適用されます。
ご注意ください:リフレクションアドオンは、任意のマークアップ属性に依存しています。各.slideに一意のIDを追加し、CSSを拡張してそれらを考慮に入れる必要があります。
さて、ここから始めるのが基本的なセットアップです。まず、スライドショーには画像が含まれているため、基本的な画像スタイルを設定しているため、これはすべてオプションです。
.slide-gfx img {
max-width: 600px;
max-height: auto;
border-radius: 20px;
box-shadow: 0 0 80px rgba(255,255,255,1);
}
全画面サイズでスライドショーを操作しているときにウィンドウのスクロールバーが表示されないように、スライダーマスクがスライドショー全体のラッパーとして追加されました。これもオプションです。
.css-slider-mask {
display: block;
overflow: hidden;
width: 100%;
height: 100%;
}
ここで、スライダーに必要な実際の設定を取得します。この最初の部分は非常に簡単です。display: none;
部分を除いて、これにより、最初はすべての人からスライドショーが非表示になりますが、後で:nth-child
をサポートするブラウザでオーバーライドされます。 <body>
要素は2番目の子になる可能性が高いですが、これを使用する前に確認する必要があります。
.css-slider {
list-style: none;
margin: 0;
padding: 0;
width: 96%;
height: 100%;
margin-left: 2%;
z-index: 1;
}
.css-slider {
position: relative;
display: none;
}
body:nth-child(2) .css-slider {
display: block;
}
次に、スライドの詳細について説明します。反転した General Sibling Selector (~
)が存在しないため、スライドのすべてのデフォルトスタイルは、将来(または次)の状態を表します。これは、将来のスライドを選択する実際の方法がないためです。
.css-slider .slide {
display: block;
position: absolute;
left: 40px;
top: 0;
right: 0;
bottom: 0;
padding-left: 0;
padding-right: 40px;
z-index: 100;
outline: 0; /* kill the focus rect! */
}
繰り返しますが、デフォルトでは前向き矢印のスタイルを設定し、後で現在および過去のスライドを上書きします。
.css-slider .slide {
background: url('arrow-right.svg') no-repeat right center;
background-size: 25px auto;
}
.css-slider .slide:hover {
background-image: url('arrow-right-hover.svg');
cursor: pointer;
}
ここでフォーカスされたスライドですが、ここでの重要な項目は:focus
(すでに説明したとおり)と:last-child
です。最初の子ではなく最後の子が使用されます。これは、逆方向に作業する必要があるためです(すべて逆の General Sibling Selector~
がないため)。どちらの子もまったく必要なのは、現在のフォーカスがない場合、つまりページのロード時に、最初のスライドに「フォーカス」できるようにするためです。
.css-slider .slide:target,
.css-slider .slide:target:hover,
.css-slider .slide:focus,
.css-slider .slide:focus:hover,
.css-slider .slide:last-child,
.css-slider .slide:last-child:hover {
left: 40px;
right: 40px;
padding-left: 0;
padding-right: 0;
background: transparent;
z-index: 101;
cursor: default;
}
ここで、過去に陥ったすべてのスライドに影響を与える必要があります。今までに:target
疑似クラスについて言及することは避けました。基本的に、これは「ジャンプナビゲーション」をサポートするために実装されています。 「ジャンプナビゲーション」の賞賛を歌わない理由は2つあります。
とにかく、過去のスライドを選択するコツは、 General Sibling Selector にかかっています。次の構成は基本的にを持っている.slideの後にある.slide(s)を選択します:focus。
.css-slider .slide:target ~ .slide,
.css-slider .slide:focus ~ .slide {
padding-left: 40px;
padding-right: 0;
left: 0;
right: 40px;
}
.css-slider .slide:target ~ .slide,
.css-slider .slide:focus ~ .slide {
background: url('arrow-left.svg') no-repeat left center;
background-size: 25px auto;
}
.css-slider .slide:target ~ .slide:hover,
.css-slider .slide:focus ~ .slide:hover {
background-image: url('arrow-left-hover.svg');
}
次に、スライドのコンテンツで正確に何が起こるかを制御する必要があります。このシステムは、必要に応じてアニメーションセクションを省略できるように設計されています。これは、スライドが瞬時に切り替わることを意味します。この次の部分は、それを処理します。
.css-slider .slide .slide-outer {
display: none;
width: 100%;
height: 100%;
}
Slide-innerは、スライドコンテンツの中央揃えを処理するためだけに使用されます。ディスプレイtable
とディスプレイtable-cell
に依存しています。
.css-slider .slide .slide-outer .slide-inner {
display: table-cell;
vertical-align: middle;
text-align: center;
width: 100%;
height: 100%;
}
Slide-gfxは、文字通り、スライドショーに入れると決めたもののラッパーです。反射はこのコンテナーから生成され、他のいくつかの視覚的なエクストラもそれに追加されます。
.css-slider .slide .slide-outer .slide-inner .slide-gfx {
position: relative;
display: inline-block;
z-index: 102;
text-align: left; /* override the centering back to defaults */
}
この部分は、アニメーションが含まれていない場合のインスタントスイッチを担当します。
.css-slider .slide:target .slide-outer,
.css-slider .slide:focus .slide-outer,
.css-slider .slide:last-child .slide-outer {
display: block; /* if they don't support display table */
display: table;
}
何もフォーカスされていないときにステップインする:last-child
宣言があるため、これ以上変更を加えないと、特定の問題が発生します。これは、:last-child
とは異なり、:focus
が常に適用されるためです。これを修正するには、:last-child
の変更を無効にする必要がありますが、何かに焦点が当てられている場合のみです。これは、次のCSSが行うことです。
.css-slider .slide:target ~ .slide:last-child,
.css-slider .slide:focus ~ .slide:last-child {
cursor: pointer;
}
.css-slider .slide:target ~ .slide:last-child .slide-outer,
.css-slider .slide:focus ~ .slide:last-child .slide-outer {
display: none;
}
これまでのところCSSはかなり一般化されていますが、常に何かがあります...
この部分は、「前のスライド」矢印のクリック機能を修正するためにのみ必要です。これにより、最新の前のフレームが他のフレームの上に浮かび上がります。前のスライドアクションを気にしない場合は、前の矢印を非表示にする限り、この手順を省略できます。 CSSが General Sibling Selector の逆バージョンをサポートしていた場合、この任意のセクション全体を削除できるため、これはかなり煩わしいものです。
基本的に、以下は最大5フレームをサポートします。必要な場合はさらに追加します。少なくとも良いニュースは、実際の悪影響なしに必要以上のフレームを追加できることです。 100フレームを超える場合は、CSSの他の部分で他のZインデックスを調整する必要があります。
.css-slider .slide:target ~ .slide:nth-child(1),
.css-slider .slide:focus ~ .slide:nth-child(1) { z-index:99; }
.css-slider .slide:target ~ .slide:nth-child(2),
.css-slider .slide:focus ~ .slide:nth-child(2) { z-index:98; }
.css-slider .slide:target ~ .slide:nth-child(3),
.css-slider .slide:focus ~ .slide:nth-child(3) { z-index:97; }
.css-slider .slide:target ~ .slide:nth-child(4),
.css-slider .slide:focus ~ .slide:nth-child(4) { z-index:96; }
.css-slider .slide:target ~ .slide:nth-child(5),
.css-slider .slide:focus ~ .slide:nth-child(5) { z-index:95; }
すでに述べたように、アニメーションはオプションであり、CSSの残りすべて、つまり視覚的なアドオンもオプションです。残りの部分は、ここではあまり詳しく説明しません。上記のトリックとCSSのトランジションまたはアニメーションを知っていれば、そのほとんどは非常に簡単です。かさばる主な理由は、いつものように、ベンダープレフィックスです。簡潔にするために削除しました。完全なCSSを入手するには、以下のデモから明らかにそうすることができます。
ご注意ください:これらのアドオンのほとんどは、非常に最新のCSS、つまりアニメーションやSVGに依存しています
/** --------------------------------------------------------------------------
* HANDLE THE SLIDE ANIMATION (optional)
* ------------------------------------------------------------------------ */
/* Override the default instant slide behaviour */
.css-slider .slide .slide-outer {
display: block !important;
display: table !important;
}
/* set up the transitions */
.css-slider .slide .slide-outer {
transition-property: opacity, transform;
transition-duration: 2s;
transition-timing-function: ease;
}
/* After state */
.css-slider .slide:target ~ .slide .slide-outer,
.css-slider .slide:target ~ .slide:last-child .slide-outer,
.css-slider .slide:focus ~ .slide .slide-outer,
.css-slider .slide:focus ~ .slide:last-child .slide-outer {
transform: translate(-150%,0);
transform: translate3D(-150%,0,0);
}
/* Before state */
.css-slider .slide .slide-outer {
transform: translate(200%,0);
transform: translate3D(200%,0,0);
}
/* Focused state*/
.css-slider .slide:target .slide-outer,
.css-slider .slide:focus .slide-outer,
.css-slider .slide:last-child .slide-outer {
transform: translate(0,0);
transform: translate3D(0,0,0);
}
/** --------------------------------------------------------------------------
* SMALL SCREEN FIX / SLIDE JERK (optional)
* ---------------------------------------------------------------------------
* When we shift 'left' and 'right' values -- in order to allow access to a future
* or past arrow -- this can cause a jump in the responsive scaling of the slide.
* if we transition the left value quickly, it can make this appear less jarring.
*/
.css-slider .slide {
transition-property: left, padding-left;
transition-duration: 1s;
transition-timing-function: ease;
}
/** --------------------------------------------------------------------------
* Add-on module : responsive images
* ------------------------------------------------------------------------ */
.with-responsive-images .slide-gfx img {
width: 100%;
height: auto;
}
/** --------------------------------------------------------------------------
* Add-on module : stop user selection
* ------------------------------------------------------------------------ */
/* if your slides don't need to be selectable, I recommend using this */
.with-selection-disabled {
user-select: none;
}
/** --------------------------------------------------------------------------
* Add-on module : initial fade in
* ------------------------------------------------------------------------ */
.with-fade-in .slide-gfx {
opacity: 0;
}
/* animate into visibility */
.with-fade-in .slide-gfx {
animation: css-slideshow-fade-in 2s;
animation-delay: 1s;
animation-fill-mode: forwards;
}
/* Vebdor animations */
@keyframes css-slideshow-fade-in { from { opacity: 0; } to { opacity: 1; } }
/** --------------------------------------------------------------------------
* Add-on module : slide reflection
* ------------------------------------------------------------------------ */
/* force our slide-gfx to be inline-block and relative positioned */
.with-reflection .slide-gfx {
display: inline-block !important;
}
/* reflection for webkit agents */
.with-reflection .slide-gfx > *:first-child {
-webkit-box-reflect: below 2px
-webkit-gradient(linear, left top, left bottom,
from(transparent), color-stop(0.9, transparent), to(white));
}
/* make sure internal images don't keep inline spacing/margin */
.with-reflection .slide-gfx img {
display: block;
}
/* generate the reflection */
.with-reflection .slide-gfx:after {
content: '';
position: absolute;
display: block;
mask: url("reflection-mask.svg#mask"); /* gradient fade the reflection */
transform: scaleY(-1); /* flip clone to appear as reflection */
opacity: 0.5; /* fade out reflection */
top: 100%;
width: 100%;
height: 60px;
z-index: 200;
margin-top: 2px;
}
/* again, due to element() requiring IDs we need arbitrary code
per each slide and each slide-gfx needs an id */
.with-reflection #s1:after { background: -moz-element(#s1) no-repeat left bottom; }
.with-reflection #s2:after { background: -moz-element(#s2) no-repeat left bottom; }
.with-reflection #s3:after { background: -moz-element(#s3) no-repeat left bottom; }
.with-reflection #s4:after { background: -moz-element(#s4) no-repeat left bottom; }
.with-reflection #s5:after { background: -moz-element(#s5) no-repeat left bottom; }
.with-reflection #s6:after { background: -moz-element(#s6) no-repeat left bottom; }
.with-reflection #s7:after { background: -moz-element(#s7) no-repeat left bottom; }
.with-reflection #s8:after { background: -moz-element(#s8) no-repeat left bottom; }
.with-reflection #s9:after { background: -moz-element(#s9) no-repeat left bottom; }
.with-reflection #s10:after { background: -moz-element(#s10) no-repeat left bottom; }
.with-reflection #s11:after { background: -moz-element(#s11) no-repeat left bottom; }
.with-reflection #s12:after { background: -moz-element(#s12) no-repeat left bottom; }
.with-reflection #s13:after { background: -moz-element(#s13) no-repeat left bottom; }
.with-reflection #s14:after { background: -moz-element(#s14) no-repeat left bottom; }
.with-reflection #s15:after { background: -moz-element(#s15) no-repeat left bottom; }
.with-reflection #s16:after { background: -moz-element(#s16) no-repeat left bottom; }
.with-reflection #s17:after { background: -moz-element(#s17) no-repeat left bottom; }
.with-reflection #s18:after { background: -moz-element(#s18) no-repeat left bottom; }
.with-reflection #s19:after { background: -moz-element(#s19) no-repeat left bottom; }
.with-reflection #s20:after { background: -moz-element(#s20) no-repeat left bottom; }
/** --------------------------------------------------------------------------
* Add-on module : slide zoom (optional, not compatible with slide float)
* ------------------------------------------------------------------------ */
.with-slide-zoom .slide .slide-gfx > *:first-child {
transition-property: max-width;
transition-duration: 2s;
transition-timing-function: ease-in-out;
transition-delay: 0.25s;
}
.with-slide-zoom .slide .slide-gfx > *:first-child:hover {
max-width: 1000px;
}
/** --------------------------------------------------------------------------
* Add-on module : slide float (optional, not compatible with slide zoom)
* ------------------------------------------------------------------------ */
/* inital transition set-up */
.with-slide-float:not(.with-slide-zoom) .slide .slide-gfx > *:first-child,
.with-slide-float-hover:not(.with-slide-zoom) .slide .slide-gfx > *:first-child {
transition-property: transform;
transition-duration: 2s;
transition-timing-function: ease-in-out;
}
/* we need a delay for the non-hover version */
.with-slide-float:not(.with-slide-zoom) .slide .slide-gfx > *:first-child {
transition-delay: 2s;
}
/* initial levitation on focus */
.with-slide-float:not(.with-slide-zoom) .slide:target .slide-gfx > *:first-child,
.with-slide-float:not(.with-slide-zoom) .slide:focus .slide-gfx > *:first-child,
.with-slide-float-hover:not(.with-slide-zoom) .slide .slide-gfx > *:first-child:hover {
transform: translate(0,-40px);
transform: translate3D(0,-40px,0);
}
/* trigger the float animation after 4s */
.with-slide-float:not(.with-slide-zoom) .slide:target .slide-gfx > *:first-child,
.with-slide-float:not(.with-slide-zoom) .slide:focus .slide-gfx > *:first-child,
.with-slide-float-hover:not(.with-slide-zoom) .slide .slide-gfx > *:first-child:hover {
animation: css-slideshow-levitate 4s;
animation-direction: alternate;
animation-fill-iteration-count: infinite;
animation-timing-function: ease-in-out;
animation-delay: 2s;
}
/* longer delay for automatic version i.e. non-hover */
.with-slide-float:not(.with-slide-zoom) .slide:target .slide-gfx > *:first-child,
.with-slide-float:not(.with-slide-zoom) .slide:focus .slide-gfx > *:first-child {
animation-delay: 4s;
}
/* Vebdor animations for the float */
@keyframes css-slideshow-levitate {
from { transform: translate(0,-40px); transform: translate3D(0,-40px,0); }
to { transform: translate(0,-20px); transform: translate3D(0,-20px,0); }
}
/** --------------------------------------------------------------------------
* Add-on module : ground shadow (optional)
* ------------------------------------------------------------------------ */
.with-shadow .slide .slide-gfx:before {
content: '';
background: url('slide-shadow.svg') no-repeat center center;
position: absolute;
bottom: -10px;
left: -20px;
right: -20px;
height: 20px;
z-index: -1;
opacity: 0.7;
}
.with-shadow.with-slide-float .slide .slide-gfx:before,
.with-shadow.with-slide-float-hover .slide .slide-gfx:before {
transition-property: opacity;
transition-duration: 2s;
transition-timing-function: ease-in-out;
}
.with-shadow.with-slide-float .slide .slide-gfx:before {
transition-delay: 2s;
}
.with-shadow.with-slide-float .slide:target .slide-gfx:before,
.with-shadow.with-slide-float .slide:focus .slide-gfx:before,
.with-shadow.with-slide-float .slide:last-child .slide-gfx:before,
.with-shadow.with-slide-float-hover .slide .slide-gfx:hover:before {
opacity: 0.1;
animation: css-slideshow-shadow 4s;
animation-delay: 4s;
animation-direction: alternate;
animation-fill-iteration-count: infinite;
animation-timing-function: ease-in-out;
}
.with-shadow.with-slide-float-hover .slide .slide-gfx:hover:before {
animation-delay: 2s;
}
/* Vebdor animations for the float */
@keyframes css-slideshow-shadow { from { opacity: 0.1; } to { opacity: 0.7; } }
http://codelamp.co.uk/css-slideshow/v0.2/
ご注意ください:ジャンプナビゲーション、つまり円形のドットはJavaScriptの小さなビットに依存しています。残りは純粋なCSSです。
私が言ったように、このシステムを改良するのにかなりの時間がかかりました。興味があるかもしれない人のために、これが私の(最後の)バージョン0.1です—以前はたくさんありました;)これは少し異なるアプローチをとり、視覚的およびインタラクティブな可動部分の両方を含みました。結局私はそれをより多くのブラウザ処理を含んでいたのでずっと不格好であったのでそれを捨てました。この無地のデモでは明らかにならないもの。