知识储备

  • jQuery 方法
    1. 根据选择器获取元素:$(selector)
    2. ajax 方法及配置参数 $.ajax({})
    3. 绑定事件 on方法
    4. html() 方法
    5. css() 方法
    6. addClass()、removeClass() 方法
    7. animate() 动画方法 finish() 结束动画方法
  • 定时器基础:
    1. js 动画的原理就是利用定时器操作某个样式值缓慢增加,轮播图操作的是 wrapper 元素的 left 属性。
    2. 停止动画就是清除定时器,再次开启动画即重新开启定时器;
    3. 定时器 setTimeout 和 setInterval 的返回值是一个定时器 id;
    4. clearTimeout(定时器 id)和 clearInterval(定时器 id);

轮播图需求:

  1. 制作一个轮播图(又称焦点图),自动轮播,并且轮播图下焦点跟随切换;
  2. 当鼠标移入轮播图时,轮播图停止自动轮播,并且展示手动切换的箭头图标,当鼠标移出轮播图时,继续自动轮播,隐藏手动切换箭头图标;
  3. 当点击手动控制轮播箭头图标的时候,可以手动切换图片。如果点击左侧箭头,则切换至上一张,如果切换到第一张,再次点击则切换至最后一张,依此类推;如果点击右侧箭头,则切换至下一张,如果已经是最后一张,再次点击则切换至第一张,实现循环播放;
  4. 点击焦点时,对应索引的图片也要跟随切换;

轮播图原理:

  1. 有一个外面的盒子 container,container 的宽度只能展示一张图片,其余的溢出隐藏。
  2. 在 container 里面有一个 wrapper 的盒子,里面装着很多张图片,这些图片在一行排列,而 wrapper 相对于 container 绝对定位,初始值 left = 0,所以默认展示第一张图片(第一张图片索引为 0)。
  3. 轮播一次其实就是让 wrapper 盒子向左移动一张图片的距离(操作 wrapper 的 left 值),这样第二张(索引为 1)图片就露出来了(此时 wrapper 的 left 的值是 负的 1 张图片的宽度),里面的盒子再向左移动一张,第三张(索引为 2)就露出来了(此时 wrapper 的 left 值是 负的 2 张图片的宽度),依次类推,我们发现如果想让轮播图轮到第几张,就将 wrapper 的 left 值设置成:负的图片宽度 * 索引。
展示图片 图片索引 wrapper 的 left 值
1 0 0 * 图片宽度
2 1 -1 * 图片宽度
3 2 -2 * 图片宽度
4 3 -3 * 图片宽度
  1. 综上,我们需要有个变量 stepIndex,记录着要轮播的是第几张,然后轮播时就是让 wrapper 的 left 值就是负的 stepIndex 乘以图片宽度,即 left = -1 stepIndex 1000 (1000 是图片宽度)

无缝轮播实现

  1. 我们首先复制一份第一张图片并且把复制的这个放到末尾,相当于真实图片的最后一张后面还有一个第一张的副本。
  2. 然后当轮播到最后一张的时候,这个时候展示的是我们第一步复制过来的第一张图片,下一次再轮播应该展示第二张图了。
  3. 这个时候我们将 wrapper 的 left 设置成 0,然后 left 为 0 时展示的是真正的第一张,下一张要轮播的是第二张所以我们将 stepIndex 的值置为 1(1 就是第二张图的索引),然后再轮播就是第二张图了。
  4. 又因为浏览器设置 wrapper 的值基本不需要时间,所以给人的感觉就是无缝轮播的。

html代码

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>珠峰培训-轮播图</title>
  6. <link rel="stylesheet" href="css/reset.min.css">
  7. <link rel="stylesheet" href="css/banner.css">
  8. </head>
  9. <body>
  10. <section class="container" id="container">
  11. <div class="wrapper">
  12. <!--<div class="slide"><img src="img/banner1.jpg" alt=""></div>
  13. <div class="slide">
  14. <img src="img/banner2.jpg" alt="">
  15. </div>
  16. <div class="slide"><img src="img/banner3.jpg" alt=""></div>
  17. <div class="slide"><img src="img/banner4.jpg" alt=""></div>-->
  18. </div>
  19. <ul class="focus">
  20. <!-- <li class="active"></li>
  21. <li></li>
  22. <li></li>
  23. <li></li>-->
  24. </ul>
  25. <a href="javascript: void 0;" class="arrow arrowLeft"></a>
  26. <a href="javascript: void 0;" class="arrow arrowRight"></a>
  27. </section>
  28. <!--<section class="container" id="container2">
  29. <div class="wrapper">
  30. &lt;!&ndash;<div class="slide"><img src="img/banner1.jpg" alt=""></div>
  31. <div class="slide">
  32. <img src="img/banner2.jpg" alt="">
  33. </div>
  34. <div class="slide"><img src="img/banner3.jpg" alt=""></div>
  35. <div class="slide"><img src="img/banner4.jpg" alt=""></div>&ndash;&gt;
  36. </div>
  37. <ul class="focus">
  38. &lt;!&ndash; <li class="active"></li>
  39. <li></li>
  40. <li></li>
  41. <li></li>&ndash;&gt;
  42. </ul>
  43. <a href="javascript:;" class="arrow arrowLeft"></a>
  44. <a href="javascript:;" class="arrow arrowRight"></a>
  45. </section>-->
  46. <script src="js/jquery.js"></script>
  47. <script src="js/banner.js"></script>
  48. <!--<script>-->
  49. <!--let b1 = new Banner({-->
  50. <!--ele: '#container',-->
  51. <!--url: 'json/banner.json',-->
  52. <!--speed: 100-->
  53. <!--});-->
  54. <!--let b2 = new Banner({-->
  55. <!--ele: '#container2',-->
  56. <!--url: 'json/banner2.json',-->
  57. <!--interval: 1000,-->
  58. <!--speed: 500-->
  59. <!--});-->
  60. <!--</script>-->
  61. </body>
  62. </html>

