国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

原生js系列之無限循環(huán)輪播組件

劉玉平 / 3378人閱讀

摘要:沒有看過上一篇文章的話,可以在這里找到原生系列之工廠模式。那么這篇文章,我們將基于上述的,從頭開始寫一個(gè)無限循環(huán)輪播圖的組件。附無限循環(huán)輪播圖示例本文源碼

前情回顧

在上一篇文章中,我們封裝了一個(gè)DOM庫(qnode),為了讓大家直觀地感受到其方便友好的自定義工廠模式,于是給大家?guī)砹诉@篇文章。

沒有看過上一篇文章的話,可以在這里找到:原生js系列之DOM工廠模式。

那么這篇文章,我們將基于上述的qnode,從頭開始寫一個(gè)無限循環(huán)輪播圖的組件。

思路講解

先看一張輪播布局圖:

滑動(dòng)的時(shí)候,整個(gè)輪播容器整體前進(jìn)或后退一格,通過css3過渡效果的設(shè)置,來達(dá)到滑動(dòng)的效果。也許你會(huì)疑惑,頭尾怎么會(huì)多出兩張圖呢?

其實(shí)無限循環(huán)輪播的核心就在于頭尾多出的兩張圖,從圖三再向后滑動(dòng),會(huì)滑到紅色圖一(我稱之為占位圖一),這個(gè)時(shí)候給用戶的感覺就是無縫從最后一張滑動(dòng)到第一張的,當(dāng)他滑到占位圖一時(shí),我們再瞬間切換到粉色圖一(即真正的圖一),由于是瞬間變換,用戶是感知不到的。同理,從圖一滑到圖三也一樣。由此,周而復(fù)始,無窮無盡,給人的感覺是永遠(yuǎn)也不會(huì)到盡頭,當(dāng)然個(gè)中奧妙只有我們知道哈哈。

目錄結(jié)構(gòu)
swiper
├── README.md
├── index.js
├── qnode
│?? ├── index.js
│?? ├── method.js
│?? └── store.js
├── render
│?? ├── index.js
│?? ├── indicator.js
│?? └── list.js
└── styles
    ├── indicator.mcss
    ├── list.mcss
    └── wrap.mcss

說明:mcss文件是通過css-modules來編譯的,給class名稱生成唯一標(biāo)識(shí),防止命名沖突。這里有我配置好的一套腳手架,覺得webpack配置麻煩的話,可以clone我這個(gè)項(xiàng)目來編譯代碼:webpack-build。

代碼編寫

index.js

import qnode from "./qnode"
import render from "./render"

const defaults = {
  initIndex: 1,
  autoplay: {
    use: true,
    delay: 3000
  },
  slide: {
    use: true,
    scale: 1 / 3,
    speed: 0.2
  },
  indicator: {
    use: true,
    bottom: "",
    dotClass: "",
    dotActiveClass: ""
  }
}

export default function swiper (node, {
  datas,
  initIndex,
  slide,
  autoplay,
  indicator
}) {
  if (!node || !datas || !datas.length) return

  // 儲(chǔ)存數(shù)據(jù)的前后順序很重要,一定要在調(diào)用前設(shè)置
  qnode.setStore("datas", datas)
  qnode.setStore("index", (initIndex || defaults.initIndex) - 1)
  qnode.setStore("slide", Object.assign({}, defaults.slide, slide))
  qnode.setStore("autoplay", Object.assign({}, defaults.autoplay, autoplay))
  qnode.setStore("indicator", Object.assign({}, defaults.indicator, indicator))

  // 渲染dom并儲(chǔ)存在qnode,以便后續(xù)的獲取和操作
  render()

  // 自動(dòng)輪播
  qnode.execMethod("autoplay")
  // 滑動(dòng)翻頁
  qnode.execMethod("slide")

  // 掛載到真實(shí)的節(jié)點(diǎn)上
  qnode.getNode("wrap").appendTo(node)
}

render/index.js

import qnode from "../qnode"
import renderList from "./list"
import renderIndicator from "./indicator"

import mcss from "../styles/wrap.mcss"

