20230327----重返学习-轮播图-function的ES6变量提升问题

day-036-thirty-six-20230327-轮播图-function的ES6变量提升问题

轮播图

  1. 设置好布局

    <div class="container" id="bannerBox">
      <div class="wrapper">
        <div class="slide"><img src="./images/banner01.png" alt="" /></div>
        <div class="slide"><img src="./images/banner02.jpg" alt="" /></div>
        <div class="slide"><img src="./images/banner03.png" alt="" /></div>
        <div class="slide"><img src="./images/banner01.png" alt="" /></div>
      </div>
      <div class="pagination">
        <span class="active" index="0"></span>
        <span index="1"></span>
        <span index="2"></span>
      </div>
      <div class="navigation prev"></div>
      <div class="navigation next"></div>
    </div>
    
    • 轮播图所有图片由图片盒子.wrapper包起来,用flex布局让图片.slide排成一行,图片宽高与轮播图盒子大小一致。
    • 图片盒子.wrapper设置绝对定位,通过left属性值的改变前后移动它的位置,同时来让其显示特定一部分在轮播图盒子.container中。
    .container {
      --container-width: 1000px;
      --container-height: 400px;
    
      width: var(--container-width);
      height: var(--container-height);
    
      margin: 50px auto;
      position: relative;
      overflow: hidden;
    
      /* 显示出具体的过程,加边框会造成数据left的计算出错,故而加box-sizing。
      & {
        box-sizing: border-box;
        border: 1px solid red;
      }
      .wrapper,
      .wrapper > .slide {
        box-sizing: border-box;
        width:400%;
      }
      .wrapper {
        border: 1px solid skyblue;
      }
      .wrapper > .slide {
        border: 1px solid yellow;
      } */
    
      .wrapper {
        display: flex;
        height: var(--container-height);
    
        position: absolute;
        left: 0;
        top: 0;
        transition: left 0.3s;
    
        .slide,
        .slide > img {
          width: var(--container-width);
          height: var(--container-height);
          float: left;
        }
      }
    
      .pagination {
        padding: 5px 5px;
        background-color: rgba(255, 255, 255, 0.5);
        border-radius: 15px;
        position: absolute;
        bottom: 30px;
        left: 50%;
        transform: translateX(-50%);
    
        span {
          display: inline-block;
          padding: 5px;
          margin: 0px 8px;
          border-radius: 50%;
          background-color: #000000;
    
          &.active {
            background-color: chocolate;
          }
        }
      }
    
      .navigation {
        height: 79px;
        width: 39px;
        background-color: rgba(245, 245, 245, 0.7);
        background: url(../images/btn.png) no-repeat;
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
    
        &.prev {
          left: 0;
        }
        &.next {
          background-position: -39px 0px;
          right: 0;
        }
      }
    }
    
  2. 设置各个数据

    • 图片宽度width为容器宽度
    • count为轮播单位总数,也顺带等于总图片个数
      • 有重复图片的总数,也是无重复图片的总数加1。因为第一幅与最后一幅是一样的
        • 最后一幅与第一幅一样:是为了当无重复图片到最后一幅,也就是有重复图片的倒数第一幅时,视觉上可以有动画地过渡到第一幅。
          • 也就是说,下一次的动画就是要第一幅到第二幅
    • 设置步长step开始为0
      • step为0到count-1时 0->1->2->count-1(有动画)
      • count-1->0 (无动画count-1-> 0),并且有动画
        • 浏览器如果只是设置属性,会统一执行完设置,统一更新渲染队列。
        • 浏览器如果是获取属性,会立刻更新渲染队列。
  3. 设置各个事件

    1. 定时滚动事件
      • 浏览器如果只是设置属性,会统一执行完设置,统一更新渲染队列。
    2. 容器移入移出事件
      • 移入盒子 停止定时器
      • 移出盒子 重新开启定时器
      • 可用事件
        • onmouseenter(移入) onmouseleave(移出) ---- 没有冒泡
        • onmouseover(移入) onmouseout(移出) — 有冒泡
    3. 点击事件委托到容器上
      1. 点击下一个
      2. 点击上一个
      3. 点击小圆点
    4. 小圆点激活
  4. 渲染页面

