jQuery 幻灯片 url 在图像之间可变淡入淡出

jQuery slideshow with url's in variable fade between images

我创建了一个基于 jQuery 的幻灯片,它位于我网页上的 DIV 中。唯一的问题是图像之间没有过渡效果,只有一张到下一张,没有第一个慢慢淡出下一个淡出。

我想交叉淡入淡出这些图像。我的 JS 中缺少什么?

var urls = ['https://example.com/front.png',
 'https://example.com/interior-scaled.jpeg'];

  var count = 1;
  $('.hero').css('background-image', 'url("' + urls[0] + '")');
  setInterval(function() {
    $('.hero').css('background-image', 'url("' + urls[count] + '")');
    count == urls.length-1 ? count = 0 : count++;
  }, 6000);

});

LINK TO FIDDLE

您可以使用 transition: background-image。可能并非所有浏览器都支持它,但大多数现代浏览器应该没问题。

添加

-webkit-transition: background-image 0.5s ease-in-out;
transition: background-image 0.5s ease-in-out;

到有背景图片的div的css。

这是一个带有工作示例的分支 fiddle:https://jsfiddle.net/bmh2qu0e/1/

您可以在 opacity 上使用过渡并在背景变化时切换不透明度,例如:

$(document).ready(function() {
  var urls = ['https://concorddentalde.com/wp-content/uploads/2020/10/concord-dental-patient-exam-room.jpeg',
    'https://concorddentalde.com/wp-content/uploads/2020/10/concord-dental-interior-scaled.jpeg',
    'https://concorddentalde.com/wp-content/uploads/2020/10/concord-dental-front.png'
  ];

  var count = 1;
  var $hero = $('.hero');

  $hero.css('background-image', 'url("' + urls[0] + '")');
  setInterval(function() {
    setTimeout(function() {
      $hero.toggleClass('transparent');

      setTimeout(function() {
        $hero.css('background-image', 'url("' + urls[count] + '")');
        count == urls.length - 1 ? count = 0 : count++;
        $hero.toggleClass('transparent');
      }, 300);
    }, 300);
  }, 6000);

});
.transparent {
  opacity: 0;
}

