成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

還在用 Swiper.js 嗎?CSS實現帶指示器的 Swiper

開發 前端
隨著CSS地不斷發展,現在純CSS也幾乎能夠實現這樣一個swiper了,實現更加簡單,更加輕量,性能也更好,完全足夠日常使用,最近在項目中也碰到了一個swiper的需求,剛好練一下手,一起看看吧!

幾乎每個前端開發都應該用過這個滑動組件庫吧?這就是大名鼎鼎的swiper.js

沒想到已經出到 11 個大版本了 https://www.swiper.com.cn/

當然我也不例外,確實非常全面,也非常強大。

不過很多時候,我們可能只用到了它的10%不到的功能,顯然是不劃算的,也會有性能方面的顧慮。

隨著CSS地不斷發展,現在純CSS也幾乎能夠實現這樣一個swiper了,實現更加簡單,更加輕量,性能也更好,完全足夠日常使用,最近在項目中也碰到了一個swiper的需求,剛好練一下手,一起看看吧!

一、CSS 滾動吸附

swiper有一個最大的特征就是滾動吸附。相信很多同學已經想到了,那就是CSS scroll snap,這里簡單介紹一下。

看似屬性非常多,其實CSS scroll snap最核心的概念有兩個,一個是scroll-snap-type,還一個是scroll-snap-align,前者是用來定義吸附的方向和吸附程度的,設置在「滾動容器」上。后者是用來定義吸附點的對齊方式的,設置在「子元素」上。

有了這兩個屬性,就可以很輕松的實現滾動吸附效果了,下面舉個例子。

<div class="swiper">
  <div class="swiper-item">
    <div class="card"></div>
  </div>
  <div class="swiper-item">
    <div class="card"></div>
  </div>
  <div class="swiper-item">
    <div class="card"></div>
  </div>
</div>

簡單修飾一下,讓swiper可以橫向滾動。

.swiper {
  display: flex;
  overflow: auto;
}
.swiper-item {
  width: 100%;
  display: flex;
  justify-content: center;
  flex-shrink: 0;
}
.card {
  width: 300px;
  height: 150px;
  border-radius: 12px;
  background-color: #9747FF;
}

效果如下:

然后加上scroll-snap-type和scroll-snap-align。

.swiper {
  /**/
  scroll-snap-type: x mandatory;
}
.swiper-item {
  /**/
  scroll-snap-align: center;
}

這樣就能實現滾動吸附了。

注意這里還有一個細節,如果滑動的非常快,是可以從第一個直接滾動到最后一個的,就像這樣。

如果不想跳過,也就是每次滑動只會滾動一屏,可以設置scroll-snap-stop屬性,他可以決定是否“跳過”吸附點,默認是normal,可以設置為always,表示每次滾動都會停止在最近的一個吸附點。

.swiper-item {
  scroll-snap-align: center;
  scroll-snap-stop: always;
}

這樣無論滾動有多快,都不會跳過任何一屏了。

還有一點,現在是有滾動條的,顯然是多余的。

這里可以用::-webkit-scrollbar去除滾動條。

::-webkit-scrollbar{
  width: 0;
  height: 0;
}

滑動基本上就這樣了,下面來實現比較重要的指示器。

二、CSS 滾動驅動動畫

首先我們加幾個圓形的指示器。

<div class="swiper">
  <div class="swiper-item">
    <div class="card"></div>
  </div>
  <div class="swiper-item">
    <div class="card"></div>
  </div>
  <div class="swiper-item">
    <div class="card"></div>
  </div>
  <!--指示器-->
  <div class="pagination">
    <i class="dot"></i>
    <i class="dot"></i>
    <i class="dot"></i>
  </div>
</div>

用絕對定位定在下方。

.pagination {
  position: absolute;
  display: inline-flex;
  justify-content: center;
  bottom: 10px;
  left: 50%;
  transform: translateX(-50%);
  gap: 4px;
}
.dot {
  width: 6px;
  height: 6px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.36);
  transition: 0.3s;
}

效果如下:

那么,如何讓下方的指示器跟隨滾動而變化呢?

在這里,我們可以再單獨繪制一個高亮的狀態,剛好覆蓋在現在的指示器上,就用偽元素來代替。

