轮播图的逻辑代码

思路:

0. 获取元素

  => imgBox: 承载图片的盒子

  => pointBox: 承载焦点的盒子

1. 根据图片创建焦点(选做)

  => 你如果自己调整好的样式, 可以不做这一步

  => 如果你希望将来简单一些, 那么现在可以做这一步

  1-1. 确定我需要生成多少个焦点

    -> imgBox 内有多少个 li, 就是有多少个图片

    -> 就应该生成多少个焦点

    -> 获取到 imgBox 内有多少个 li

  1-2. 如何生成焦点

    -> 根据 pointNum 循环

    -> 每循环一次, 创建一个 li 标签

    -> 插入到 pointBox 内部

  1-3. 重新调整一下 pointBox 的宽度(选做)

2. 复制一份元素(选做)

  => 你可以手动复制, 然后通过调整 css 的方式来实现

  2-1. 拿到要复制的元素去克隆一份

    -> imgBox 内的第一个子元素

    -> imgBox 内的最后一个子元素

  2-2. 插入指定的父元素内

    -> first 插入到 imgBox 内的最后一个, appendChild()

    -> last 插入到 imgBox 内的第一个, insertBefore()

  2-3. 调整一下 imgBox 的宽度

    -> 本身设置的时候, 是设置的 五个图片的宽度

    -> 为了循环播放, 我们复制了两个元素放进去

    -> 导致子元素变成 7 个了, 宽度不够了

    -> 根据最新的子元素的数量来设置宽度

  2-4. 调整一下 imgBox 的 left 值

    -> 让真实的第一张显示在可视窗口内

    -> 将 imgBox 向 负方向 移动一段距离

    -> 走多少, 一个 li 的宽度

    -> 一个 li 就是一个 可视窗口的 宽度

    -> 只要拿到可视窗口的宽度

3. 开始自动轮播

  => 每间隔一段时间, 让 imgBox 移动一段距离

  => 达到切换下一张的效果

  => 问题1: 如何让 imgBox 移动一段距离 ?

    -> 运动函数

  => 问题2: 什么时候, 让 imgBox 移动一段距离 ?

    -> 每间隔一段固定的时间移动一次

    -> setInterval()

  => 问题3: 运动的目标位置是多少 ?

    -> 当前显示的是 [1] 的那一张, imgBox 的 left 是 -600px

    -> 当你显示的是 [2] 的那一张, imgBox 的 left 是 -1200px

    -> 当你显示的是 [3] 的那一张, imgBox 的 left 是 -1800px

4. 运动结束

  => 需要在运动结束以后, 让 imgBox 瞬间定位到真实第一张的位置

  => 每一次运动完都需要定位吗

  => 问题: 什么时候需要瞬间定位 ?

    -> 假的第一张运动完毕以后

    -> 假的自一张索引是多少, imgBox.children.length - 1

  => 需要让焦点跟随一起切换

    -> 让 pointBox 内的所有子元素都没有 active 类名

    -> 只有索引和 图片 配套的这一张有 active 类名

    -> 如何配套

      图片: 0 1 2 3 4 5 6

      焦点:   0 1 2 3 4

5. 移入移出

  => 需要绑定一个移入移出事件

  => 移入的时候, 停止自动轮播

  => 移出的时候, 再次开启自动轮播

  => 问题: 移入移出谁的时候, 执行这个操作 ?

    -> 整个可视区域, banner

6. 点击事件

  => 左按钮: 点击事件

  => 右按钮: 点击事件

  => 焦点按钮: 点击事件

  => 事件委托: 委托给 banner

7. 解决切换标签页时的问题

  => 问题: 当你切换标签页, 会导致页面混乱 ?

    -> 当你离开当前页面的时候, 页面上的任何 DOM 是绝对不允许移动的

    -> 你的所有 DOM 操作是无效的, 因为这个原因, 浏览器会把你的 DOM 操作 保留

    -> 当时当你离开当前页面的时候, 定时器不会听

  => 解决:

    -> 只要离开页面的时候, 关闭定时器

    -> 回到页面的时候再次开启自动轮播

  => 问题2: 如何判断离开页面了 ?

    -> 有一个事件, 叫做 visibilitychange 事件

    -> 只能绑定给 document

    -> 当文档流的可视程度改变的时候会触发

  => 问题3: 你怎么知道是离开还是回来 ?

    -> document 身上有一个属性, 叫做 visibilityState

    -> 当你离开的时候, 值是 hidden

    -> 当你回来的时候, 值是 visible