export default function () {
  renderList() // 渲染列表
  renderIndicator() // 渲染指示器,若沒有開啟則不會(huì)渲染

  qnode.setNode("wrap", "$div")
    .addClass(mcss.wrap)
    .append([
      qnode.getNode("list"),
      qnode.getNode("indicator") // 有可能沒有值,這一層我們的qnode會(huì)過濾調(diào),所以放心大膽地寫
    ])
}

render/list.js

import { isElement, isString } from "@m/utils/is"
import qnode from "../qnode"

import mcss from "../styles/list.mcss"

function getItemNode (data) {
  const qItem = qnode.q("$div").addClass(mcss.item)

  if (isElement(data)) {
    return qItem.append(data)
  }

  if (isString(data)) {
    return qItem.html(data)
  }

  return qItem.html(`
    
      
    
  `)
}

export default function () {
  const datas = qnode.getStore("datas")
  const tdTime = qnode.getStore("tdTime")
  const posIndex = qnode.getStore("index") + 1

  const qItems = datas.map(item => getItemNode(item))

  // 首位多插入一個(gè)節(jié)點(diǎn),用于視覺感知,交互完成后瞬間替換到相應(yīng)的節(jié)點(diǎn)
  qItems.unshift(getItemNode(datas[datas.length - 1]))
  qItems.push(getItemNode(datas[0]))

  qnode.setNode("list", "$div")
    .addClass(mcss.list)
    .style({
      transitionDuration: tdTime + "ms",
      transform: `translateX(${posIndex * -100}%)`
    })
    .append(qItems)
}

render/indicator.js

import qnode from "../qnode"

import mcss from "../styles/indicator.mcss"

export default function () {
  const indicator = qnode.getStore("indicator")
  const last = qnode.getStore("datas").length - 1
  const index = qnode.getStore("index")
  const dotClass = indicator.dotClass || mcss.dot
  const dotActiveClass = indicator.dotActiveClass || mcss.dotActive

  if (indicator.use) {
    let qDots = []
    for (let i = 0; i <= last; i++) {
      qDots.push(
        qnode.q("$div").addClass(dotClass, (i === index) && dotActiveClass)
      )
    }

    qnode.setNode("dots", qDots)
    qnode.setStore("dotActiveClass", dotActiveClass)
    qnode.setNode("indicator", "$div")
      .addClass(mcss.indicator)
      .style("bottom", indicator.bottom)
      .append(qDots)
  }
}

qnode/index.js

import { QNode } from "@m/qnode"
import { tdTime } from "./store"
import { change, autoplay, slide, indicator } from "./method"

const qnode = new QNode()

qnode.setStore("tdTime", tdTime)

qnode.setMethod("change", change)
qnode.setMethod("autoplay", autoplay)
qnode.setMethod("slide", slide)
qnode.setMethod("indicator", indicator)

export default qnode

qnode/store.js

// 靜態(tài)數(shù)據(jù)可以放在這里
export const tdTime = 500

qnode/method.js

import touchSlide from "./touchSlide"

// 翻頁處理
export function change (isNext) {
  let index = this.getStore("index")
  let cacheIndex = index // 用于記錄上一次的索引,移除指示器激活樣式時(shí)使用
  let last = this.getStore("datas").length - 1
  let tdTime = this.getStore("tdTime")
  let qList = this.getNode("list")
  let isNextContinue = isNext && (index === last)
  let isPrevContinue = !isNext && (index === 0)
  let posIndex = index + (isNext ? 2 : 0)

  if (isNextContinue || isPrevContinue) {
    // 滑到占位圖
    qList.style("transform", `translateX(${posIndex * -100}%)`)
    index = isNextContinue ? 0 : last

    setTimeout(() => {
      qList.style({
        transitionDuration: "0ms",
        transform: `translateX(${(index + 1) * -100}%)`
      })
    }, tdTime)
  } else {
    qList.style({
      transitionDuration: tdTime + "ms",
      transform: `translateX(${posIndex * -100}%)`
    })
    index += isNext ? 1 : -1
  }

  this.setStore("index", index)
  this.execMethod("indicator", cacheIndex, index)
}

// 自動(dòng)輪播
export function autoplay () {
  let opt = this.getStore("autoplay")

  if (!opt.use) return

  let timer = setInterval(() => {
    this.execMethod("change", true)
  }, opt.delay)

  this.setStore("timer", timer)
}