css 代码

  1. .container {
  2. position: relative;
  3. margin: 20px auto;
  4. width: 1000px;
  5. height: 300px;
  6. overflow: hidden;
  7. }
  8. .container .wrapper {
  9. position: absolute;
  10. top: 0;
  11. left: 0;
  12. width: 4000px; /*根据 JS 中获取的数据动态控制宽度*/
  13. height: 100%;
  14. }
  15. .container .wrapper .slide {
  16. float: left;
  17. width: 1000px;
  18. height: 100%;
  19. overflow: hidden;
  20. }
  21. .container .wrapper .slide img {
  22. display: block;
  23. width: 100%;
  24. height: 100%;
  25. }
  26. .container .focus {
  27. position: absolute;
  28. z-index: 999;
  29. bottom: 10px;
  30. left: 50%;
  31. transform: translateX(-50%); /*基于CSS3中的 transform 变形属性,在不固定的宽度的情况下实现水品居中 translateX:让当前元素在水平方向发生位移(此处-50%向左移动当前盒子的一半)*/
  32. padding: 4px;
  33. height: 12px;
  34. background: rgba(0, 0, 0, .5);
  35. border-radius: 10px; /*让其为盒子高度的一半,这样把长方型的盒子修改为椭圆状*/
  36. font-size: 0;
  37. }
  38. .container .focus li {
  39. display: inline-block;
  40. margin: 0 4px;
  41. width: 12px;
  42. height: 12px;
  43. border-radius: 50%;
  44. background: #FFF;
  45. cursor: pointer;
  46. }
  47. .container .focus li.active {
  48. background: #DB192A;
  49. }
  50. .container .arrow {
  51. display: none;
  52. position: absolute;
  53. top: 50%;
  54. margin-top: -22.5px;
  55. width: 28px;
  56. height: 45px;
  57. background: url("../img/pre.png") no-repeat;
  58. opacity: 0.3;
  59. }
  60. .container .arrow:hover {
  61. opacity: 1;
  62. }
  63. .container .arrow.arrowLeft {
  64. left: 0;
  65. background-position: 0 0;
  66. }
  67. .container .arrow.arrowRight {
  68. right: 0;
  69. background-position: -50px 0;
  70. }