const theLun = (function () {
  let container = document.querySelector(".container"); //整个轮播图容器
  let wrapper = container.querySelector(".wrapper"); //要移送的盒子

  let width = container.offsetWidth; //整个容器的宽度,也是图片的宽度
  let step = 0; //步长
  let count = 0; //总图片个数,为ajax获取到的图片总数加1。

  let timer = null; //定时器

  let data = null;

  //获取数据(ajax 4步)+循环渲染
  const render = function render() {
    let xhr = new XMLHttpRequest();
    xhr.open("GET", "data.json", false);
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4 && xhr.status === 200) {
        // console.log(xhr);
        data = JSON.parse(xhr.response);
      }
    };
    xhr.send();

    console.log(data);
    let wrapperString = ``;
    let pagination = container.querySelector(".pagination");
    let paginationString = ``;
    data.forEach((item, index) => {
      wrapperString += `<div class="slide">
        <img src="${item.pic}" alt="" />
      </div>`;

      // paginationString += `<span class="${ index === step ? "active" : "" }" index="${index}"></span>`;
      paginationString += `<span index="${index}"></span>`;
    });
    //除了拼接5张图片外,还要重复第一幅图片
    wrapperString += `<div class="slide">
        <img src="${data[0].pic}" alt="" />
      </div>`;
    wrapper.innerHTML = wrapperString;
    pagination.innerHTML = paginationString; //如果小圆点是作为全局变量的,那么要这里重新查找一下。

    count = data.length + 1; //总图片个数,为ajax获取到的图片总数加1。
  };
  render();

  const autoPlay = function autoPlay() {
    step++;
    if (step > count - 1) {
      //1.如果是最后一幅重复的,无动画,回到第一幅。不回的话,下一个就是空白了。
      step = 0; //将进入第一幅
      wrapper.style.transitionDuration = "0s"; //无动画
      wrapper.style.left = `-${step * width}px`; //回到第一幅

      // 浏览器如果只是设置属性,会统一执行完设置,统一更新渲染队列
      // 浏览器如果是获取属性,会立刻更新渲染队列
      wrapper.offsetWidth;

      // 2. 由第一幅图片,使用动画进入第二幅
      step = 1; //将进入第二幅
      wrapper.style.transitionDuration = "0.3s"; //有动画
    }
    wrapper.style.left = `-${step * width}px`; //-0*1000 0 //-1*1000 -1000

    showCricle();//更新小圆点的各个状态。
  };
  timer = setInterval(autoPlay, 1000);//每隔1000ms要通过图片盒子的left移动盒子

  //onmouseenter(移入) onmouseleave(移出) ---- 没有冒泡
  // onmouseover(移入) onmouseout(移出) --- 有冒泡
  //移入盒子 停止定时器
  container.onmouseenter = function () {
    clearInterval(timer);
    timer = null;
  };
  //移出盒子 重新开启定时器
  container.onmouseleave = function () {
    timer = setInterval(autoPlay, 1000);
  };

  // 修改小圆点的激活状态
  const showCricle = function showCricle() {
    //不能直接修改step,因为全局都在使用step。
    // 声明一个变量temp,复制一版step,改temp
    let temp = step;
    if (temp === count - 1) {
      temp = 0;
    }

    let spanList = Array.from(container.querySelectorAll(".pagination>span"));
    spanList.forEach((item, index) => {
      //如果index等于step, 0--0  1--1  2--2
      //给span添加className="active"
      if (index === temp) {
        item.className = "active";
      } else {
        item.className = "";
      }
    });
  };

  // 绑定事件----事件委托 将事件绑定给祖先级元素,减少事件的绑定,提高性能
  container.onclick = function (e) {
    // console.log(e.target);//目标元素
    // console.log(e.target.tagName);//目标元素的大写标签名
    // console.log(e.target.className);//目标元素的类名字符串

    if (e.target.tagName === "SPAN") {//小圆点
      let index = Number(e.target.getAttribute("index"));//找到小圆点的索引
      // console.log("点击小圆点", index);

      // 如果index与step值一样,就不做操作
      // 如果step为3,index为0,就不做操作
      if (index === step || (step === count - 1 && index === 0)) {
        return;
      }
      step = index;//修改步长 步长等于index
      wrapper.style.left = `-${width * step}px`;//根据步长设置位置
      showCricle();//展示小圆点
    }

    if (e.target.className.includes("prev")) {//上一个
      console.log("点击上一个");
      step--;
      if (step < 0) {
        //1. 由第一幅无动画地立刻回到最后一幅
        step = count - 1;//将要前往最后一幅
        wrapper.style.transitionDuration = `0s`;//无动画
        wrapper.style.left = `-${width * step}px`;//前往最后一幅

        wrapper.offsetWidth;//获取属性,刷新一次队列

        // 2.由倒数第一副-->倒数第二幅(有动画)
        step = count - 2;//将要前往倒数第二幅
        wrapper.style.transitionDuration = `0.3s`;//有动画
      }
      wrapper.style.left = `-${width * step}px`;//-3*1000  //-2*1000
      showCricle();
    }
    if (e.target.className.includes("next")) {//下一个
      console.log("点击下一个");
      autoPlay();
    }
  };
})();

试题回顾

  • function的ES6变量提升问题
  • function声明的函数,声明提升到当前私有作用域,赋值提到了当前块作用域;以这行代码为分界线,上面为全局作用域,下方为块级作用域
console.log(foo)//undefined
{
    console.log(foo)//ƒ foo() {return 2}
    function foo() {return 1}
    console.log(foo)//ƒ foo() {return 2}
    foo = 1;
    function foo() {return 2}
    console.log(foo)//1
    foo = 3;
    console.log(foo)//3
}
console.log(foo);//1
//EC(G) foo--undefined--0x001--0x002---1
console.log(foo)
{
    //EC(G) foo--0x001--0x002---1
    console.log(foo)//ƒ foo() {return 2}
    function foo() {return 1}//0x001
    console.log(foo)//ƒ foo() {return 2}
    foo = 1;//这里依旧是全局作用域
    function foo() {return 2}//0x002 //这行代码的声明提升到当前私有作用域,赋值提到了当前块作用域;以这行代码为分界线,上面为全局作用域,下方为块级作用域

    //EC(anonymous) foo--0x002---1---3
    console.log(foo)//1
    foo = 3;
    console.log(foo)//3
}
console.log(foo);//1

相当于

//EC(G) foo--undefined--0x001--0x002---1
var foo
console.log(foo)
{
  //EC(G) foo--0x001--0x002---1
  foo=function foo() {return 1}//0x001
  foo=function foo() {return 2}//0x002 
  console.log(foo)//ƒ foo() {return 2}
  
  console.log(foo)//ƒ foo() {return 2}
  foo = 1;//这里依旧是全局作用域
  //-----------------
  //EC(anonymous) foo--0x002---1---3
  {
    let foo=1//赋值为上次的值

    console.log(foo)//1
    foo = 3;
    console.log(foo)//3
  }
}
console.log(foo);//1

进阶参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值