使用下一个和上一个按钮实现仅限CSS的幻灯片/轮播?

时间:2014-02-08 14:43:56

标签: css slideshow carousel

一段时间以来,我一直在尝试实现仅限CSS的幻灯片,其中包括:

  1. 提供前进和后退导航。
  2. 不修改导航历史记录。
  3. 为内容的移动指明明确的方向。
  4. 在尽可能多的浏览器中工作。
  5. 我遇到的大多数其他CSS幻灯片都没有勾选所有这些框。

    值得庆幸的是,我花了这么长时间以来浏览器本身已经改进了无端,现在实际上它已经非常广泛,尽管有一些“现代”的CSS主义。为了防止其他人对我有用,我想我会在这里发布。

    那么如何使用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>
    

1 个答案:

答案 0 :(得分:48)

simple screenshot

一个工作实例

http://jsfiddle.net/q4d9m/2/

  

UPDATE: There seems to be a bug with Firefox 32 (Mac)这意味着省略号不会在SVG模板中呈现,这会导致反射失败......并且不会让我开始使用Chrome 37.0。 2062.120(Mac)如果您将鼠标悬停在此答案脚下的实施示例中的任何图像上。

     

- 2014年9月18日


解释基本原理

幻灯片由两个主要部分构成,即视觉部分和交互式部分。交互式部分几乎保持静态,视觉部分是动画的。经过多次游戏后,我扩展了最初的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翻译只会影响幻灯片的视觉内容,交互式部分仍然是层叠在一起,填充整个视口区域,差不多......

illustration of previous, next and current slides

我之所以说几乎是因为为了在下一张和之前的幻灯片上触发焦点 - 通过鼠标或触摸 - 他们的交互式图层的一部分必须是用户可访问和可点击的。使用leftrightpadding的混合,可以在不移动视觉元素的情况下显示正确的区域。

snippets of key css

因此,随着当前聚焦的幻灯片的变化,可访问的交互层的区域也会发生变化。每次用户点击下一个或上一个箭头时,他们实际上都会关注相邻的<li>元素,而不是单击执行任何操作的链接。

illustration of interactive layer positioning


有用知道

  1. 为了使其在Webkit浏览器上运行,需要使用tabindex属性,以便:focus Pseudo-class可以在用于表示a的任何基本元素上运行滑动。

  2. 由于使用了技巧,幻灯片将以反向顺序播放。

  3. 您可以使用标签浏览幻灯片,但是,由于第2点,它会向后导航。如果您有Mac,则可能需要tweak your os settings才能显示标签。

  4. 由于展示导航箭头的leftright技巧,向前导航时会出现轻微的视觉故障 - 您可以看到后续的上一个箭头快速动画到位

  5. 由于此系统在焦点丢失时基于:focus工作,幻灯片显示会恢复到初始幻灯片,因此,子链接无法在幻灯片中使用 - 除非您增强与JavaScript的交互。

  6. 我的演示使用了SVG背景图片,这些图像显然是可选的,不适用于旧浏览器。


  7. 支持

    1. Firefox v26.0(Mac / PC~ 很可能早期版本
    2. Chrome v32(Mac / PC~ 很可能早期版本
    3. Safari v7(Mac / PC~ 很可能是早期版本
    4. Opera v18(Mac / PC~ 很可能早期版本
    5. Internet Explorer 9+(PC)
    6.   

      IE7&amp; IE8甚至无法理解:last-child:nth-child,所以不,为他们工作。


      添加额外内容

      在下面的演示中,您将看到有一些JavaScript蓬勃发展,这些可以帮助展示构造可以做什么,或者它们逐步增强。复选框仅适用于演示,应允许您启用或禁用某些功能。这些功能由简单的类应用:

      1. .with-responsive-images 〜小黑客强迫图片自动调整大小。
      2. .with-selection-disabled 〜阻止用户拖动和突出显示。
      3. .width-fade-in 〜最初淡入幻灯片。
      4. .with-reflection 〜启用firefox和webkit的反射。
      5. .with-slide-zoom 〜悬停时,幻灯片将缩放到最大宽度。
      6. .with-slide-float 〜焦距会使幻灯片悬浮。
      7. .with-slide-float-hover 〜悬停时幻灯片会悬浮。
      8. .with-shadow 〜一个穷人的反思。
      9.   

        请注意:反射插件依赖于任意标记属性。您必须为每个.slide添加唯一ID,然后扩展CSS以将其考虑在内。


        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>元素很可能是第二个孩子,但在使用之前应该检查。

        .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 ~)。为什么要求任何一个孩子都是为了让我们能够专注于&#34;当没有当前焦点时,即页面加载时的初始幻灯片。

        .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伪类,基本上这已经实现了以支持&#34;跳转导航&#34;。有两个原因可以解释为什么我不会赞美跳跃导航&#34;:

        1. 部分由JavaScript提供支持。
        2. 它依赖于#hash或#fragment值,由于创建了历史记录状态,因此可能会破坏您网站的可用性。
        3. 无论如何,选择过去幻灯片的技巧取决于General Sibling Selector。以下构造基本上是指在.slide之后找到的 select .slide,其中: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声明在没有任何焦点时介入,如果您不做进一步更改,您会发现某些事情会中断。这是因为: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;
          }
          


          最后烦人的z-index hack

          到目前为止,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; } }
          

          simple screenshot of implemented example


          实施例(v0.2)

          http://codelamp.co.uk/css-slideshow/v0.2/

            

          请注意:跳转导航,即圆点确实依赖于一小部分JavaScript。剩下的就是纯CSS。


          以前的版本

          正如我所说,改进这个系统需要相当多的时间。对于那些可能感兴趣的人,这里是我的(最后)版本0.1 - 以前有过很多;)这采用了略有不同的方法,涉及视觉和交互式移动部件。最后我废弃它,因为它涉及更多的浏览器处理,因此更加笨重;这个纯色演示没有透露的东西。

          http://jsfiddle.net/3cyP8/7/