haizlin / fe-interview

前端面试每日 3+1,以面试题来驱动学习,提倡每日学习与思考,每天进步一点!每天早上5点纯手工发布面试题(死磕自己,愉悦大家),6000+道前端面试题全面覆盖,HTML/CSS/JavaScript/Vue/React/Nodejs/TypeScript/ECMAScritpt/Webpack/Jquery/小程序/软技能……

Home Page:http://www.h-camel.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[js] 第195天 用js实现一个轮播图,并简述有哪些实现的方法

haizhilin2013 opened this issue · comments

第195天 用js实现一个轮播图,并简述有哪些实现的方法

关键方法 setInterval 以及计数器的把控 其次为达成优美的切换效果需要善用css过渡动画

轮播图1.0版

<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<style>
		* {
			margin: 0;
			padding: 0;
		}
		
		li {
			list-style: none;
		}
		.tb {
			width: 520px;
			height: 280px;
			background-color: pink;
			margin:100px auto;
            /*如果是绝对定位,margin:100px auto就不起作用了*/
            /*相对定位只要没有边偏移就没有事情*/
			position: relative;   

		}

		.tb a {
            /*左右箭头是24*36规格的*/
			width: 24px;
			height: 36px;
			/*background-color: pink;*/
            /*把超链接转换成块级元素*/
			display: block;
			position: absolute;
            /*让左右箭头居中显示*/
			top: 50%;
			margin-top:-18px;
		}

		.left {
			left: 0;
			background: url(images/left.png) no-repeat;
		}

		.right {
			right: 0;
			background: url(images/right.png) no-repeat;
		}

		.tb ul {
		width: 70px;
		height: 13px;
		background: rgba(255, 255, 255, 0.3);
		position: absolute; /* 加定位*/
		bottom: 18px;
		left: 50%; /*水平走父容器的一半*/
		margin-left: -35px; /*左走自己的一半*/
		border-radius: 8px;
 	}

 	.tb ul li {
 		width: 8px;
 		height: 8px;
 		background-color: #fff;
 		float: left;
 		/*display: inline-block;*/
 		margin: 3px;
 		border-radius: 50%;
 	}

 	.tb .current {  /*前面要加tb,不然优先级不够*/
 		background-color: #f40;
 	}
	</style>
</head>
<body>
	<div class="tb">
		<img src="images/tb.jpg">
		<a href="#" class="left"></a>
		<a href="#" class="right"></a>
		<ul>
			<li class="current"></li>          
			<!--5个小点是切换效果,没有链接效果 这是第4个兄弟,并列关系 -->
			<li></li>
			<li></li>
			<li></li>
			<li></li>
		</ul>
	</div>
</body>
</html>

轮播图1.0效果图
轮播图2.0版

之前写的轮播图是个静态页面,没有特效,现在来个有特效的。
轮播图也称为焦点图,是网页中比较常见的网页特效。

功能需求:

1.鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮。

2.点击右侧按钮一次,图片往左播放一张,以此类推, 左侧按钮同理。

3.图片播放的同时,下面小圆圈模块跟随一起变化。

4.点击小圆圈,可以播放相应图片。

5.鼠标不经过轮播图, 轮播图也会自动播放图片。

6.鼠标经过,轮播图模块, 自动播放停止。

案例分析:

①因为js较多,我们单独新建js文件夹,再新建js文件, 引入页面中。

②此时需要添加 load 事件。

③鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮。

④显示隐藏 display 按钮。








index.html代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>动态轮播图</title>
	<!-- 引入首页的CSS文件 -->
	<link rel="stylesheet" href="https://github.com/haizlin/fe-interview/issues/css/index.css" target="_blank" rel="nofollow">
	
	<!-- 引入首页的animate.js文件(这个animate.js必须要写到index.js的上面引入)-->
	<script src="js/animate.js"></script>

	<!-- 引入首页的index.js文件 -->
	<script src="js/index.js"></script>
</head>
<body>
	<div class="focus">
		<!-- 左侧按钮 -->
		<!-- javascript:;可以让超链接不要跳转 -->
        <a href="javascript:;" class="arrow-l">  </a>
         
        <!-- 右侧按钮 -->
        <a href="javascript:;" class="arrow-r">  </a>

		<!-- 核心的滚动区域 -->
		<ul>
			<li><a href="#"><img src="images/focus.jpg"></a></li>
			<li><a href="#"><img src="images/focus1.jpg"></a></li>
			<li><a href="#"><img src="images/focus2.jpg"></a></li>
			<li><a href="#"><img src="images/focus3.jpg"></a></li>
			<!-- 要无缝滚动,第一张图片再放一次到最后面 -->
			<!-- <li><a href="#"><img src="images/focus.jpg"></a></li> -->
		</ul>

		<!-- 小圆圈 -->
		<ol class="circle">
		
		</ol>
	</div>