8. 点击过快抖动问题

  => 问题: 当你点击过快的时候, 会出现抖动问题 ?

    -> 原因见本文件最后

  => 解决:

    ->


轮播图的过程

前提:

  + 点击一次会切换下一张

  + 切换一张所用的时间是 1s

分析:

  + 考虑切换一张你做的事情都有什么

  + 把 imgBox 的 left 从 -1200 移动到 -1800, 这个移动的过程用时 1s

    => 当运动结束(从你点击到 1s 以后)

    => 判断是否是最后一张, 如果是, 拉回来

    => 判断是否是第一张, 如果是, 拉回去

    => 操作所有焦点没有类名

    => 给图片配套的焦点设置类名

  + 根据你点击一次切换一张的操作

    => 当你点击了第一下以后, 会需要消耗 1s 的时候, 切换完毕

    => 如果你在 1s 内继续点击第二次

    => 上一次的运动没有结束, 下一次的运动开始了


JS

// 0. 获取元素
const banner = document.querySelector('.banner')
const imgBox = document.querySelector('.imgBox')
const pointBox = document.querySelector('.pointBox')

// 拿到 可视区的 宽度
// 元素.clientWidth, 获取到的是元素 内容 + padding 区域的尺寸
const banner_width = banner.clientWidth
// 接受定时器返回值
let timer = 0
// 准备一个变量, 表示当前第几张, 默认是 1
let index = 1
// 准备一个开关, 默认是开启的
let flag = true

// 1. 创建焦点
setPoint()
let setPoint = () => {
  // 1-1. 获取到 imgBox 内有多少 li
  // 元素.children, 获取到的是一个伪数组, 里面是该元素下的所有子元素
  const pointNum = imgBox.children.length

  // 1-2. 根据 pointNum 循环
  for (let i = 0; i < pointNum; i++) {
    // 创建一个 li 标签
    // 创建节点: document.createElement('标签名')
    const li = document.createElement('li')

    // 给每一个 li 添加一个类名
    li.classList.add('point_item')

    // 给每一个 li 添加一个自定义属性, 就是当前这个 li 的索引
    li.dataset.point = i

    // 默认第一个有一个 active 类名
    if (i === 0) li.classList.add('active')

    // 插入到 pointBox 内部
    // 父元素.appendChild(子元素)
    pointBox.appendChild(li)
  }

  // 1-3. 重新调整 pointBox 的宽度
  pointBox.style.width = (20 + 10) * pointNum + 'px'
}

// 2. 复制元素
copyEle()
let copyEle = () => {
  // 2-1. 拿到要复制的子元素去克隆一份
  // 元素.cloneNode(true) 连带后代元素一起克隆
  const first = imgBox.firstElementChild.cloneNode(true)
  const last = imgBox.lastElementChild.cloneNode(true)

  // 2-2. 插入指定位置
  imgBox.appendChild(first)
  imgBox.insertBefore(last, imgBox.firstElementChild)

  // 2-3. 调整 imgBox 的宽度
  imgBox.style.width = imgBox.children.length * 100 + '%'

  // 2-4. 调整 imgBox 的 left
  imgBox.style.left = -banner_width + 'px'
}

// 3. 自动轮播
autoPlay()
let autoPlay = () => {
  // 每间隔一段时间切换一张
  timer = setInterval(() => {
    // 改变当前是第几张
    index++

    // 切换一张
    move(imgBox, { left: -banner_width * index }, moveEnd)
  }, 2000)
}

// 4. 运动结束
let moveEnd = () => {
  // 4-1. 判断来到了假的第一张, 真实的最后一张
  if (index === imgBox.children.length - 1) {
    // 瞬间定位, 定位回到索引第 [1] 张
    index = 1
    imgBox.style.left = -banner_width * index + 'px'
  }

  // 4-2. 判断来到了假的最后一张, 真实的 [0] 那一张
  // 瞬间定位到真实的最后一张, 也就是索引倒数第二个
  if (index === 0) {
    index = imgBox.children.length - 2
    imgBox.style.left = -banner_width * index + 'px'
  }


  // 4-3. 焦点配套一起走
  for (let i = 0; i < pointBox.children.length; i++) {
    pointBox.children[i].classList.remove('active')
  }
  pointBox.children[index - 1].classList.add('active')

  // 当代码来到这里, 说明一张的切换已经完全结束了
  // 可以准备好去切换下一张了
  flag = true
}