.pagination::before{
  content: '';
  position: absolute;
  width: 6px;
  height: 6px;
  border-radius: 3px;
  background-color: #F24822;
  left: 0;
}

效果如下:

然后給這個高亮狀態一個動畫,從第一個指示器位置移動到最后一個。

.pagination::after{
  /**/
  animation: move 3s linear forwards;
}
@keyframes move {
  to {
    left: 100%;
    transform: translateX(-100%);
  }
}

現在這個紅色的圓會自動從左到右運動,效果如下:

最后,讓這個動畫和滾動關聯起來,也就是滾動多少,這個紅色的圓就運動多少。

.swiper {
  /**/
  scroll-timeline: --scroller x;
}
.pagination::after{
	/**/
  animation: move 3s linear forwards;
  animation-timeline: --scroller;
}

這樣就基本實現了指示器的聯動。

當然,你還可以換一種動畫形式,比如steps。

.pagination::after{
	/**/
  animation: move 3s steps(3, jump-none) forwards;
  animation-timeline: --scroller;
}

效果如下(可能會更常見)。

你也可以訪問以下在線demo

  • CSS swiper (juejin.cn)[1]

三、CSS 時間線范圍

上面的指示器實現其實是通過覆蓋的方式實現的,這就意味著無法實現這種有尺寸變化的效果,例如:

這種情況下,每個指示器的變化是獨立的,而且尺寸變化還會相互擠壓。

那么,有沒有辦法實現這樣的效果呢?當然也是有的,需要用到 CSS 時間線范圍,也就是 timeline-scope。

https://developer.mozilla.org/en-US/docs/Web/CSS/timeline-scope

這是什么意思呢?默認情況下,CSS 滾動驅動作用范圍只能影響到子元素,但是通過timeline-scope,可以讓任意元素都可以受到滾動驅動的影響。簡單舉個例子。

<div class="content">
  <div class="box animation"></div>
</div>

<div class="scroller">
  <div class="long-element"></div>
</div>

這是兩個元素,右邊的是滾動容器,左邊的是一個可以旋轉的矩形。

我們可以在他們共同的父級,比如body定義一個timeline-scope。

body{
  timeline-scope: --myScroller;
}

然后,滾動容器的滾動和矩形的動畫就可以通過這個變量關聯起來了。

.scroller {
  overflow: scroll;
  scroll-timeline-name: --myScroller;
  background: deeppink;
}
.animation {
  animation: rotate-appear;
  animation-timeline: --myScroller;
}

效果如下:

我們回到這個例子中來,很明顯每個卡片對應一個指示器,但是他們從結構上又不是包含關系,所以這里也可以給每個卡片和指示器一個相關聯的變量,具體實現如下:

<div class="swiper-container" style="timeline-scope: --t1,--t2,--t3;">
  <div class="swiper" style="--t: --t1">
    <div class="swiper-item">
      <div class="card">1</div>
    </div>
    <div class="swiper-item" style="--t: --t2">
      <div class="card">2</div>
    </div>
    <div class="swiper-item"  style="--t: --t3">
      <div class="card">3</div>
    </div>
  </div>
  <div class="pagination">
    <i class="dot" style="--t: --t1"></i>
    <i class="dot" style="--t: --t2"></i>
    <i class="dot" style="--t: --t3"></i>
  </div>
</div>

然后,給每個指示器添加一個動畫。

@keyframes move {
  50% {
    width: 12px;
    border-radius: 3px 0px;
    border-color: rgba(0, 0, 0, 0.12);
    background: #fff;
  }
}

效果如下:

然后我們需要將這個動畫和卡片的滾動關聯起來,由于是需要監聽卡片的位置狀態,比如只有第二個出現在視區范圍內時,第二個指示器才會變化,所以這里要用到view-timeline,關鍵實現如下:

.swiper-item {
  /**/
  view-timeline: var(--t) x;
}
.dot {
  /**/
  animation: move 3s;
  animation-timeline: var(--t);
}

這樣就實現了我們想要的效果。

你也可以訪問以下在線demo

  • CSS swiper timeline scope (juejin.cn)[2]

四、CSS 自動播放

由于是頁面滾動,CSS 無法直接控制,所以要換一種方式。通常我們會借助JS定時器實現,但是控制比較麻煩。

沒錯,我們這里也可以用這個原理實現。

給容器定義一個無關緊要的動畫。