// 滑動(dòng)處理
export function slide () {
  let qWrap = this.getNode("wrap")
  let qList = this.getNode("list")
  let tdTime = this.getStore("tdTime")
  let slideData = this.getStore("slide")
  let self = this

  if (!slideData.use) return

  touchSlide(qWrap.current(), {
    delay: 0,
    start () {
      // 清除輪播定時(shí)器和css3過渡效果
      clearTimeout(self.getStore("timer"))
      qList.style("transitionDuration", "0ms")
    },
    move (info) {
      let posIndex = self.getStore("index") + 1
      let move = info.disX / qWrap.width() * 100
      let total = posIndex * -100 + move

      qList.style("transform", `translateX(${total}%)`)
    },
    end (info) {
      // 開啟輪播和css3過渡效果
      self.execMethod("autoplay")
      qList.style("transitionDuration", tdTime + "ms")

      let posIndex = self.getStore("index") + 1
      let scale = Math.abs(info.disX) / qWrap.width()
      let speed = Math.abs(info.speedX)

      if (scale >= slideData.scale || speed >= slideData.speed) {
        self.execMethod("change", info.disX < 0) // 翻頁
      } else {
        qList.style("transform", `translateX(${posIndex * -100}%)`)
      }
    }
  })
}

// 修改指示器索引
export function indicator (lastIndex, currIndex) {
  const qDots = this.getNode("dots")
  const dotActiveClass = this.getStore("dotActiveClass")

  if (qDots && dotActiveClass) {
    qDots[lastIndex].removeClass(dotActiveClass)
    qDots[currIndex].addClass(dotActiveClass)
  }
}

touchSlide.js

// 截流
function throttle (fn, delay = 100) {
  let wait = false

  return function () {
    if (!wait) {
      fn && fn.apply(this, arguments)
      wait = true

      setTimeout(() => {
        wait = false
      }, delay)
    }
  }
}

/**
 *
 * 滑動(dòng)
 * @param {HTMLElement} node
 * @param {Object} {
 *   delay = 100, // move截流時(shí)間
 *   start, // 滑動(dòng)開始
 *      參數(shù): pageX, pageY
 *   move, // 滑動(dòng)中,會(huì)不斷地觸發(fā),可以通過截流來限制觸發(fā)頻率
 *      參數(shù):
            time, // 總時(shí)間:ms
            disX, // 總路程:px
            disY,
            addX, // 路程增量:px
            addY,
            speedX: disX / time, // 平均速度:px/ms
            speedY: disY / time
 *   end, // 滑動(dòng)結(jié)束,參數(shù)同move
 * }
 */
export default function (node, {
  delay = 100,
  start,
  move,
  end
}) {
  if (!node) return

  let sTouch, eTouch, sTime
  let touch, time, disX, disY, addX, addY

  node.addEventListener("touchstart", e => {
    e.preventDefault()

    sTime = e.timeStamp
    sTouch = eTouch = e.targetTouches[0]

    start && start({
      pageX: sTouch.pageX,
      pageY: sTouch.pageY
    })
  }, false)

  node.addEventListener("touchmove", throttle(e => {
    touch = e.targetTouches[0]
    time = e.timeStamp - sTime
    disX = touch.pageX - sTouch.pageX
    disY = touch.pageY - sTouch.pageY
    addX = touch.pageX - eTouch.pageX
    addY = touch.pageY - eTouch.pageY

    move && move({
      time, // 總時(shí)間:ms
      disX, // 總路程:px
      disY,
      addX, // 路程增量:px
      addY,
      speedX: disX / time, // 平均速度:px/ms
      speedY: disY / time
    })

    // 記錄上一次touch
    eTouch = touch
  }, delay), false)

  node.addEventListener("touchend", e => {
    touch = e.changedTouches[0]
    time = e.timeStamp - sTime
    disX = touch.pageX - sTouch.pageX
    disY = touch.pageY - sTouch.pageY
    addX = touch.pageX - eTouch.pageX
    addY = touch.pageY - eTouch.pageY

    end && end({
      time,
      disX,
      disY,
      addX,
      addY,
      speedX: disX / time,
      speedY: disY / time
    })
  }, false)
}

