CSS从角落动画揭示

26

我想实现以下动画效果:

当一个横幅广告显示时,下一个横幅广告的右下角应该可见。当你点击这个角落时,它应该隐藏当前的横幅广告并显示下一个。

我当前的标记如下:

<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来创建角落:


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'横幅应具有优先权,所以当删除active类时,它会立即显示下一个横幅,因此理想情况下,z-index应该在动画完成后才更改。

有人有任何想法如何实现这一点吗?理想情况下,我需要一个跨浏览器的解决方案(对IE < 10不太担心)。

5个回答

36

没有使用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-index和一些兄弟选择器来提高精度。


2
这看起来很棒!在Firefox中也运行得非常好。只有一个请求(抱歉有点麻烦)-是否可能仅使用div标签来完成此操作?我认为inputlabel不是此功能的最语义化正确的标签。让我知道你的想法。再次感谢。 - MAX POWER
@GSTAR 简短的回答是“不行”。但总的来说,语义并不重要,只要它能工作就好。除非你担心SEO问题。如果是这样,我可能有一个想法...但简单的答案是用类替换输入和兄弟选择器,然后使用JavaScript切换类,就像你的OP一样。 - Randy Hall
太棒了。我明天会试一下。还有一件事,我刚刚注意到(可能应该早点注意),clip-path在IE11或Edge中不受支持。您知道任何类似的技术可以产生角形状,同时保持跨浏览器兼容性吗? - MAX POWER
1
@GSTAR 可能正在查看类似的设置,但使用画布或SVG。 - Randy Hall
1
不要使用 clip-path,而是使用 transform: skew(20deg) rotate(20deg),然后在子元素上使用 transform: skew(-20deg) rotate(-20deg) 来获得相同的效果。 - Josh Burgess

9
这应该适用于任何支持转场效果的浏览器: 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/


谢谢!这看起来很棒。我可以看到你是通过将角落的背景颜色设置为下一张幻灯片的相同颜色来完成的。我想知道如何增强它以支持背景图片?理想情况下,我不想把背景图放在角落,因为我正在使用的旋转木马插件会自动调整图片大小,所以可能不总是匹配。 - MAX POWER
@GSTAR 背景附件固定有点棘手。你能提供一下你尝试实现的效果的实际图片示例吗?这只是一个背景图像,还是你也会有内容呢? - Randy Hall
嗨,它们将是填充整个幻灯片的背景图像 - 基本上就像您在图像演示中所做的那样 :) 每个幻灯片中都会有一个.content div,但我认为这不太可能引起任何问题。 - MAX POWER
@GSTAR 不是这个答案中的那一个,我也发布了完整的第二个答案 :) - Randy Hall
@GSTAR 更新了另一个答案,请查看最终解决方案。在许多情况下应该可以很顺利地设置任何大小。 - Randy Hall
显示剩余6条评论

5
这将在任何地方都起作用,甚至在IE/Edge中也是如此。它基于CSS过渡和通过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;
}
<div></div>

主要演示(有许多硬编码的值)。为了更好地理解它是如何工作的,您可以将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://istack.dev59.com/tt875.webp");
}

.slide2 .slide-content {
  background-image: url("https://istack.dev59.com/hzbmw.webp");
}

.slide3 .slide-content {
  background-image: url("https://istack.dev59.com/4UxLW.webp");
}

.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变量,又称CSS自定义属性中。它们在许多浏览器中都能正常工作,但不能与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://istack.dev59.com/tt875.webp");
}

.slide2 .slide-content {
  background-image: url("https://istack.dev59.com/hzbmw.webp");
}

.slide3 .slide-content {
  background-image: url("https://istack.dev59.com/4UxLW.webp");
}

.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://istack.dev59.com/tt875.webp");
}

.slide2 .slide-content {
  background-image: url("https://istack.dev59.com/hzbmw.webp");
}

.slide3 .slide-content {
  background-image: url("https://istack.dev59.com/4UxLW.webp");
}

.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://istack.dev59.com/tt875.webp");
}

.slide2 .slide-content {
  background-image: url("https://istack.dev59.com/hzbmw.webp");
}

.slide3 .slide-content {
  background-image: url("https://istack.dev59.com/4UxLW.webp");
}

.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>


干得好,Vadim!我只想说的是这里有很多固定宽度/高度。我们如何确保在响应式设计中看起来完美?轮播图将始终是视口的100%宽度和高度。 - MAX POWER
@GSTAR 我知道,写这个花了很多时间,我计划很快提供公式。有很多 sincos 计算,所以最好使用 CSS 预处理器或者计算一次然后忘记它。 - Vadim Ovchinnikov
@GSTAR,你现在对我的演示有任何问题吗? - Vadim Ovchinnikov
@GSTAR 这是一个使用CSS变量的演示,它只能在Chrome中正常工作。如果要在任何地方都能正常工作,您需要将CSS变量替换为硬编码的值。这对您有意义吗? - Vadim Ovchinnikov
老实说,我认为这不可能,因为它在IE或Edge中根本没有显示。在此代码中使用CSS变量的地方很多,更改它们都会很困难。无论如何,我已经在这里发布了一个类似的问题-也许你可以帮忙?https://stackoverflow.com/questions/45165937/css-next-previous-clip-path-animation - MAX POWER
显示剩余3条评论

5
这是一个没有使用clip-path的答案,因为DOM元素上除了svg之外的浏览器兼容性较低。
我现在明白了,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/

  • 我并没有使用clip-path, 而是改变了幻灯片容器的height, 并使用transition来实现动画效果。

IE问题

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

以下代码片段在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/

  • 我添加了一个额外的 <div class="corner">,它覆盖了所有幻灯片。
  • JS 中的点击处理程序现在绑定到此 .corner,并且在处理程序的开头,下一张幻灯片的引用被存储到一个变量中,该变量在代码的其余部分中使用。
  • CSS 中也有一个新的规则适用于 .corner

JS 中的幻灯片数组

请查看下面的代码片段,以获取 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将它们包含在内,它看起来也同样干净,甚至更清洁)。

我真的想不出还有什么了,正如我所说,我更喜欢第一种方法。但是对于其他人来说,它可能也很有用。


你的解决方案是我从2015年开始在Firefox和IE11中唯一有效的解决方案。 - deblocker
@myfunkyside - 这是一个非常好的解决方案 - 跨浏览器也可以工作!我只是想知道,您是否知道这是否可以与位于左上角的另一个代码片段一起使用,该代码片段将充当“上一页”按钮? - MAX POWER

0

这个示例在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这样的幻灯片制作软件。

轻松下载,易于使用:下载链接

它是非常强大的软件。查看屏幕截图{{link3:here}}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接