.swiper {
  animation: scroll 3s infinite; /*每3s動畫,無限循環*/
}
@keyframes scroll {
  to {
    transform: opacity: .99; /*無關緊要的樣式*/
  }
}

然后監聽animationiteration事件,這個事件表示每次動畫循環就觸發一次,也就相當于每3秒執行一次。

swiper.addEventListener("animationiteration", (ev) => {
  // 輪播邏輯
  if (ev.target.offsetWidth+ev.target.scrollLeft >= ev.target.scrollWidth) {
    // 滾動到最右邊了直接回到0
    ev.target.scrollTo({
      left: 0,
      behavior: "smooth",
    })
  } else {
    // 每次滾動一屏
    ev.target.scrollBy({
      left: ev.target.offsetWidth,
      behavior: "smooth",
    });
  }
})

相比定時器的好處就是,可以直接通過CSS控制播放和暫停,比如我們要實現當鼠標放在輪播上是自動暫停,可以這樣來實現,副作用更小

swiper:hover, .swiper:active{
  animation-play-state: paused; /*hover暫停*/
}

最終效果如下:

你也可以訪問以下在線demo

  • CSS swiper autoplay (juejin.cn)[3]

五、回調事件

swiper很多時候不僅僅只是滑動,還需要有一個回調事件,以便于其他處理。這里由于是滾動實現,所以有必要監聽scroll事件。

實現很簡單,只需要監聽滾動偏移和容器本身的尺寸就可以了,具體實現如下:

swiper.addEventListener("scroll", (ev) => {
  const index =  Math.floor(swiper.scrollLeft / swiper.offsetWidth)
  console.log(index)
})

效果如下:

你可能覺得觸發次數太多了,我們可以限制一下,只有改變的時候才觸發。

swiper.addEventListener("scroll", (ev) => {
  const index =  Math.floor(swiper.scrollLeft / swiper.offsetWidth)
  // 和上次不相同的時候才打印
  if (swiper.index!== index) {
    swiper.index = index
    console.log(index)
  }
})

現在就好一些了。

還可以繼續優化,當滑動超過一半時,就認為已經滑到下一個卡片了,只需要在原有基礎上加上0.5就行了。

swiper.addEventListener("scroll", (ev) => {
  const index =  Math.floor(swiper.scrollLeft / swiper.offsetWidth + 0.5)
  if (swiper.index!== index) {
    swiper.index = index
    console.log(index)
  }
})

效果如下:

如果在 vue這樣的框架里,就可以直接這樣實現了。

const current = ref(0)
const scroll = (ev: Event) => {
  const swiper = ev.target as HTMLDivElement
  if (swiper) {
    current.value = Math.floor(swiper.scrollLeft / swiper.offsetWidth + 0.5)
  }
}
const emits = defineEmits(['change'])
watch(current, (v) => {
  emits('change', v)
})

六、兼容性處理

前面提到的CSS滾動驅動動畫兼容性不是很好,需要Chrome 115+,所以對于不支持的瀏覽器,你也可以用監聽回調事件的方式來實現指示器聯動,就像這樣。

swiper.addEventListener("scroll", (ev) => {
  const index =  Math.floor(swiper.scrollLeft / swiper.offsetWidth + 0.5)
  if (swiper.index!== index) {
    swiper.index = index
    console.log(index)
    if (!CSS.supports("animation-timeline","scroll()")) {
      document.querySelector('.dot[data-current="true"]').dataset.current = false
      document.querySelectorAll('.dot')[index].dataset.current = true
    }
  }
})

對于 CSS部分,還需要用CSS support判斷一下,這樣一來,不支持瀏覽器就不會自動播放動畫了。

@supports (animation-timeline: scroll()) {
  .dot{
    animation: move 1s;
    animation-timeline: var(--t);
  }
}
@supports not (animation-timeline: scroll()) {
  .dot[data-current="true"]{
    width: 12px;
    border-radius: 3px 0px;
    border-color: rgba(0, 0, 0, 0.12);
    background: #fff;
  }
}

這樣既使用了最新的瀏覽器特性,又兼顧了不支持的瀏覽器,下面是Safari的效果。

對比一下支持animation-timeline的瀏覽器(chrome 115+)。

你會發現,這種效果更加細膩,指示器是完全跟隨滾動進度變化的。

