【问题标题】:CSS reveal from corner animationCSS从角落动画显示
【发布时间】:2017-12-16 22:52:42
【问题描述】:

我正在尝试实现如下动画效果:

显示横幅时,应该可以看到下一个横幅的右下角。当您单击此角时,它应该隐藏当前横幅并显示下一个。

我目前的标记如下:

<div class="banners">
    <div class="image active" style="background-color: red;">
        <div class="corner"></div>
    </div>

    <div class="image" style="background-color: blue;">
        <div class="corner"></div>
    </div>
</div>

CSS 如下:注意我使用clip-path 创建角:

.banners {
    width: 800px;
    height: 600px;
}

.image {
     width: 100%;
     height: 100%;
     position: absolute;
     left: 0;
     top: 0;
}

.image.active {
     z-index: 1;
     clip-path: polygon(100% 0, 100% 65%, 60% 100%, 0 100%, 0 0);
}

.corner {
    width: 50%;
    height: 50%;
    position: absolute;
    right: 0;
    bottom: 0;
}

JS:

$(document).ready(function () {
    $('.corner').click(function() {
        $('.image.active').removeClass('active');
        $(this).parent().addClass('active');
    });
});

这是上面所有代码的 JSFiddle:https://jsfiddle.net/cqqxjjgu/

一个直接的问题是,因为我使用z-index 来指定当前的“活动”横幅应该具有优先权,所以当删除active 类时它只会立即显示下一个横幅,所以理想情况下是@ 987654328@ 只能在动画完成后更改。