js 代码

  1. let bannerRender = (function () {
  2. // -> 获取后续需要操作的对象或者元素
  3. let container = $('#container'),
  4. wrapper = $('.wrapper'),
  5. focus = $('.focus'),
  6. arrowLeft = $('.arrowLeft'),
  7. arrowRight = $('.arrowRight'),
  8. slideList = null,
  9. focusList = null;
  10. // => 轮播图运动的基础参数
  11. let stepIndex = 0; // stepIndex 记录当前展示块的索引
  12. let autoTimer = null; // autoTimer 自动轮播的定时器
  13. let interval = 3000; // interval 间隔多长时间自动切换
  14. // autoMove:控制轮播图的运动和切换
  15. /*
  16. * 索引为1,展示第二张,warpper 的 left - 1000;
  17. * 索引为2,展示第三章,wrapper 的 left - 2000;
  18. * ....
  19. * wrapper 的 left 值其实就是当前要展示的图片索引对应的结果 :-索引 * 1000
  20. * */
  21. function autoMove() {
  22. stepIndex++;
  23. if (stepIndex >= slideList.length) {
  24. // 说明再往后切换没有了,现在展示的是克隆的第一张,此时我们将 wrapper 设置到回到真实的第一张的位置,即 left = 0,然后 stepIndex = 1,这样就可以切换到第二张了
  25. wrapper.css('left', 0);
  26. stepIndex = 1;
  27. }
  28. wrapper.animate({
  29. left: -stepIndex * 1000
  30. }, 200); // 200 是从当前切换到下一张的动画时间 interval 是间隔多久切换一次
  31. // -> 每一次运动完成需要让焦点跟着切换
  32. changeFocus();
  33. }
  34. // changeFocus: 让焦点跟着轮播图切换而切换(运动到克隆的这一张的时候,也需要让第一个 li 有选中的样式
  35. function changeFocus() {
  36. // 当轮播图运动到最后一张(克隆的第一张,我们需要让第一个 li 有选中样式,之所以使用 tempIndex 是因为 stepIndex 对轮播图切换起着很大作用,不能轻易修改)
  37. let tempIndex = stepIndex;
  38. tempIndex === slideList.length - 1
  39. ? tempIndex = 0
  40. : null;
  41. focusList.each((index, item) => {
  42. if (index === tempIndex) {
  43. $(item).addClass('active')
  44. } else {
  45. $(item).removeClass('active')
  46. }
  47. })
  48. }
  49. // queryDate:获取数据
  50. function queryData(onSuccess, onError) {
  51. $.ajax({
  52. url: 'json/banner.json',
  53. method: 'GET',
  54. dataType: 'json',
  55. async: false,
  56. success: onSuccess,
  57. error: onError
  58. })
  59. }
  60. // bindHTML:数据绑定
  61. function bindHTML(data) {
  62. let strSlide = ``;
  63. let strFocus = ``;
  64. data.forEach((item, index) => {
  65. let {
  66. img = 'img/banner1.jpg',
  67. desc = '珠峰培训'
  68. } = item;
  69. strSlide += `<div class="slide">
  70. <img src="${img}" alt="${desc}">
  71. </div>`;
  72. strFocus += `<li class="${index === 0 ? 'active' : '' }"></li>`
  73. });
  74. // 将第一张克隆一份放到最后
  75. strSlide += `<div class="slide">
  76. <img src="${data[0].img}" alt="${data[0].desc}">
  77. </div>`
  78. // 插入到容器中
  79. wrapper.html(strSlide);
  80. focus.html(strFocus);
  81. // -> 获取所有的 slide 和 li
  82. slideList = $('.slide');
  83. focusList = $('.focus > li');
  84. // -> 根据 slide 的个数动态计算 wrapper 的宽度
  85. wrapper.css({
  86. width: slideList.length * 1000
  87. })
  88. }
  89. // handleContainer 鼠标进入后停止自动轮播,离开后开始自动轮播;
  90. function handleContainer() {
  91. container.on('mouseenter', function () {
  92. clearInterval(autoTimer);
  93. arrowLeft.css({
  94. display: 'block'
  95. });
  96. arrowRight.css({
  97. display: 'block'
  98. })
  99. }).on('mouseleave', function () {
  100. clearInterval(autoTimer);
  101. autoTimer = setInterval(autoMove, interval);
  102. arrowLeft.css({
  103. display: 'none'
  104. });
  105. arrowRight.css({
  106. display: 'none'
  107. })
  108. });
  109. }
  110. // 点击箭头切换轮播
  111. function handleArrow() {
  112. arrowRight.click(autoMove);
  113. arrowLeft.click(function () {
  114. stepIndex--;
  115. if (stepIndex < 0) {
  116. // 如果索引减减后小于 0,说明当前已经是第一张,不能向右运动了,如果再减就该展示最后一张图片了(这里的最后一张应该是真实的最后一张图,而不是我们复制出来的那个),最后一张在 slide 中的位置是倒数第二,即索引位置是 length - 2
  117. wrapper.css({
  118. left: -(slideList.length - 1) * 1000
  119. });
  120. stepIndex = slideList.length - 2;
  121. }
  122. wrapper.finish().animate({
  123. left: -stepIndex * 1000
  124. }, 200);
  125. changeFocus();
  126. })
  127. }
  128. // 点击焦点切换
  129. function handleFocus() {
  130. focusList.on('click', function () {
  131. // 给每个焦点绑定点击事件,点击的是谁,就让 stepIndex 的值等于谁的索引,然后运动到这里
  132. let focusIndex = $(this).index();
  133. stepIndex = index;
  134. wrapper.finish().animate({
  135. left: -focusIndex * 1000
  136. }, 200);
  137. changeFocus();
  138. })
  139. }
  140. // queryData(bindHTML);
  141. // handleContainer();
  142. // handleArrow();
  143. // handleFocus();
  144. // autoTimer = setInterval(autoMove, interval)
  145. return {
  146. init() {
  147. queryData(bindHTML);
  148. handleContainer();
  149. handleArrow();
  150. handleFocus();
  151. autoTimer = setInterval(autoMove, interval)
  152. }
  153. }
  154. })();
  155. bannerRender.init();