</body>
</html>

index.css代码如下

*{
	margin: 0;
	padding: 0;
}

li{
	list-style: none;
}

a{
	text-decoration: none;
}

@font-face {
    font-family: 'icomoon';
    src:url('../fonts/icomoon.eot?7kkyc2');
    src:url('../fonts/icomoon.eot?7kkyc2#iefix') format('embedded-opentype'),
    url('../fonts/icomoon.ttf?7kkyc2') format('truetype'),
    url('../fonts/icomoon.woff?7kkyc2') format('woff'),
    url('../fonts/icomoon.svg?7kkyc2#icomoon') format('svg');
    font-weight: normal;
    font-style: normal;
}

.focus{
	width: 721px;
	height: 455px;
	margin: 100px auto;
	/*background-color: pink;*/
	position: relative;
	overflow: hidden;
}

.focus ul{
	/*大盒子里面的ul默认和父亲一样宽,即使ul设了浮动,也无法在一行显示。
	所以要把里面的ul宽度弄的更宽一点(比父亲还宽),这样4张图片才能在一行显示
	宽度设置的大一点是没有关系的*/
	width: 600%;

    /*因为animate.js中图片移动(注:是ul移动!!!而不是li移动)必须要定位,
    所以这个ul必须要加个定位*/
	position: absolute;
    top: 0;
    left: 0;
}

 .focus ul li {
 	float: left;
 }

.circle{
    position: absolute;
    bottom: 10px;
    left: 50px;
}
.arrow-l,
.arrow-r {
    display: none;
    position: absolute;
    top: 50%;
    margin-top: -20px;
    width: 24px;
    height: 40px;
    background: rgba(0, 0, 0, .3);
    text-align: center;
    line-height: 40px;
    color: #ccc;
    font-family: 'icomoon';
    font-size: 18px;
    /*ul有定位,arrow_l,r也有定位,要提高层级,否则会被压住*/
    z-index: 2;
}

.arrow-r {
    right: 0;
}

.circle li {
    float: left;
    width: 8px;
    height: 8px;
    /*background-color: #fff;*/
    border: 2px solid rgba(255, 255, 255, 0.5);
    margin: 0 3px;
    border-radius: 50%;
    /*鼠标经过显示小手*/
    cursor: pointer;
}

.current {
    background-color: #fff;
}

animate.js代码如下:

function animate(obj, target, callback) {
    // console.log(callback);  callback = function() {}  调用的时候 callback()

    // 先清除以前的定时器,只保留当前的一个定时器执行
    clearInterval(obj.timer);
    obj.timer = setInterval(function() {
        // 步长值写到定时器的里面
        // 把我们步长值改为整数 不要出现小数的问题
        // var step = Math.ceil((target - obj.offsetLeft) / 10);
        var step = (target - obj.offsetLeft) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        if (obj.offsetLeft == target) {
            // 停止动画 本质是停止定时器
            clearInterval(obj.timer);
            // 回调函数写到定时器结束里面
            // if (callback) {
            //     // 调用函数
            //     callback();
            // }
            //和上面的一样,效果一样
            callback && callback();
        }
        // 把每次加1 这个步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
        obj.style.left = obj.offsetLeft + step + 'px';

    }, 15);
}

index.js代码如下:(重点!!!)