有没有人知道我怎样才能做到这一点?理想情况下,我需要一个跨浏览器解决方案(不要太在意 IE

【问题讨论】:

    标签: javascript jquery css css-animations


    【解决方案1】:

    一个无需 javascript 即可实现此效果的简单示例:
    https://jsfiddle.net/freer4/j2159b1e/2/

    html, body{
      height:100%;
      width:100%;
      margin:0;
      padding:0;
    }
    .banners {
      position:relative;
      background:#000;
      width: 100%;
      height: 100%;
      overflow:hidden;
    }
    .banners input{
      display:none;
    }
    .slide1{
      background-image: url(https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT5T6nwVYWsbzLcLF-JNxnGXFFFwkZMBcCMbaqeTevuldkxHg0N);
    }
    .slide2{
      background-image:url(http://www.rd.com/wp-content/uploads/sites/2/2016/02/06-train-cat-shake-hands.jpg);
    }
    .slide3{
      background-image:url(https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTKr6YlGNsqgJzvgBBkq1648_HsuDizVn_ZXC6iQp9kjXFzLvs1BA);
    }
    .image {
      display:block;
      height:100%;
      width:100%;
      position: absolute;
      overflow:hidden;
      z-index:1;
      text-align:center;
      background-position:0 0;
      background-size:cover;
      transition:z-index 1s step-end;
      clip-path: polygon(100% 0, 100% 100%, 100% 100%, 0 100%, 0 0);
      animation-duration: 2s;
      animation-name: clipout;
    }
    input:checked + .image{
      z-index:3;
      transition:z-index 1s step-end;
      clip-path: polygon(100% 0, 100% 50%, 50% 100%, 0 100%, 0 0);
      animation-duration: 2.2s;
      animation-name: clipin;
      cursor:default;
    }
    .image:nth-child(2),
    input:checked + * + * + .image{
      z-index:2;
      cursor:pointer;
    }
    
    
    .content{
      color:#FFF;
      display:inline-block;
      vertical-align:middle;
      font-family:arial;
      text-transform:uppercase;
      font-size:24px;
      opacity:0;
      transition:0s opacity 1s;
    }
    input:checked + .image .content{
      opacity:1;
      transition:0.8s opacity 0.8s;
    }
    .spanner{
      vertical-align:middle;
      width:0;
      height:100%;
      display:inline-block;
    }
    
    @keyframes clipout {
      from { clip-path: polygon(100% 0, 100% 50%, 50% 100%, 0 100%, 0 0); }
      50%  { clip-path: polygon(100% 0, 100% -100%, -100% 100%, 0 100%, 0 0); }
      51%   { clip-path: polygon(100% 0, 100% 100%, 100% 100%, 0 100%, 0 0); }
      to   { clip-path: polygon(100% 0, 100% 100%, 100% 100%, 0 100%, 0 0); }
    }
    @keyframes clipin{
      from { clip-path: polygon(100% 0, 100% 100%, 100% 100%, 0 100%, 0 0); }
      50%  { clip-path: polygon(100% 0, 100% 100%, 100% 100%, 0 100%, 0 0); }
      to   { clip-path: polygon(100% 0, 100% 50%, 50% 100%, 0 100%, 0 0); }  
    }
    <div class="banners">
      <input type="radio" id="slide1" name="slides" checked="checked" />
      <label class="image slide1" for="slide1">
        <div class="content">
          Slide 1
        </div>
        <div class="spanner"></div>
      </label>
      <input type="radio" id="slide2" name="slides"  />
      <label class="image slide2" for="slide2">
        <div class="content">
          Slide 2
        </div>
        <div class="spanner"></div>
      </label>
      <input type="radio" id="slide3" name="slides"  />
      <label class="image slide3" for="slide3">
        <div class="content">
          Slide 3
        </div>
        <div class="spanner"></div>
      </label>
    </div>

    基本上,只需使用关键帧为剪辑路径设置动画即可。喜欢 z-indexes 和一些兄弟选择器。

    【讨论】:

    • 这看起来太棒了!在 Firefox 中也能很好地工作。只有一个请求(很抱歉很痛苦) - 是否可以只使用div 标签来做到这一点?对于此功能,我认为inputlabel 不是语义上最正确的标签。让我知道你的想法。再次感谢。
    • @GSTAR 简短的回答是“不”。而且,一般来说,螺丝语义,只要它有效。除非你可能担心 SEO。在这种情况下,我可能有一个想法......但简单的答案是用类替换输入和兄弟选择器,然后使用类似于您的 OP 的 javascript 切换类
    • 太棒了。明天我会试一试。 IE11 或 Edge 不支持我刚刚注意到的一件事(可能早该注意到)clip-path。您是否知道任何可以在保持跨浏览器兼容性的同时产生角形状的类似技术?
    • @GSTAR 可能正在寻找类似的设置,但使用的是 canvas 或 svg
    • 而不是clip-path 只是transform: skew(20deg) rotate(20deg) 然后在孩子上你可以transform: skew(-20deg) rotate(-20deg) 来获得同样的效果。
    【解决方案2】:

    这应该适用于任何支持转换的浏览器: https://jsfiddle.net/freer4/cqqxjjgu/1/

    基本上,制作一张非常大的封面幻灯片,背景颜色与下一张幻灯片相同,然后将其拉到当前幻灯片上。然后淡出以显示下一张幻灯片。

    所以对html稍作调整:

    <div class="banners">
      <div class="image active" style="background-color: black;">
        <div class="content">
          Slide 1
        </div>
        <div class="spanner"></div>
        <div class="corner" style="background-color: cyan;"></div>      
      </div>
    
      <div class="image" style="background-color: cyan;">
        <div class="content">
          Slide 2
        </div>
        <div class="spanner"></div>
        <div class="corner" style="background-color: magenta;"></div>
      </div>
    
      <div class="image" style="background-color: magenta;">
        <div class="content">
          Slide 3
        </div>
        <div class="spanner"></div>
        <div class="corner" style="background-color: black;"></div>
      </div>
    </div>
    

    更改 jQuery 以选择下一张幻灯片或第一张(如果没有更多):

    $(document).ready(function () {
        $('.corner').click(function() {
            var $parent = $(this).parent();
            $parent.removeClass("active");
            if ($parent.next().length){
                $parent.next().addClass("active");
            } else {
                $parent.prevAll().last().addClass("active");
            }
        });
    });
    

    并设置一些复杂的过渡,您可以调整时间:

    .image {
        width: 100%;
        height: 100%;
        position: absolute;
        left: 0;
        top: 0;
        overflow:hidden;
        z-index:1;
        transition:z-index 2s step-end, 1s opacity 1s ease-in-out;
        text-align:center;
        opacity:0;
    }
    .image.active{
        opacity:1;
        z-index:2;
        transition:z-index 2s step-end, 0s opacity 0s;
    }
    
    .corner {
        width: 200%;
        height: 200%;
        position: absolute;
        top: -100%;
        left: -100%;
        clip-path: polygon(100% 0, 0 70%,  0 100%, 100% 100%, 100% 0, 100% 0);
        z-index:3;
        margin-left:150%;
        margin-top:150%;
        transition:2s top ease-in-out, 2s left ease-in-out, 0s margin 2s;
    }
    .image.active .corner{
      top:0;
      left:0;
      margin-top:0;
      margin-left:0;
      transition:0s top ease-in-out 1s, 0s left ease-in-out 1s, 2s margin ease-in-out 1s;
    }
    

    旁白:这个例子是完全灵活的(不关心大小):

    .banners {
        width: 100%;
        height: 100%;
    }
    

    或带图片:https://jsfiddle.net/freer4/ens7caaL/

    【讨论】:

    • 谢谢!这看起来很棒。通过将角的背景颜色设置为与下一张幻灯片相同的颜色,我可以看到您在那里所做的工作。我想知道如何增强它以支持背景图像?理想情况下,我不想将背景图像作为角落,因为我使用的轮播插件会自动调整图像大小,因此它可能并不总是匹配。
    • 刚刚注意到您创建了一个带有图像的演示!极好的!但是你认为这样做可以让我们不必在角落设置背景图像吗?
    • 谢谢。如果您对此有疑问,请不要担心 :) 顺便说一句,我注意到您的图像演示在 Firefox 中无法正常工作。看起来这是background-attachment 的问题。
    • @GSTAR 背景附件固定变得有点棘手。您能否为您尝试的效果提供实际图像的示例?它只是一张背景图片,还是你也会有内容?
    • 嗨,它们将是填充整个幻灯片的背景图像 - 基本上是您在图像演示中所做的 :) 每张幻灯片中都会有一个 .content div,但我没有认为这可能会导致任何问题。
    【解决方案3】:

    这是不使用clip-path 的答案,因为在svg 以外的DOM 元素上的浏览器兼容性很低。

    我现在看到 Vadim 对旋转容器的想法与我相同(直到我完成后才返回此处查看),但据我所知,我们的答案之间仍有足够的差异来证明发布我的解决方案是合理的:

    $(document).ready(function() {
      $(".slider").on("click",".next",function() {
        if ($(this).prev().length) {$(this).prev().removeClass("curr");} else {$(this).siblings().last().removeClass("curr");} //deactivate current slide
        if ($(this).next().length) {$(this).next().addClass("next");} else {$(this).siblings().first().addClass("next");} //prepare slide that follows next slide
        $(this).removeClass("next").addClass("curr"); //activate next slide
      });
    });
    .slider, .slider .img {
      width: 55vw;
      height: calc(55vw / 16*9);
      background: #000 center/contain no-repeat;
    }
    .slider {position:relative; margin:0 auto; overflow:hidden;}
    
    .slider .slide {
      position: absolute;
      z-index: 0;
      width: 250%;
      height: 0;
      transform: translateX(-50%) rotate(-20deg);
      transform-origin: 50% 0;
      transition:z-index 0s 0.7s, height 0.7s;
      overflow: hidden;
    }
    .slider .slide.next {z-index:1; height:155%; opacity:0.5; transition:z-index 0s 1.1s, height 0s 0.7s; cursor:pointer;}
    .slider .slide.curr {z-index:2; height:135%; opacity:1.0; transition:z-index 0s 1.1s, height 0.4s 0.7s, opacity 0.7s;}
    
    .slider .slide .img {margin-left:50%; transform:rotate(20deg); transform-origin:0 0;}
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    
    <div class="slider">
      <div class="slide curr"><div class="img" style="background-image:url(https://placeimg.com/640/480/animals);"></div></div>
      <div class="slide next"><div class="img" style="background-image:url(https://placeimg.com/640/480/people);"></div></div>
      <div class="slide"><div class="img" style="background-image:url(https://placeimg.com/640/480/nature);"></div></div>
      <div class="slide"><div class="img" style="background-image:url(https://placeimg.com/640/480/tech);"></div></div>
      <div class="slide"><div class="img" style="background-image:url(https://placeimg.com/640/480/arch);"></div></div>
    </div>
    codepen:https://codepen.io/anon/pen/JJQRvM
    jsfiddle:https://jsfiddle.net/tq2hw7b9/5/
    • 我只是更改了幻灯片容器的height,而不是clip-path,使用transition 来实现动画效果。

    IE 问题

    不幸的是,像往常一样,IE 处理transform:rotate() 的方式与其他浏览器不同。视觉上发生了旋转,但浏览器似乎仍然保留元素的原始空间,因此下一张幻灯片的暴露角不可点击,因为当前幻灯片正在“覆盖”它。使用 -ms--webkit- 前缀没有区别。

    以下代码 sn-p 可以在 IE 中工作:

    $(document).ready(function() {
      $(".slider .corner").on("click",function() {
      	var $next = $(this).siblings(".next");
        if ($next.prev().length) {$next.prev().removeClass("curr");} else {$next.siblings(".slide").last().removeClass("curr");} //deactivate current slide
        if ($next.next(".slide").length) {$next.next().addClass("next");} else {$next.siblings().first().addClass("next");} //prepare slide that follows next slide
        $next.removeClass("next").addClass("curr"); //activate next slide
      });
    });
    .slider, .slider .img {
      width: 55vw;
      height: calc(55vw / 16*9);
      background: #000 center/contain no-repeat;
    }
    .slider {position:relative; margin:0 auto; overflow:hidden;}
    
    .slider .corner {
      position: absolute;
      z-index: 3;
      bottom: 0;
      right: 0;
      width: 100%;
      height: 21%;
      transform: rotate(-20deg);
      transform-origin: 100% 0;
      cursor: pointer;
    }
    
    .slider .slide {
      position: absolute;
      z-index: 0;
      width: 250%;
      height: 0;
      transform: translateX(-50%) rotate(-20deg);
      transform-origin: 50% 0;
      transition:z-index 0s 0.7s, height 0.7s;
      overflow: hidden;
    }
    .slider .slide.next {z-index:1; height:155%; opacity:0.5; transition:z-index 0s 1.1s, height 0s 0.7s;}
    .slider .slide.curr {z-index:2; height:135%; opacity:1.0; transition:z-index 0s 1.1s, height 0.4s 0.7s, opacity 0.7s;}
    
    .slider .slide .img {margin-left:50%; transform:rotate(20deg); transform-origin:0 0;}
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    
    <div class="slider">
      <div class="slide curr"><div class="img" style="background-image:url(https://placeimg.com/640/480/animals);"></div></div>
      <div class="slide next"><div class="img" style="background-image:url(https://placeimg.com/640/480/people);"></div></div>
      <div class="slide"><div class="img" style="background-image:url(https://placeimg.com/640/480/nature);"></div></div>
      <div class="slide"><div class="img" style="background-image:url(https://placeimg.com/640/480/tech);"></div></div>
      <div class="slide"><div class="img" style="background-image:url(https://placeimg.com/640/480/arch);"></div></div>
      <div class="corner"></div>
    </div>
    codepen:https://codepen.io/anon/pen/GEbrzQ
    jsfiddle:https://jsfiddle.net/8ggqndj1/
    • 我添加了一个额外的&lt;div class="corner"&gt;,涵盖了所有幻灯片。
    • JS 中的点击处理程序现在绑定到这个.corner,并且在处理程序的开始处,对下一张幻灯片的引用被存储到一个变量中,该变量在其余代码中使用。
    • 在 CSS 中,.corner 也有一条新规则。

    JS 中的滑动数组

    查看下面的代码 sn-p,了解 JS 中的幻灯片列表(如果有人需要的话):

    $(document).ready(function() {
      var slides = [
      	2, //index for next slide
        "https://placeimg.com/640/480/animals",
        "https://placeimg.com/640/480/people",
        "https://placeimg.com/640/480/nature",
        "https://placeimg.com/640/480/tech",
        "https://placeimg.com/640/480/arch"
      ];
      
    //INITIALIZE SLIDESHOW--------------------
      $(".slider").css("background-image","url("+slides[2]+")"); //set next slide
      $(".slider .current .img").css("background-image","url("+slides[1]+")"); //set current slide, and set slide-height to slideshow-height
      
    //SLIDESHOW CLICK-HANDLER--------------------
      $(".slider .current").on("click",function(e){e.stopPropagation();});
      $(".slider").on("click",function() {
      	$(this).children(".current").animate({height:0},700,function(){
        	$(this).children(".img").css("background-image","url("+slides[slides[0]]+")"); //set the current slide to the next slide
        	$(this).css("height","155%"); //cover entire slide
          if (slides[0]==slides.length-1) {slides[0]=1;} else {++slides[0];} //increase/loop index for next slide
          $(this).parent().css("background-image","url("+slides[slides[0]]+")"); //set the next slide to the next slide after that
          $(this).animate({height:"135%"},400); //reveal corner for next slide
        });
      });
    });
    .slider, .slider .img {
      width: 55vw;
      height: calc(55vw / 16*9);
      background: #000 center/contain no-repeat;
    }
    .slider {margin:0 auto; cursor:pointer; overflow:hidden;}
    
    .slider .current {
      width: 250%;
      height: 135%;
      transform: translateX(-50%) rotate(-20deg);
      transform-origin: 50% 0;
      overflow: hidden;
      cursor: default;
    }
    .slider .current .img {margin-left:50%; transform:rotate(20deg); transform-origin:0 0;}
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    
    <div class="slider"><div class="current"><div class="img"></div></div></div>
    codepen:https://codepen.io/anon/pen/EXBgew
    jsfiddle:https://jsfiddle.net/qghv9bnh/13/
    • 它可能对某些人有一些偏好,尽管我认为我的第一个解决方案更简洁、更快、更灵活,可以添加额外的幻灯片(当然,当您使用 WordPress 或 Joomla 等 CMS 时):
      1. 只有在您实际使用滑块时才会加载图像,因此用户可以为他们不点击的每张幻灯片节省带宽。
      2. HTML 非常简洁,无论您拥有多少张幻灯片都不会增长,因此您的 HTML 看起来会更干净(但如果您使用 PHP 包含它们,它看起来会一样干净,甚至更干净)。

    真的想不出别的了,就像我说的,我更喜欢第一个。但无论如何,它可能会派上用场。

    【讨论】:

    • yours 是 2015 年 FF 和 IE11 中唯一适用于我的解决方案
    • @myfunkyside - 这是一个非常好的解决方案 - 也可以跨浏览器工作!我只是想知道您是否知道这是否可以与左上角的另一个 sn-p 一起使用,它将充当“上一张幻灯片”按钮?
    【解决方案4】:

    这适用于任何地方,即使在 IE/Edge 中也是如此。它基于 CSS transitions 并通过 JavaScript 替换 CSS 类。

    我正在使用旋转矩形来裁剪图像。主要原理演示(包含大量硬编码、先前计算的值):

    *,
    *:before,
    *:after {
      box-sizing: border-box;
    }
    
    body {
      margin: 0;
      height: 100vh;  
      display: flex;
      align-items: center;
      justify-content: center;
    }
    
    div {
      width: 300px;
      height: 200px;
      border: 1px solid red;
      position: relative;
    }
    
    div:after {
      content: "";
      position: absolute;
      left: -86.6px;
      top: 50px;
      width: 359.8px;
      height: 240px;
      transform-origin: 0 0;
      transform: rotate(-30deg);
      border: 1px solid blue;
    }
    &lt;div&gt;&lt;/div&gt;

    主要演示(有很多硬编码值)。为了更好地理解它的工作原理,您可以将border 添加到.slide-cropper

    $(document).ready(function() {
      $(".banners").on("click", ".slide-cropper.next .slide-content", function() {
        var $container = $(this).closest(".slide");
        
        $(".slide-cropper").removeClass("prev")
          .removeClass("current")
          .removeClass("next");
    
        $(this).closest(".slide-cropper").addClass("current");
    
        var $prevContainer;
        if ($container.prev().length) {
          $prevContainer = $container.prev();
        } else {
          $prevContainer = $container.siblings(":last");
        }
        $prevContainer.find(".slide-cropper").addClass("prev");
    
        var $nextContainer;
        if ($container.next().length) {
          $nextContainer = $container.next();
        } else {
          $nextContainer = $container.siblings(":first");
        }
        $nextContainer.find(".slide-cropper").addClass("next");
      });
    });
    *,
    *:before,
    *:after {
      box-sizing: border-box;
    }
    
    /* all body styles are just for demo */
    /* just centering the slider */
    body {
      margin: 0;
      height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
      overflow: hidden;
    }
    
    .banners {
      width: 300px;
      height: 200px;
      position: relative;
    }
    
    .slide {
      width: 100%;
      height: 100%;
    }
    
    .slide .slide-cropper {
      position: absolute;
      left: -86.6px;
      top: 50px;
      width: 359.8px;
      height: 323.2px;
      transform-origin: 0 0;
      transform: rotate(-30deg);
      overflow: hidden;
      transition: height 2s linear;
    }
    
    .slide-content {
      position: absolute;
      background-size: 100% 100%;
      left: 100px;
      top: 0;
      width: 300px;
      height: 200px;
      transform: rotate(30deg);
      transform-origin: 0 0;
      z-index: 0;
      
      /* just styles for text */
      /* using flexbox to center text */
      display: flex;
      justify-content: center;
      align-items: center;
      color: #fff;
      font-size: 3em;
    }
    
    .slide1 .slide-content {
      background-image: url("https://i.stack.imgur.com/tt875.jpg");
    }
    
    .slide2 .slide-content {
      background-image: url("https://i.stack.imgur.com/hzbmw.jpg");
    }
    
    .slide3 .slide-content {
      background-image: url("https://i.stack.imgur.com/4UxLW.jpg");
    }
    
    .slide-cropper.prev {
      height: 0;
      z-index: 3;
    }
    
    .slide-cropper.current {
      height: 240px;
      transition-delay: 2s;
      z-index: 2;
    }
    
    .slide-cropper.next {
      z-index: 1;
    }
    
    /* Fix for IE */
    .slide-cropper.current {
      pointer-events: none;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    
    <div class="banners">
      <div class="slide slide1">
        <div class="slide-cropper current">
          <div class="slide-content">
            Slide 1
          </div>
        </div>
      </div>
    
      <div class="slide slide2">
        <div class="slide-cropper next">
          <div class="slide-content">
            Slide 2
          </div>
        </div>
      </div>
    
      <div class="slide slide3">
        <div class="slide-cropper">
          <div class="slide-content">
            Slide 3
          </div>
        </div>
      </div>
    </div>

    为了了解它的工作原理,我将所有计算移至 CSS variables AKA CSS custom properties。它们适用于许多浏览器,但不能与 CSS calc 函数结合使用。此示例仅在 Chrome 中完美运行,但有助于理解和修改此示例(只需将 CSS 变量替换为计算出的硬编码值)。 您还可以将此计算移至 CSS 预处理器或 JavaScript 代码。

    $(document).ready(function() {
      $(".banners").on("click", ".slide-cropper.next .slide-content", function() {
        var $container = $(this).closest(".slide");
        
        $(".slide-cropper").removeClass("prev")
          .removeClass("current")
          .removeClass("next");
    
        $(this).closest(".slide-cropper").addClass("current");
    
        var $prevContainer;
        if ($container.prev().length) {
          $prevContainer = $container.prev();
        } else {
          $prevContainer = $container.siblings(":last");
        }
        $prevContainer.find(".slide-cropper").addClass("prev");
    
        var $nextContainer;
        if ($container.next().length) {
          $nextContainer = $container.next();
        } else {
          $nextContainer = $container.siblings(":first");
        }
        $nextContainer.find(".slide-cropper").addClass("next");
      });
    });
    *,
    *:before,
    *:after {
      box-sizing: border-box;
    }
    
    html {
      --width: 300px;
      --height: 200px;
      /* rotate for image cropping */
      --rotate-angle: 30deg;
      /* sin 30 degrees for image cropping */
      --sin-rotate-angle: 0.5;
      /* cos 30 degrees for image cropping */
      --cos-rotate-angle: 0.8660254037844386;
      /* clipper ratio for width, can be from 0 to 1 */
      --clipper-ratio: 0.45;
      --animation-timeout: 2s;
    }
    
    /* all body styles are just for demo */
    /* just centering the slider */
    body {
      margin: 0;
      height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
      overflow: hidden;
    }
    
    .banners {
      width: var(--width);
      height: var(--height);
      position: relative;
    }
    
    .slide {
      width: 100%;
      height: 100%;
    }
    
    .slide .slide-cropper {
      position: absolute;
      left: calc(-1 * var(--height) * var(--sin-rotate-angle) * var(--cos-rotate-angle));
      top: calc(var(--height) * var(--sin-rotate-angle) * var(--sin-rotate-angle));
      width: calc(var(--height) * var(--sin-rotate-angle) + var(--width) * var(--cos-rotate-angle));
      height: calc(var(--height) * var(--cos-rotate-angle) + var(--width) * var(--sin-rotate-angle));
      transform-origin: 0 0;
      transform: rotate(calc(-1 * var(--rotate-angle)));
      overflow: hidden;
      transition: height var(--animation-timeout) linear;
    }
    
    .slide-content {
      position: absolute;
      background-size: 100% 100%;
      left: calc(var(--height) / 2);
      width: var(--width);
      height: var(--height);
      transform: rotate(var(--rotate-angle));
      transform-origin: 0 0;
      z-index: 0;
      
      /* just styles for text */
      display: flex;
      justify-content: center;
      align-items: center;
      color: #fff;
      font-size: 3em;
    }
    
    .slide1 .slide-content {
      background-image: url("https://i.stack.imgur.com/tt875.jpg");
    }
    
    .slide2 .slide-content {
      background-image: url("https://i.stack.imgur.com/hzbmw.jpg");
    }
    
    .slide3 .slide-content {
      background-image: url("https://i.stack.imgur.com/4UxLW.jpg");
    }
    
    .slide-cropper.prev {
      height: 0;
      z-index: 3;
    }
    
    .slide-cropper.current {
      height: calc(var(--height) * var(--cos-rotate-angle) + var(--clipper-ratio) * var(--width) * var(--sin-rotate-angle));
      transition-delay: var(--animation-timeout);
      z-index: 2;
    }
    
    .slide-cropper.next {
      z-index: 1;
    }
    
    /* Fix for IE */
    .slide-cropper.current {
      pointer-events: none;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    
    <div class="banners">
      <div class="slide slide1">
        <div class="slide-cropper current">
          <div class="slide-content">
            Slide 1
          </div>
        </div>
      </div>
    
      <div class="slide slide2">
        <div class="slide-cropper next">
          <div class="slide-content">
            Slide 2
          </div>
        </div>
      </div>
    
      <div class="slide slide3">
        <div class="slide-cropper">
          <div class="slide-content">
            Slide 3
          </div>
        </div>
      </div>
    </div>

    要将其更改为全屏,您需要设置 --width: 100vw--height: 100vh。 (当然,您必须用硬编码值替换 CSS 变量才能在所有浏览器中工作)。演示:

    $(document).ready(function() {
      $(".banners").on("click", ".slide-cropper.next .slide-content", function() {
        var $container = $(this).closest(".slide");
        
        $(".slide-cropper").removeClass("prev")
          .removeClass("current")
          .removeClass("next");
    
        $(this).closest(".slide-cropper").addClass("current");
    
        var $prevContainer;
        if ($container.prev().length) {
          $prevContainer = $container.prev();
        } else {
          $prevContainer = $container.siblings(":last");
        }
        $prevContainer.find(".slide-cropper").addClass("prev");
    
        var $nextContainer;
        if ($container.next().length) {
          $nextContainer = $container.next();
        } else {
          $nextContainer = $container.siblings(":first");
        }
        $nextContainer.find(".slide-cropper").addClass("next");
      });
    });
    *,
    *:before,
    *:after {
      box-sizing: border-box;
    }
    
    html {
      --width: 100vw;
      --height: 100vh;
      /* rotate for image cropping */
      --rotate-angle: 30deg;
      /* sin 30 degrees for image cropping */
      --sin-rotate-angle: 0.5;
      /* cos 30 degrees for image cropping */
      --cos-rotate-angle: 0.8660254037844386;
      /* clipper ratio for width, can be from 0 to 1 */
      --clipper-ratio: 0.45;
      --animation-timeout: 2s;
    }
    
    /* all body styles are just for demo */
    /* just centering the slider */
    body {
      margin: 0;
      height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
      overflow: hidden;
    }
    
    .banners {
      width: var(--width);
      height: var(--height);
      position: relative;
    }
    
    .slide {
      width: 100%;
      height: 100%;
    }
    
    .slide .slide-cropper {
      position: absolute;
      left: calc(-1 * var(--height) * var(--sin-rotate-angle) * var(--cos-rotate-angle));
      top: calc(var(--height) * var(--sin-rotate-angle) * var(--sin-rotate-angle));
      width: calc(var(--height) * var(--sin-rotate-angle) + var(--width) * var(--cos-rotate-angle));
      height: calc(var(--height) * var(--cos-rotate-angle) + var(--width) * var(--sin-rotate-angle));
      transform-origin: 0 0;
      transform: rotate(calc(-1 * var(--rotate-angle)));
      overflow: hidden;
      transition: height var(--animation-timeout) linear;
    }
    
    .slide-content {
      position: absolute;
      background-size: 100% 100%;
      left: calc(var(--height) / 2);
      width: var(--width);
      height: var(--height);
      transform: rotate(var(--rotate-angle));
      transform-origin: 0 0;
      z-index: 0;
      
      /* just styles for text */
      display: flex;
      justify-content: center;
      align-items: center;
      color: #fff;
      font-size: 3em;
    }
    
    .slide1 .slide-content {
      background-image: url("https://i.stack.imgur.com/tt875.jpg");
    }
    
    .slide2 .slide-content {
      background-image: url("https://i.stack.imgur.com/hzbmw.jpg");
    }
    
    .slide3 .slide-content {
      background-image: url("https://i.stack.imgur.com/4UxLW.jpg");
    }
    
    .slide-cropper.prev {
      height: 0;
      z-index: 3;
    }
    
    .slide-cropper.current {
      height: calc(var(--height) * var(--cos-rotate-angle) + var(--clipper-ratio) * var(--width) * var(--sin-rotate-angle));
      transition-delay: var(--animation-timeout);
      z-index: 2;
    }
    
    .slide-cropper.next {
      z-index: 1;
    }
    
    /* Fix for IE */
    .slide-cropper.current {
      pointer-events: none;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    
    <div class="banners">
      <div class="slide slide1">
        <div class="slide-cropper current">
          <div class="slide-content">
            Slide 1
          </div>
        </div>
      </div>
    
      <div class="slide slide2">
        <div class="slide-cropper next">
          <div class="slide-content">
            Slide 2
          </div>
        </div>
      </div>
    
      <div class="slide slide3">
        <div class="slide-cropper">
          <div class="slide-content">
            Slide 3
          </div>
        </div>
      </div>
    </div>

    还演示了在 Firefox 中工作的 CSS 变量(Firefox 对 CSS 变量和 transform: rotate 的组合不友好,所以我只是将 transform: rotate 替换为硬编码值):

    $(document).ready(function() {
      $(".banners").on("click", ".slide-cropper.next .slide-content", function() {
        var $container = $(this).closest(".slide");
        
        $(".slide-cropper").removeClass("prev")
          .removeClass("current")
          .removeClass("next");
    
        $(this).closest(".slide-cropper").addClass("current");
    
        var $prevContainer;
        if ($container.prev().length) {
          $prevContainer = $container.prev();
        } else {
          $prevContainer = $container.siblings(":last");
        }
        $prevContainer.find(".slide-cropper").addClass("prev");
    
        var $nextContainer;
        if ($container.next().length) {
          $nextContainer = $container.next();
        } else {
          $nextContainer = $container.siblings(":first");
        }
        $nextContainer.find(".slide-cropper").addClass("next");
      });
    });
    *,
    *:before,
    *:after {
      box-sizing: border-box;
    }
    
    html {
      --width: 100vw;
      --height: 100vh;
      /* sin 30 degrees for image cropping */
      --sin-rotate-angle: 0.5;
      /* cos 30 degrees for image cropping */
      --cos-rotate-angle: 0.8660254037844386;
      /* clipper ratio for width, can be from 0 to 1 */
      --clipper-ratio: 0.45;
      --animation-timeout: 2s;
    }
    
    /* all body styles are just for demo */
    /* just centering the slider */
    body {
      margin: 0;
      height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
      overflow: hidden;
    }
    
    .banners {
      width: var(--width);
      height: var(--height);
      position: relative;
    }
    
    .slide {
      width: 100%;
      height: 100%;
    }
    
    .slide .slide-cropper {
      position: absolute;
      left: calc(-1 * var(--height) * var(--sin-rotate-angle) * var(--cos-rotate-angle));
      top: calc(var(--height) * var(--sin-rotate-angle) * var(--sin-rotate-angle));
      width: calc(var(--height) * var(--sin-rotate-angle) + var(--width) * var(--cos-rotate-angle));
      height: calc(var(--height) * var(--cos-rotate-angle) + var(--width) * var(--sin-rotate-angle));
      transform-origin: 0 0;
      transform: rotate(-30deg);
      overflow: hidden;
      transition: height var(--animation-timeout) linear;
    }
    
    .slide-content {
      position: absolute;
      background-size: 100% 100%;
      left: calc(var(--height) / 2);
      width: var(--width);
      height: var(--height);
      transform: rotate(30deg);
      transform-origin: 0 0;
      z-index: 0;
      
      /* just styles for text */
      display: flex;
      justify-content: center;
      align-items: center;
      color: #fff;
      font-size: 3em;
    }
    
    .slide1 .slide-content {
      background-image: url("https://i.stack.imgur.com/tt875.jpg");
    }
    
    .slide2 .slide-content {
      background-image: url("https://i.stack.imgur.com/hzbmw.jpg");
    }
    
    .slide3 .slide-content {
      background-image: url("https://i.stack.imgur.com/4UxLW.jpg");
    }
    
    .slide-cropper.prev {
      height: 0;
      z-index: 3;
    }
    
    .slide-cropper.current {
      height: calc(var(--height) * var(--cos-rotate-angle) + var(--clipper-ratio) * var(--width) * var(--sin-rotate-angle));
      transition-delay: var(--animation-timeout);
      z-index: 2;
    }
    
    .slide-cropper.next {
      z-index: 1;
    }
    
    /* Fix for IE */
    .slide-cropper.current {
      pointer-events: none;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    
    <div class="banners">
      <div class="slide slide1">
        <div class="slide-cropper current">
          <div class="slide-content">
            Slide 1
          </div>
        </div>
      </div>
    
      <div class="slide slide2">
        <div class="slide-cropper next">
          <div class="slide-content">
            Slide 2
          </div>
        </div>
      </div>
    
      <div class="slide slide3">
        <div class="slide-cropper">
          <div class="slide-content">
            Slide 3
          </div>
        </div>
      </div>
    </div>

    【讨论】:

    • 干得好瓦迪姆!我唯一要说的是,这里有很多固定的宽度/高度。我们如何确保这看起来响应完美?轮播将始终为视口的 100% 宽度和高度。
    • @GSTAR 我知道,写这篇文章花了很多时间,我打算很快提供公式。有很多sincos的计算,所以使用CSS预处理器或者计算一次就忘记了会更容易。
    • @GSTAR 你现在对我的演示有什么问题吗?
    • 这在 Chrome 中看起来很棒!在 Firefox 中它显示为对角线,您能看出问题所在吗?
    • @GSTAR 这是带有 CSS 变量的演示,它只能在 Chrome 中正常工作。要在任何地方使用它,您必须将 CSS 变量替换为硬编码值。这对你有意义吗?
    【解决方案5】:

    此示例适用于 Firefox、Chrome、IE。

    用于更改滑动规则更改transition

    $(document).ready(function () {
        $('.angle').click(function() {
            var $parent = $(this).parent();
            $parent.removeClass("current");
            if ($parent.next().length){
            	$parent.next().addClass("current");
            } else {
            	$parent.prevAll().last().addClass("current");
            }
        });
    });
    body{
      height:100%;
      width:100%;
    }
    
    .slideShow {
        width: 100%;
        height: 100%;
    }
    
    .image {
        width: 100%;
        height: 100%;
        position: absolute;
        left: 0;
        top: 0;
        overflow:hidden;
        z-index:1;
        transition:z-index 2s step-end, 1s opacity 1s ease-in-out;
        text-align:center;
        opacity:0;
        background-size:100% 100%;
        background-attachment:fixed;
    }
    .image.current{
        opacity:1;
        z-index:2;
        transition:z-index 2s step-end, 0s opacity 0s;
    }
    
    .angle {
        width: 200%;
        height: 200%;
        position: absolute;
        top: -100%;
        left: -100%;
        clip-path: polygon(100% 0, 0 70%,  0 100%, 100% 100%, 100% 0, 100% 0);
        z-index:3;
        margin-left:150%;
        margin-top:150%;
        transition:2s top ease-in-out, 2s left ease-in-out, 0s margin 2s;
        background-size:100% 100%;
        background-attachment:fixed;
    }
    .image.current .angle{
      top:0;
      left:0;
      margin-top:0;
      margin-left:0;
      transition:0s top ease-in-out 1s, 0s left ease-in-out 1s, 2s margin ease-in-out 1s;
    }
    .main{
      color:#FFF;
      display:inline-block;
      vertical-align:middle;
      font-family:arial;
      text-transform:uppercase;
      font-size:24px;
    }
    .middle{
      vertical-align:middle;
      width:0;
      height:100%;
      display:inline-block;
    }
    
    .image1, .image3 .angle{
      background-image: url(http://i3.imgbus.com/doimg/4c5o0m8m6o5n4e0.png);
    }
    .image1 .angle, .image2{
      background-image:url(http://i4.imgbus.com/doimg/1c7obm6m1o3nbb0.png);
    }
    .image2 .angle, .image3{
      background-image:url(http://i3.imgbus.com/doimg/ccbo5m2m8o8n759.jpg);
    }
    <div class="slideShow">
      <div class="image image1 current">
        <div class="main">
        </div>
        <div class="middle"></div>
        <div class="angle" style="background-color: cyan;"></div>      
      </div>
    
      <div class="image image2" style="background-color: cyan;">
        <div class="main">
        </div>
        <div class="middle"></div>
        <div class="angle" style="background-color: magenta;"></div>
      </div>
      
      <div class="image image3" style="background-color: magenta;">
        <div class="main">
        </div>
        <div class="middle"></div>
        <div class="angle"></div>
      </div>
    </div>

    您也可以使用Amazing Slider等SildeShow Maker软件。

    轻松下载,轻松使用:download link

    这是一个非常强大的软件。见截图

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-07-25
      • 1970-01-01
      • 1970-01-01
      • 2021-02-02
      • 1970-01-01
      • 2012-10-13
      • 1970-01-01
      相关资源
      最近更新 更多