styles/wrap.mcss

.wrap {
  position: relative;
  overflow: hidden;
  transform: translate3d(0, 0, 0);
}

styles/list.mcss

.list {
  display: flex;
  flex-direction: row;
  transform: translateX(0);
  transition: transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.item {
  flex-basis: 100%;
  flex-shrink: 0;
  box-sizing: border-box;

  a {
    display: block;
    font-size: 0;

    img {
      width: 100%;
      height: auto;
    }
  }
}

styles/indicator.mcss

.indicator {
  position: absolute;
  bottom: 1em;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
}

.dot {
  width: 1em;
  height: 0.12em;
  margin: 0 0.12em;
  background-color: rgba(255, 255, 255, 0.5);

  &-active {
    background-color: #fff;
  }
}
README 參數(shù)

node: 要掛載的dom節(jié)點(diǎn),必須

options: 如下(其中datas是必要的)

{
  initIndex: 1, // 初始化展示的索引
  autoplay: { // 自動(dòng)輪播設(shè)置
    use: true, // 開關(guān)
    delay: 3000 // 間隔3s
  },
  slide: { // 手指滑動(dòng)設(shè)置
    use: true, // 開關(guān)
    scale: 1/3, // 劃過總共寬度的1/3則翻頁
    speed: 0.2 // 滑動(dòng)的速度超過0.2px/ms則翻頁,即快速滑動(dòng)也可以翻頁
  },
  indicator: { // 索引指示器設(shè)置
    use: true, // 開關(guān)
    bottom: "", // 底部的距離
    dotClass: "", // 自定義圓點(diǎn)樣式
    dotActiveClass: "" // 自定義激活樣式
  },
  datas: [ // 圖片數(shù)據(jù)
    {
      src: "xxx", // 圖片URL
      href: "/", // 圖片錨點(diǎn),可以不設(shè)置
      target: "_blank" // 點(diǎn)擊錨點(diǎn)的跳轉(zhuǎn)處理(是在當(dāng)前頁打開還是新建窗口)
    }
  ]
}
示例
import swiper from "@c/swiper"

import img1 from "./images/1.jpg"
import img2 from "./images/2.jpg"
import img3 from "./images/3.jpg"
import img4 from "./images/4.jpg"
import img5 from "./images/5.jpg"
import img6 from "./images/6.jpg"

const rootNode = document.getElementById("root")

swiper(rootNode, {
  // initIndex: 1,
  // autoplay: {
  //   use: true,
  //   delay: 3000
  // },
  // slide: {
  //   use: true,
  //   scale: 1/3,
  //   speed: 0.2
  // },
  // indicator: {
  //   use: true,
  //   bottom: "",
  //   dotClass: "",
  //   dotActiveClass: ""
  // },
  datas: [
    {
      src: img1,
      href: "/",
      target: "_blank"
    },
    {
      src: img2,
      href: "/",
      target: "_blank"
    },
    {
      src: img3,
      href: "/",
      target: "_blank"
    },
    {
      src: img4,
      href: "/",
      target: "_blank"
    },
    {
      src: img5,
      href: "/",
      target: "_blank"
    },
    {
      src: img6,
      href: "/",
      target: "_blank"
    }
  ]
})
使用心得

總體來說使用qnode來開發(fā)的話還是比較方便的,文件拆分以及數(shù)據(jù)共享都可以做到,唯一有一點(diǎn)瑕疵的話,就是對于js執(zhí)行的順序要慎重考慮。想一想為什么render文件暴露出來的是函數(shù),原因就是因?yàn)榇藭r(shí)數(shù)據(jù)還未儲(chǔ)存到qnode,因此通過函數(shù)來進(jìn)行惰性加載,在合適的地方執(zhí)行。

對于qnode,目前還沒有錯(cuò)誤提醒,調(diào)用方式不對的話沒有信息吐出,后續(xù)可以考慮補(bǔ)上這個(gè)功能,畢竟其他開發(fā)者用的話,可能并不熟悉API,調(diào)用姿勢不對也是有可能發(fā)生的。

以上就是本文的全部內(nèi)容了。

附:

無限循環(huán)輪播圖示例

本文源碼

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/112873.html

相關(guān)文章

  • 原生js系列無限循環(huán)輪播組件

    摘要:沒有看過上一篇文章的話,可以在這里找到原生系列之工廠模式。那么這篇文章,我們將基于上述的,從頭開始寫一個(gè)無限循環(huán)輪播圖的組件。附無限循環(huán)輪播圖示例本文源碼 前情回顧 在上一篇文章中,我們封裝了一個(gè)DOM庫(qnode),為了讓大家直觀地感受到其方便友好的自定義工廠模式,于是給大家?guī)砹诉@篇文章。 沒有看過上一篇文章的話,可以在這里找到:原生js系列之DOM工廠模式。 那么這篇文章,我們...

    ad6623 評(píng)論0 收藏0
  • 原生js系列無限循環(huán)輪播組件

    摘要:沒有看過上一篇文章的話,可以在這里找到原生系列之工廠模式。那么這篇文章,我們將基于上述的,從頭開始寫一個(gè)無限循環(huán)輪播圖的組件。附無限循環(huán)輪播圖示例本文源碼 前情回顧 在上一篇文章中,我們封裝了一個(gè)DOM庫(qnode),為了讓大家直觀地感受到其方便友好的自定義工廠模式,于是給大家?guī)砹诉@篇文章。 沒有看過上一篇文章的話,可以在這里找到:原生js系列之DOM工廠模式。 那么這篇文章,我們...

    jimhs 評(píng)論0 收藏0
  • JS -- 記一種用原生JS 實(shí)現(xiàn)輪播圖的方法(非無限循環(huán)不自動(dòng)切換)

    摘要:實(shí)現(xiàn)一個(gè)非無限循環(huán)不自動(dòng)切換的輪播圖只需要幾張圖片和兩個(gè)按鈕簡化部分兩個(gè)按鈕,幾張圖片假如有四張圖右側(cè)按鈕左側(cè)按鈕部分動(dòng)態(tài)添加刪除的屬性部分已是最后一張圖這是第一張圖 實(shí)現(xiàn)一個(gè)非無限循環(huán)不自動(dòng)切換的輪播圖只需要幾張圖片和兩個(gè)按鈕(簡化) HTML部分 兩個(gè)按鈕,幾張圖片(假如有四張圖) 右側(cè)按鈕 左側(cè)按鈕 CSS部分 動(dòng)態(tài)...

    hidogs 評(píng)論0 收藏0
  • 50行代碼搞定無限滑動(dòng)幻燈片

    摘要:對,滑動(dòng)式幻燈片的關(guān)鍵就在于隱藏。在條件里我們添加一個(gè)事件相當(dāng)于滑動(dòng)后的回掉,依賴這個(gè)事件在幻燈片滑動(dòng)執(zhí)行完畢后立即執(zhí)行里面的閃回操作。通過添加事件監(jiān)聽滑動(dòng)是否結(jié)束從而迅速閃回,達(dá)到貌似無限滑動(dòng)的效果。 slider輪播組件,在各類網(wǎng)站上出現(xiàn)及其頻繁,有漸隱式的,滑動(dòng)式的等等一系列。栗子在這: 但我當(dāng)初學(xué)習(xí)寫輪播時(shí)卻被各種入門教程搞得焦頭爛額。不是代碼太復(fù)雜,就是封裝太嚴(yán)重,初學(xué)者很難...

    yimo 評(píng)論0 收藏0
  • 原生js系列DOM工廠模式

    摘要:于是在不斷的摸索和思考中,想出了工廠模式這個(gè)概念,咱們可以這么理解工廠就是取快遞的地方,不管是從哪里發(fā)來的貨品,統(tǒng)一送到這里,然后再由特定的人群來取。 寫在前面 如今,在項(xiàng)目中使用React、Vue等框架作為技術(shù)棧已成為一種常態(tài),在享受帶來便利性的同時(shí),也許我們漸漸地遺忘原生js的寫法。 現(xiàn)在,是時(shí)候回歸本源,響應(yīng)原始的召喚了。本文將一步一步帶領(lǐng)大家封裝一套屬于自己的DOM操作庫,我將...

    sunny5541 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<