window.addEventListener('load', function() {
    // 1. 获取元素
    var arrow_l = document.querySelector('.arrow-l');
    var arrow_r = document.querySelector('.arrow-r');
    var focus = document.querySelector('.focus');
    //这个要减去1像素,不然图片有点问题
    var focusWidth = focus.offsetWidth - 1;
    // console.log(focusWidth);
    // 2. 鼠标经过focus 就显示隐藏左右按钮
    focus.addEventListener('mouseenter', function() {
        arrow_l.style.display = 'block';
        arrow_r.style.display = 'block';
        clearInterval(timer);
        timer = null;  //清除定时器变量
      
    });

    focus.addEventListener('mouseleave', function() {
        arrow_l.style.display = 'none';
        arrow_r.style.display = 'none';
        timer = setInterval(function(){
        arrow_r.click();
     },2000)
       
    });

    // 3. 动态生成小圆圈  有几张图片,我就生成几个小圆圈
    var ul = focus.querySelector('ul');
    var ol = focus.querySelector('.circle');
    // console.log(ul.children.length);
    for(var i = 0; i < ul.children.length; i++){
        //创建一个小li
        var li = document.createElement('li');
        //记录当前小圆圈的索引号 通过自定义属性来做
        li.setAttribute('index',i);
        //把小li插入到ol里面
        ol.appendChild(li);

        //4.小圆圈的排他** 我们可以直接在生成小圆圈的同时直接绑定点击事件
        li.addEventListener('click',function(){
            //干掉所有人  把所有的小li清除current类名
            for(var i = 0; i < ol.children.length; i++){
                ol.children[i].className = '';
            }
            //留下我自己  当前的小li设置current类名
            this.className = 'current';

            //5.点击小圆圈,移动图片 当然移动的是ul
            //ul的移动距离 小圆圈的索引号 乘以 图片的宽度  注意是负值
            //当我们点击了某个小li 就拿到当前小li的索引号
            var index = this.getAttribute('index');
            //当我们点击了某个小li就要把这个li的索引号给num
            num = index;
            //当我们点击了某个小li就要把这个li的索引号给circle
            circle = index;

            console.log(focusWidth);
            console.log(index);

            animate(ul,-index * focusWidth);
        })
    }

    //把ol里面的第一个小li设置类名为current
    ol.children[0].className = 'current';

    //6.克隆第一章图片li放到ul后面
    var first = ul.children[0].cloneNode(true);
    ul.appendChild(first);
    //7.点击右侧按钮,图片滚动一张
    var num = 0;
    //circle控制小圆圈的播放
    var circle = 0;
    //flag  节流阀
    var flag = true;
    arrow_r.addEventListener('click',function(){
        //如果走到了最后复制的一张图片,此时,我们的ul要快速复原,left改为0
        if(flag){
            flag = false;       //关闭节流阀
            if(num == ul.children.length - 1){
            ul.style.left = 0;
            num = 0;
        }
        
        num++;
        // alert(1);
        animate(ul,-num * focusWidth,function(){
            flag = true;//打开节流阀
        });

        //8.点击右侧按钮,小圆圈跟随一起变化,可以再声明一个变量控制小圆圈的播放
        circle++;

        //如果circle等于4,说明走到最后我们克隆的这张图片了 我们就复原
        // if(circle == 4){
        if(circle == ol.children.length ){
            circle = 0;
        }
        //先清除其余小圆圈的current类名
        for(var i = 0; i < ol.children.length; i++){
            ol.children[i].className = '';
        }
        //留下当前小圆圈的current类名
        ol.children[circle].className = 'current';
        }
    });

    //9.左侧按钮做法
     arrow_l.addEventListener('click',function(){
        //如果走到了最后复制的一张图片,此时,我们的ul要快速复原,left改为0
       if(flag){
        flag = false;
         if(num ==0){
            num = ul.children.length - 1 ;
            ul.style.left = -(ul.children.length - 1) * focusWidth + 'px';
        }
       
        num--;
        // alert(1);
        animate(ul,-num * focusWidth,function(){
            flag = true;
        });

        //8.点击右侧按钮,小圆圈跟随一起变化,可以再声明一个变量控制小圆圈的播放
        circle--;

        //如果circle小于0,则说明第一张图片,则小圆圈要改为第4个小圆圈(3)
        // if(circle == 4){
        if(circle < 0){
            circle = 3;
        }
        //先清除其余小圆圈的current类名
        for(var i = 0; i < ol.children.length; i++){
            ol.children[i].className = '';
        }
        //留下当前小圆圈的current类名
        ol.children[circle].className = 'current';
        }
    });

     //10.自动播放功能
     var timer = setInterval(function(){
        arrow_r.click();
     },2000)
})

来源于我自己的博客-PC端网页特效-轮播图

最简单的轮播图:
function run() { if(step >= len) { step = 0 imgList.forEach((img, index) => { if (index != step) { img.style.transform = translate(0px)} }) } imgList[step].style.transform =translate(-${step * ht}px imgList[step].style.transition = '0.2s' } setInterval(()=>{ step++; run() }, 1500)