// 5. 移入移出
overOut()
let overOut = () => {
  // 5-1. 移入事件
  // 通过关闭定时器的方式来实现停止自动轮播
  banner.addEventListener('mouseover', () => clearInterval(timer))

  // 5-2. 移出事件
  // 通过调用 autoPlay 方法实现再次开启自动轮播
  banner.addEventListener('mouseout', () => autoPlay())
}

// 6. 点击切换
change()
let change = () => {
  // 6-1. 给 banner 绑定点击事件, 进行事件委托
  banner.addEventListener('click', e => {
    // 处理事件对象兼容
    e = e || window.event
    // 处理事件目标兼容
    const target = e.target || e.srcElement

    // 判断点击的是哪一个 按钮 了
    if (target.className === 'left') {
      if (!flag) return

      flag = false
      index--
      move(imgBox, { left: -banner_width * index }, moveEnd)
    }

    if (target.className === 'right') {
      // 判断开关是否是开启的
      if (flag === false) return

      // 代码能执行到这里, 说明开关是开着的
      index++
      move(imgBox, { left: -banner_width * index }, moveEnd)
      flag = false
    }

    if (target.className === 'point_item') {
      if (!flag) return

      flag = false
      // 你怎么直到你点击的 li 的索引是 多少
      // 可以在渲染页面的时候, 把 li 的索引以自定义属性的形式记录在 li 身上
      index = target.dataset.point - 0 + 1
      move(imgBox, { left: -banner_width * index }, moveEnd)
    }
  })
}

// 7. 切换标签页
changeTab()
let changeTab = () => {
  // 7-1. 给 document 绑定一个事件
  document.addEventListener('visibilitychange', () => {
    // 7-2. 判断离开还是回来
    if (document.visibilityState === 'hidden') {
      // 关闭定时器
      clearInterval(timer)
    } else if (document.visibilityState === 'visible') {
      // 开启自动轮播
      autoPlay()
    }
  })
}

HTML

<!-- 布局结构 -->
<!-- banner: 轮播图可视区域 -->
<div class="banner">
  <!-- imgBox: 承载所有图片的大盒子 -->
  <ul class="imgBox">
    <!-- li: 一个图片的小盒子 -->
    <li style="background-color: pink;">1</li>
    <li style="background-color: skyblue;">2</li>
    <li style="background-color: orange;">3</li>
    <li style="background-color: purple;">4</li>
    <li style="background-color: cyan;">5</li>
  </ul>

  <!-- pointBox: 承载所有焦点的盒子 -->
  <ol class="pointBox">
    <!-- li: 一个焦点 -->
    <!-- <li data-point="0" class="active"></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li> -->
  </ol>

  <!-- left: 左按钮, 切换上一张 -->
  <div class="left">&lt;</div>
  <!-- right: 右按钮, 切换下一张 -->
  <div class="right">&gt;</div>
</div>

CSS

* {
  margin: 0;
  padding: 0;
}

ol, ul, li {
  list-style: none;
}

.banner {
  width: 600px;
  height: 400px;
  border: 1px solid #333;
  border-radius: 10px;
  overflow: hidden;
  margin: 30px auto;

  position: relative;
}

.banner > .imgBox {
  width: 500%;
  height: 100%;
  display: flex;

  /* 将来是需要移动的, 就得有定位 */
  position: absolute;
  left: 0;
  top: 0;
}

.banner > .imgBox > li {
  flex: 1;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 100px;
  color: #fff;
}


.banner > .pointBox {
  width: 200px;
  height: 30px;
  background-color: rgba(0, 0, 0, .5);
  border-radius: 15px;

  position: absolute;
  left: 50%;
  bottom: 30px;
  transform: translateX(-50%);

  display: flex;
  justify-content: space-evenly;
  align-items: center;
}

.banner > .pointBox > li {
  width: 20px;
  height: 20px;
  background-color: #fff;
  border-radius: 50%;
  cursor: pointer;
}

.banner > .pointBox > li.active {
  background-color: red;
}

.banner:hover > div {
  display: flex;
}

.banner > div {
  width: 40px;
  height: 40px;
  background-color: rgba(0, 0, 0, .2);
  display: flex;
  justify-content: center;
  align-items: center;
  color: #fff;
  cursor: pointer;
  font-size: 24px;

  display: none;

  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}

.banner > div:hover {
  background-color: rgba(0, 0, 0, .5);
}

.banner > div.left {
  left: 0;
  border-radius: 0 20px 20px 0;
}

.banner > div.right {
  right: 0;
  border-radius: 20px 0 0 20px;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值