.hero {
  height: 45%;
  height: 45vh;
  min-height: 400px;
  background-color: none;
  text-align: center;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: 50% 50%;
  -webkit-transition: opacity 0.5s ease-in-out;
  transition: opacity 0.5s ease-in-out;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="hero"></div>

如果您想更进一步,可以使用 class 使其更通用:

class ImageSlider {
  imagePos = 0;
  intevalHandle = null;
  intervalMS = 6000;

  constructor(elem, images, startImmediately) {
    this.images = images || [];
    this.elem = $(elem);

    if (startImmediately) {
      this.startSlider();
    }
  }

  startSlider() {
    if (this.startTimer()) {
      this.imagePos = 0;
      this.onTimerInterval();
    }
  };

  pauseSlider() {
    this.clearTimer();
  }

  resumeSlider() {
    this.startTimer();
  }

  stopSlider() {
    this.clearTimer();
    this.imagePos = 0;
  };

  startTimer() {
    if (this.intervalHandle != null) {
      return false;
    }

    this.intervalHandle = setInterval(() => this.onTimerInterval(), this.intervalMS);
    return true;
  };

  clearTimer() {
    if (this.intervalHandle) {
      this.clearInterval(this.intervalHandle);
      this.intervalHandle = null;
    }
  }

  onTimerInterval() {
    if (this.images.length <= 0) {
      return;
    }

    setTimeout(() => {
      this.elem.toggleClass('transparent');

      setTimeout(() => {
        if (this.imagePos >= this.images.length) {
          this.imagePos = 0;
        }

        this.elem.css('background-image', 'url("' + this.images[this.imagePos] + '")');
        this.imagePos++;
        this.elem.toggleClass('transparent');
      }, 300);
    }, 300);
  }
}

$(document).ready(function() {
  var urls = ['https://concorddentalde.com/wp-content/uploads/2020/10/concord-dental-patient-exam-room.jpeg',
    'https://concorddentalde.com/wp-content/uploads/2020/10/concord-dental-interior-scaled.jpeg',
    'https://concorddentalde.com/wp-content/uploads/2020/10/concord-dental-front.png'
  ];
  var slider1 = new ImageSlider('#ss1', urls, true);
  var slider2 = new ImageSlider('#ss2', [...urls].reverse(), true);
});
.transparent {
  opacity: 0;
}

.hero {
  height: 45%;
  height: 45vh;
  min-height: 400px;
  background-color: none;
  text-align: center;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: 50% 50%;
  -webkit-transition: opacity 0.5s ease-in-out;
  transition: opacity 0.5s ease-in-out;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="ss1" class="hero"></div>
<div id="ss2" class="hero"></div>

如果您不反对使用 jQuery 幻灯片库,那么我建议您使用 Ken Wheelers Slick 旋转木马 jQuery 库。

在您的第一条评论中,您提到...

even if images slide like a carousel would be sufficient.

好吧 Slick 可以轻松完成这两项工作,此外还有大量其他很酷的选项、事件回调和响应式断点设置。它可能会利用您已经在使用的 jQuery 为您的项目创建 sliding/fading 轮播。

我在下面的示例中包括了 2 个英雄幻灯片,都在 fade: false 模式下。

  • #Hero_1 幻灯片在图像加载或未加载之前运行。
  • #Hero_2 使用 $(window).on('load') 确保您的图像在幻灯片放映之前已加载

// our hero examples as constant variables
const hero_1 = $('#hero_1');
const hero_2 = $('#hero_2');

// our slide image urls in constant variable array 
const slides = [
  'https://concorddentalde.com/wp-content/uploads/2020/10/concord-dental-patient-exam-room.jpeg',
  'https://concorddentalde.com/wp-content/uploads/2020/10/concord-dental-interior-scaled.jpeg',
  'https://concorddentalde.com/wp-content/uploads/2020/10/concord-dental-front.png'
];

// for each of the slide images as key > url
$.each(slides, function(key, url) {

  // append slide to hero carousel div
  $('.carousel', '.hero').append('<div class="slide" style="background-image:url(\'' + url + '\');"></div>');

});

// the below slick js should not run until the above each function has finished appending images in slides array

// slick hero carousel on init
$('.carousel', hero_1).on('init', function(slick) {

  // add show class to hero div to animate height when slick init
  $(hero_1).addClass('show');

// slick carousel options
}).slick({
  slidesToShow: 1,
  slidesToScroll: 1,
  dots: false,
  arrows: false,
  fade: true,
  adaptiveHeight: false,
  autoplay: true,
  infinite: true,
  pauseOnFocus: false,
  pauseOnHover: false,
  autoplaySpeed: 4000,
  speed: 1000,
  draggable: false
});

// use this if you want all background images to load first
// tho may be slow to run depending on how many images and the image size you are loading

$(window).on('load', function() {

  // slick on init
  $('.carousel', hero_2).on('init', function(slick) {

    // add show class to hero div to expand height
    $(hero_2).addClass('show');

  // slick options
  }).slick({
    slidesToShow: 1,
    slidesToScroll: 1,
    dots: false,
    arrows: false,
    fade: true,
    adaptiveHeight: false,
    autoplay: true,
    infinite: true,
    pauseOnFocus: false,
    pauseOnHover: false,
    autoplaySpeed: 4000,
    speed: 1000,
    draggable: false
  });

});
.hero {
  position: relative;
  overflow: hidden;
  background: rgba(0, 0, 0, .75);
  min-height: 0;
  height: 0;
  transition: all 0.5s ease;
  margin: 0 0 .5rem 0;
}

.hero.show {
  min-height: 150px;
  height: 150px;
  /* 
  height:45%;
  height:45vh;
  min-height:400px;
  */
}

.hero .carousel {
  position: absolute;
  top: 0;
  right: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity 0.5s ease;
}

.hero .carousel.slick-initialized {
  opacity: 1;
}

.hero .carousel .slick-list,
.hero .carousel .slick-track {
  height: 100% !important;
}

.hero .carousel .slide {
  background-color: none;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: 50% 50%;
  height: 100%;
}

.hero .overlay {
  color: #fff;
  position: relative;
  text-align: center;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  text-shadow: 1px 1px 2px rgba(0, 0, 0, .75);
}


/* for demo styling purposes */

BODY {
  font-family: helvetica;
}

H1 {
  font-size: 2rem;
  font-weight: 600;
  margin: 0 0 .5rem 0;
}

P {
  margin: 0 0 .5rem 0;
}

.lead {
  font-size: 1.4rem;
  margin: 0 0 .5rem 0;
}

.row {
  margin: 0 -4px 0 -4px;
}

.col {
  float: left;
  width: calc(50% - 8px);
  padding: 0 4px 0 4px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.css" rel="stylesheet" />

<div class="row">
  <div class="col">

    <p>
      <code><strong>#hero_1</strong><br/></code>
      <code><small>Slick inits after each function is complete.</small></code>
    </p>

    <div id="hero_1" class="hero">
      <div class="carousel"></div>
      <div class="overlay">
        <h1>Hero 1</h1>
        <p class="lead">
          Tooth Hurty
        </p>
      </div>
    </div>

  </div>
  <div class="col">

    <p>
      <code><strong>#hero_2</strong></code><br/>
      <code><small>Waits for all imgs to load before init slick.</small></code>
    </p>

    <div id="hero_2" class="hero">
      <div class="carousel"></div>
      <div class="overlay">
        <h1>Hero 2</h1>
        <p class="lead">
          Tooth Hurty
        </p>
      </div>
    </div>

  </div>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.js"></script>


这是上面相同的代码,但在 fade: false 模式下...

  • #Hero_1 幻灯片在图像加载或未加载之前运行。
  • #Hero_2 使用 $(window).on('load') 确保您的图像在幻灯片放映之前已加载

// our hero examples as constant variables
const hero_1 = $('#hero_1');
const hero_2 = $('#hero_2');

// our slide image urls in constant variable array 
const slides = [
  'https://concorddentalde.com/wp-content/uploads/2020/10/concord-dental-patient-exam-room.jpeg',
  'https://concorddentalde.com/wp-content/uploads/2020/10/concord-dental-interior-scaled.jpeg',
  'https://concorddentalde.com/wp-content/uploads/2020/10/concord-dental-front.png'
];

// for each of the slide images as key > url
$.each(slides, function(key, url) {

  // append slide to hero carousel div
  $('.carousel', '.hero').append('<div class="slide" style="background-image:url(\'' + url + '\');"></div>');

});

// the below slick js should not run until the above each function has finished appending images in slides array

// slick hero carousel on init
$('.carousel', hero_1).on('init', function(slick) {

  // add show class to hero div to animate height when slick init
  $(hero_1).addClass('show');

// slick carousel options
}).slick({
  slidesToShow: 1,
  slidesToScroll: 1,
  dots: false,
  arrows: false,
  fade: false,
  adaptiveHeight: false,
  autoplay: true,
  infinite: true,
  pauseOnFocus: false,
  pauseOnHover: false,
  autoplaySpeed: 4000,
  speed: 1000,
  draggable: false
});

// use this if you want all background images to load first
// tho may be slow to run depending on how many images and the image size you are loading

$(window).on('load', function() {

  // slick on init
  $('.carousel', hero_2).on('init', function(slick) {

    // add show class to hero div to expand height
    $(hero_2).addClass('show');

  // slick options
  }).slick({
    slidesToShow: 1,
    slidesToScroll: 1,
    dots: false,
    arrows: false,
    fade: false,
    adaptiveHeight: false,
    autoplay: true,
    infinite: true,
    pauseOnFocus: false,
    pauseOnHover: false,
    autoplaySpeed: 4000,
    speed: 1000,
    draggable: false
  });

});
.hero {
  position: relative;
  overflow: hidden;
  background: rgba(0, 0, 0, .75);
  min-height: 0;
  height: 0;
  transition: all 0.5s ease;
  margin: 0 0 .5rem 0;
}

.hero.show {
  min-height: 150px;
  height: 150px;
  /* 
  height:45%;
  height:45vh;
  min-height:400px;
  */
}

.hero .carousel {
  position: absolute;
  top: 0;
  right: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity 0.5s ease;
}

.hero .carousel.slick-initialized {
  opacity: 1;
}

.hero .carousel .slick-list,
.hero .carousel .slick-track {
  height: 100% !important;
}

.hero .carousel .slide {
  background-color: none;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: 50% 50%;
  height: 100%;
}

.hero .overlay {
  color: #fff;
  position: relative;
  text-align: center;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  text-shadow: 1px 1px 2px rgba(0, 0, 0, .75);
}


/* for demo styling purposes */

BODY {
  font-family: helvetica;
}

H1 {
  font-size: 2rem;
  font-weight: 600;
  margin: 0 0 .5rem 0;
}

P {
  margin: 0 0 .5rem 0;
}

.lead {
  font-size: 1.4rem;
  margin: 0 0 .5rem 0;
}

.row {
  margin: 0 -4px 0 -4px;
}

.col {
  float: left;
  width: calc(50% - 8px);
  padding: 0 4px 0 4px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.css" rel="stylesheet" />

<div class="row">
  <div class="col">

    <p>
      <code><strong>#hero_1</strong><br/></code>
      <code><small>Slick inits after each function is complete.</small></code>
    </p>

    <div id="hero_1" class="hero">
      <div class="carousel"></div>
      <div class="overlay">
        <h1>Hero 1</h1>
        <p class="lead">
          Tooth Hurty
        </p>
      </div>
    </div>

  </div>
  <div class="col">

    <p>
      <code><strong>#hero_2</strong></code><br/>
      <code><small>Waits for all imgs to load before init slick.</small></code>
    </p>

    <div id="hero_2" class="hero">
      <div class="carousel"></div>
      <div class="overlay">
        <h1>Hero 2</h1>
        <p class="lead">
          Tooth Hurty
        </p>
      </div>
    </div>

  </div>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.js"></script>

您不能 cross-fade 使用 CSS 的单个背景图片。

一个可能的解决方案是在 hero <div> 里面放两个容器。

例如:

<div class="hero">
    <div class="img-container" id="first"></div>
    <div class="img-container" id="second"></div>
</div>

为了获得所需的交叉淡入淡出效果,您需要这些图像覆盖英雄顶部所需的区域 <div>

这可以通过这些 CSS 规则来完成:

.img-container {
    background-repeat: no-repeat;
    background-size: cover;
    background-position: 50% 50%;
    background-color: transparent;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

现在我们需要加载图像并 cross-fade 加载图像。

$(document).ready(function() {
  var urls = [
    'imgURL1',
    'imgURL2',
    'imgURL3'
  ];

  // Preload the images
  var tempImg = []
  for (var i = 0; i < urls.length; i++) {
    (new Image()).src = urls[i]
  }

  // The currently shown image's index
  var currentShown = 0;
  
  // Get the containers
  var first = $("#first");
  var second = $("#second");

  // This shows whether the second object is on top or not
  var secondOnTop = true;

  // Set the first container's value so that there is something on the screen and load the second image on top.
  first.css('background-image', 'url("' + urls[urls.length - 1] + '")');
  second.css({
    backgroundImage: 'url("' + urls[0] + '")',
    opacity: 1
  });

  // Change the image every X seconds
  setInterval(function() {

    var animationSpeed = 1000; // In milliseconds

    // Increment currently shown image index
    currentShown === urls.length - 1 ? currentShown = 0 : currentShown++;

    // Determine which object has visual priority
    var primaryObj = first;
    var auxObj = second;
    if (secondOnTop) {
      primaryObj = second;
      auxObj = first;
    }
    secondOnTop = !secondOnTop;

    // Show aux obj background
    auxObj.css({backgroundImage: 'url("' + urls[currentShown] + '")'});
    auxObj.animate({
      opacity: 1
    }, animationSpeed);

    // Change shown object's background and set to 0
    primaryObj.animate({
      opacity: 0,
    }, animationSpeed, function() {
      // Change the primary's background to the next in queue
      var nextImg = currentShown === urls.length - 1 ? 0 : currentShown + 1;
      primaryObj.css('background-image', 'url("' + urls[nextImg] + '")');
    });

  }, 6000);

});

我已经为你的 fiddle 创建了一个分支: https://jsfiddle.net/YeloPartyHat/uLfr389g/88/

这是一个解决方案:(我不得不将缩短的 url 替换为完整的 url 否则不会让我保存答案)

$(document).ready(function(){
var urls = ['https://concorddentalde.com/wp-content/uploads/2020/10/concord-dental-patient-exam-room.jpeg',
     'https://concorddentalde.com/wp-content/uploads/2020/10/concord-dental-interior-scaled.jpeg',
     'https://concorddentalde.com/wp-content/uploads/2020/10/concord-dental-front.png'];

        var totalLayers = 2;
      var layerIndex = 0;
      var count = 0;
      $('.layer-' + layerIndex)
    .removeClass('layer')
    .css('background-image', 'url("' + urls[count] + '")');
    console.log({first: layerIndex, second: (layerIndex + 1) % totalLayers, count})

      setInterval(function() {
        var outLayer = layerIndex
      var inLayer = ++layerIndex % totalLayers
      layerIndex = inLayer
        count = ++count % urls.length;
      console.log({first: outLayer, second: inLayer, count})
          $('.layer-' + outLayer)
      .addClass('animateXFadeOut');
        $('.layer-' + inLayer)
      .removeClass('layer')
      .css('background-image', 'url("' + urls[count] + '")')
      .addClass('animateXFadeIn');
      setTimeout(function() {
        $('.layer-' + outLayer).css({backgroundImage: 'none', opacity: 1});
        $('.layers').removeClass('animateXFadeIn animateXFadeOut');
      }, 1000);
      }, 6000);

    });
.hero {
/*  height: 45%;
    height: 45vh;
    min-height: 400px;
     */
  background-color: none;
    text-align: center;
    background-repeat: no-repeat;
    background-size: cover;
    background-position: 50% 50%;
}
@keyframes xfadein {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
@keyframes xfadeout {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}
.animateXFadeIn {
  animation-name: xfadein;
  animation-duration: 1s;
}
.animateXFadeOut {
  animation-name: xfadeout;
  animation-duration: 1s;
}
.layer-0, .layer-1 {
  display: block;
  position: absolute;
    height: 45%;
    height: 45vh;
    min-height: 400px;
  width: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="hero">
  <div class="layers layer-0"></div>
  <div class="layers layer-1"></div>
</div>

你可以用一小块 class 和一行 jquery 来做到这一点

$(document).ready(function(){
var urls = ['image_one_url',
     'image_two_url',
     'image_three_url'];

      var count = 1;
    
      $('.hero').css('background-image', 'url("' + urls[0] + '")');
    $('.hero').addClass('animatedinout');
    
      setInterval(function() {
        $('.hero').css('background-image', 'url("' + urls[count] + '")');
        count == urls.length-1 ? count = 0 : count++;
      }, 6000);

    });
.animatedinout{
  animation: fadeinout;
  animation-duration: 6000ms;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
}

@keyframes fadeinout{
  0%{
    opacity: 0;
  }
  10%{
    opacity: 1;
  }
  90%{
    opacity: 1;
  }
  100%{
    opacity: 0;
  }
}

我只是添加了一个名为 animatedinoutcss class,它每 6000 英里秒永远使用一个动画并添加

$('.hero').addClass('animatedinout'); 

就在你的 setInterval 之前。

感谢大家的辛勤工作。我找到的解决方案(因为我的截止日期很紧)实际上非常简单,它结合了 JS 和 CSS 来实现“真正的”交叉淡入淡出过渡。

HTML:

<div id="background-images">
    <div class="bgImages active" id="bgImg1"></div>
    <div class="bgImages" id="bgImg2"><br></div>
    <div class="bgImages" id="bgImg3"><br><br></div>
    <div class="bgImages" id="bgImg4"><br></div>
    <div class="bgImages" id="bgImg5"><br><br></div>
</div>

jQuery:

function cycleImages() {
  var $active = $("#background-images .active");
  var $next = $active.next().length > 0
    ? $active.next()
    : $("#background-images div:first");
  $next.css("z-index", 2); // move the next img up the stack
  $active.fadeOut(1500, function() {
    //fade out the top image
    $active.css("z-index", 1).show().removeClass("active"); //reset z-index and unhide the image
    $next.css("z-index", 3).addClass("active"); //make the next image the top one
  });
}

$(document).ready(function() {
  $("#cycler img").show();
  // run every 6 seconds
  setInterval(cycleImages, 6000);
});

CSS:

#background-images {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 670px;
  z-index: -5;
}
#bgImg1, #bgImg2, #bgImg3, #bgImg4, #bgImg5 {
  width: 100%;
  height: 100%;
  position: fixed;
  background-position-x: center;
  background-position-y: center;
  background-size: cover;
  background-repeat: no-repeat;
}
#bgImg1 { background-image: url("https://image1.jpg"); }
#bgImg2 { background-image: url("https://image2.png"); z-index: 2; }
#bgImg3 { background-image: url("https://image3.jpeg"); }
#bgImg4 { background-image: url("https://image4.jpg"); }
#bgImg5 { background-image: url("https://image5.jpeg"); }
  

这是使用 z-index 结合活动状态使图像真正交叉淡化而没有白色“闪烁”的聪明方法。

CodePen here 上找到它。