也算一種體驗增強吧,你也可以訪問以下在線demo

  • CSS swiper support (juejin.cn)

七、總結一下

做好兼容,CSS 也是可以嘗試最新特性的,下面總結一下要點

  • swiper 非常強大,我們平時可能只用到了它的10%不到的功能,非常不劃算。
  • CSS發展非常迅速,完全可以借助 CSS代替部分swiper。
  • 滾動吸附比較容易,需要借助CSS scroll snap完成。
  • 指示器聯動可以用CSS滾動驅動動畫實現,讓指示器唯一動畫和滾動關聯起來,也就是滾動多少,指示器就偏移多少。
  • 默認情況下,CSS 滾動驅動作用范圍只能影響到子元素,但是通過timeline-scope,可以讓任意元素都可以受到滾動驅動的影響。
  • 利用timeline-scope,我們可以將每個卡片的位置狀態和每個指示器的動畫狀態聯動起來。
  • 自動播放可以借助animationiteration回調事件,相比JS定時器,控制更加方便,副作用更小。
  • 回調事件需要監聽scroll實現,只需要監聽滾動偏移和容器本身的尺寸的比值就行了。
  • 對于不兼容的瀏覽器,也可以通過回調事件手動關聯指示器的狀態。
  • 兼容性判斷,JS可以使用CSS.supports,CSS可以使用@supports。

當然,swiper的功能遠不止上面這些,但是我們平時遇到的需求可能只是其中的一小部分,大可以通過CSS方式去實現,充分發揮瀏覽器的特性,量身定制才會有足夠的性能和體驗。

[1]CSS swiper (juejin.cn): https://code.juejin.cn/pen/7391010495207047205

[2]CSS swiper timeline scope (juejin.cn): https://code.juejin.cn/pen/7391018122460954636

[3]CSS swiper autoplay (juejin.cn): https://code.juejin.cn/pen/7391025055079890995

責任編輯:姜華 來源: 前端偵探
相關推薦

2024-11-12 16:28:34

2022-09-13 17:54:55

CSS定時器監聽事件

2022-11-14 18:43:03

JSCSS節流

2009-08-27 12:58:44

C#索引指示器

2012-07-19 10:03:32

2023-08-08 14:31:42

輪播圖鴻蒙

2021-11-02 16:44:40

部署DevtoolsJRebel

2021-07-17 15:31:20

ChromeHTTPS瀏覽器

2024-06-11 00:00:00

前端輪播圖硬件

2021-01-03 17:14:16

ORMObjective S運行

2020-03-04 14:05:35

戴爾

2025-04-02 08:47:23

DOM文檔結構API

2024-09-18 09:18:11

2022-05-06 16:12:40

定時器CSS前端

2021-02-21 11:09:18

鴻蒙HarmonyOS應用開發

2021-06-15 15:28:31

谷歌Android開發

2024-10-11 16:34:22

2019-02-21 10:17:45

Windows 10 剩余時間電池壽命

2021-04-08 07:51:24

CSS 處理圖片漸變疊加

2025-06-11 08:10:00

JavaScripAPI代碼
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产成人福利 | 久久精品小视频 | 国产精品日韩一区二区 | 欧美一区二区三区免费在线观看 | 成人在线小视频 | 2022国产精品| 在线观看的av | 黄色一级电影在线观看 | 精品国产18久久久久久二百 | 免费在线观看成人av | 国产成人a亚洲精品 | 亚洲一区二区三区免费观看 | 久久天堂 | 日韩高清国产一区在线 | 91久久精品国产 | 精品成人一区二区 | 久久鲁视频 | 在线精品一区 | 久久久久亚洲 | 日韩在线视频免费观看 | 亚洲网站在线播放 | 欧美成人精品一区二区三区 | 日本一区不卡 | 欧美日韩综合 | 国产成人免费视频 | 蜜桃视频在线观看免费视频网站www | 欧美成ee人免费视频 | 天天爽天天操 | 久久99精品视频 | 免费看国产精品视频 | 久久性 | 91麻豆精品一区二区三区 | 成人在线免费视频观看 | 一区二区三区精品视频 | 99re在线视频观看 | 日韩中文一区 | 亚洲视频中文 | 欧美激情va永久在线播放 | av网站免费观看 | 国产成人综合在线 | 亚洲一区中文字幕 |