zoukankan      html  css  js  c++  java
  • 原生JS:响应式轮播图

    要求

    1. 图片自行滚动(规定自左向右滚动)
    2. 点击左右箭头,实现图片翻页;
    3. 点击提示圆点,显示不同图片;
    4. 滚动、翻页和显示都需要过渡效果;
    5. 响应式:轮播图随着浏览器窗口大小变化而变化;
    6. 功能整合。

    最终效果

    点击预览:全屏响应式轮播图


    整体思路

    之前也写过轮播图,那时候是WEB里的一个js作业,懵了半天,最后靠自己独立写出来了,不过思路不是很清晰,有点浆糊的感觉。而这一次是组队PROJECT,要我写一个页面的全屏轮播并响应浏览器大小

    之前也搜索了其他人写的轮播图,参考了一些网站的轮播是怎么根据浏览器窗口大小响应的,发现可以从最基本的点击事件出发:

    左右点击翻页是最基本的操作封装到一个next_pic()函数里,定时器可以周期执行这个函数来实现自动轮播,而显示提示点以及提示点和当前图片的联系就小菜一碟了。


    具体实现

    HTML文档结构

    <!-- 轮播图 -->
    <div id="content">
        <div id="carousel_wrap">
            <div id="carousel_images">
                <!-- 前后分别加上一张图片,方便无缝过渡显示。可以使用JS DOM增加节点操作省去该步骤 -->
                <img src="https://s2.ax1x.com/2019/06/05/VNGTdP.jpg">
                <img src="https://s2.ax1x.com/2019/06/05/VNG5qI.jpg">
                <img src="https://s2.ax1x.com/2019/06/05/VNGoZt.jpg">
                <img src="https://s2.ax1x.com/2019/06/05/VNGTdP.jpg">
                <img src="https://s2.ax1x.com/2019/06/05/VNG5qI.jpg">
            </div>
            <span class="arrow left-arrow">&lt;</span>
            <span class="arrow right-arrow">&gt;</span>
            <div id="dots">
                <!-- 使用小点标记实际多少张图片,要添加图片时需要修改carousel_images和此处 -->
                <span class="dot active"></span>
                <span class="dot"></span>
                <span class="dot"></span>
            </div>
        </div>
        <div id="test">
            <h1>blog</h1>
            <h1>blogs</h1>
            <h1>blog</h1>
            <h1>blog</h1>
        </div>
    </div>
    <!-- END 轮播图 -->
    

    CSS样式

    将“装着”轮播图片的carousel_images中的white-space属性设置为:nowrap,意为当到达容器边界时不换行,配合overflow实现carousel_wrap之外的图片都隐藏掉。

    这里提到white-space属性:用来设置element元素对内容中的空格的处理方式,有着几个可选值:normal,nowrap,pre,pre-wrap,pre-line。没有设置white-space属性,则默认为white-space:normal。

    区别这几个属性值关键词:源码空格源码换行容器大小换行。源码换行分为(1)br标签换行和(2)源码换行符换行,而(1)是全都符合的,所以区别就在于源码中的换行是否有效,容器大小换行就是根据容器自适应换行。因此在关键一点就变成->合并/保留空格、换行/不换行

    1. normal表示合并空格、换行,多个相邻空格合并成一个空格,在源码中的换行作为空格处理,只会根据容器的大小进行自动换行

    2. nowrap合并空格、不换行,在源码中的换行作为空格处理,但是不会根据容器大小换行,表示不换行

    常与overflow配合使用,如:

    overflow: hidden;
    text-overflow: ellipsis; 表示文本省略号
    
    1. pre表示保留空格、不换行,保持源码中的空格,有几个空格算几个空格显示,同时换行只认源码中的换行和br标签,不会去自适应容器换行。

    也就是说pre中的文本在源码中是什么样子,到网页中就是什么样子。

    1. pre-wrap表示保留空格、换行,保留空格,并且除了碰到源码中的换行和
      会换行外,还会自适应容器的边界进行换行。

    2. pre-line比较特殊,表示合并空格、换行,合并空格,换行和white-space:pre-wrap一样,遇到源码中的换行和br会换行,碰到容器的边界也会换行。

    white-space:pre-line会保留源码中的换行,但是不会保留源码中的空格


    回到轮播当中,发现使用了nowrap之后,图片之间有间隙,可以使用font-size: 0;清除缝隙。

    body {
    	overflow: scroll;
    }
    
    /* 轮播图样式表 */
    
    #content #carousel_wrap {
    	position: relative;
    	margin: 0 auto;
    	width: 100%; /* 轮播图宽度 */
    	overflow: hidden;
    }
    
    #content #carousel_wrap #carousel_images {
    	position: absolute;
    	border: 0;
    	outline: none;
    	white-space: nowrap; /* 将图片一行排列 */
    	width: 100%;
    	font-size: 0; /* 清除white-space间隙 */
    	margin: 0px; 
    }
    
    #content #carousel_wrap #carousel_images img {
    	width: 100%;
    }
    
    #content #carousel_wrap .arrow {
    	position: absolute;
    	font-weight: bold;
    	font-size: 50px;
    	color: lightgray;
    	top: 50%;
    	transform: translateY(-50%);
    	cursor: pointer;
    	transition-property: opacity;
    	transition-duration: 0.5s;
    }
    
    #content #carousel_wrap .arrow:hover {
    	opacity: 0.5;
    }
    
    #content #carousel_wrap .left-arrow {
    	left: 20px;
    }
    
    #content #carousel_wrap .right-arrow {
    	right: 20px;
    }
    
    #content #carousel_wrap #dots {
    	position: absolute;
    	bottom: 20px;
    	left: 50%;
    	transform: translateX(-50%)
    }
    
    #content #carousel_wrap .dot {
    	background-color: white;
    	display: inline-block;
    	width: 10px;
    	height: 10px;
    	border-radius: 50%;
    	margin: 4px;
    	opacity: 0.2;
    	cursor: pointer;
    }
    
    #content #carousel_wrap .active {
    	opacity: 1;
    }
    
    .transition {
    	transition-property: left;
    	transition-duration: 1s;
    }
    /* END 轮播图样式表 */
    

    JS

    有了好的设计思路,轮播图的JS就不难写了。
    DOM操作定位到具体元素:

    var carouImg = document.getElementById("carousel_images");
    var carouWrap = document.getElementById("carousel_wrap");
    var img = carouImg.getElementsByTagName("img")[0];
    var leftArrow = document.getElementsByClassName("left-arrow")[0];
    var rightArrow = document.getElementsByClassName("right-arrow")[0];
    var oBtn = document.getElementsByClassName("dot");
    var index = 0; //标记当前图片
    var index_length = oBtn.length; //图片数量
    

    响应浏览器

    为了能响应浏览器的大小来全屏显示轮播图片,让图片的宽占据浏览器宽的百分比值,图片的大小不定,那么轮播判断位置的时候就要使用clientWidth获取宽度。

    关于响应式,要实现响应式,关键要对浏览器窗口的变化做出监听,使用onresize事件监听浏览器窗口变化,使用offsetWidth获取窗口的宽度。

    同时为了代码的通用性,方便以后添加图片,不要把图片总数量的确切数值写在代码里,可以用提示点代表一张图片,用length获取总数。

    涉及临界情况时,要考虑图片的位置以及过渡效果的影响。

    动态获取绝对定位轮播图的高度,设置carousel_wrap的高度,宽度为整个main宽度。监听body大小变化,修改轮播图的图片位置和高度:

    // 动态获取绝对定位轮播图的高度,设置carousel_wrap的高度,宽度为整个main宽度
    	// 如果mystyle.css中使用overflow:auto->含有滚动条宽度; 故使用overflow:scroll
    	carouImg.style.left = -img.clientWidth + "px"; 
    	console.log(carouImg.style.left);
    	carouWrap.style.height = img.offsetHeight + "px";
    
    	// 监听body大小变化,修改轮播图的图片位置和高度
    	document.body.onresize = function () { 
    		carouImg.style.left = -img.clientWidth + "px";
    		carouWrap.style.height = img.offsetHeight + "px";
    	}
    

    点击左右箭头翻页

    next_pic()函数:

    // 下一张图片
    	function next_pic() {
    		var left = parseInt(carouImg.style.left);
    		if (left <= (-img.clientWidth) * (index_length + 1)) {
    			// 临界情况判断
    			carouImg.classList.remove("transition");
    			var newLeft = -img.clientWidth * 1;
    			carouImg.style.left = newLeft + 'px';
    			newLeft = -img.clientWidth * 2;
    			carouImg.classList.add("transition");
    			index = 1;
    		}
    		else {
    			// 一般情况
    			var newLeft = parseInt(carouImg.style.left) - img.clientWidth;
    			(index == (index_length - 1)) ? index = 0 : index += 1;
    		}
    		carouImg.style.left = newLeft + 'px'; // 不要忘记添加'px'
    		console.log(newLeft);
    	}
    

    pre_pic()函数:

    // 上一张图片
    	function pre_pic() {
    		var left = parseInt(carouImg.style.left);
    		if (left >= -10) {
    			// 临界情况判断
    			carouImg.classList.remove("transition");
    			var newLeft = -img.clientWidth * index_length;
    			carouImg.style.left = newLeft + 'px';
    			newLeft = -img.clientWidth * (index_length - 1);
    			carouImg.classList.add("transition");
    			index = index_length - 2;
    		}
    		else {
    			// 一般情况
    			var newLeft = parseInt(carouImg.style.left) + img.clientWidth;
    			(index == 0) ? index = (index_length - 1) : index -= 1;
    		}
    		carouImg.style.left = newLeft + 'px';
    		console.log(newLeft);
    	}
    

    显示提示点

    通过对CSS类的增删操作来实现提示点样式的变化。

        function showCurrentDot(index) {
    		for (let i = 0; i < oBtn.length; ++ i) {
    			(i == index) ? oBtn[i].classList.add("active") : oBtn[i].classList.remove("active");
    		}
    	}
    

    无缝过渡

    同样通过对类的增删操作,用transition属性实现过渡动画效果。

    // 给图片添加过渡效果
    	carouImg.classList.add("transition");
    

    定时器自动翻转

    设置定时器实现自动轮播,添加鼠标移入移出监听事件

    // 设置轮播定时器
    	var timer = setInterval(function(){
    		next_pic();
    		showCurrentDot(index);
    	}, 3000);
    
    	carouWrap.onmouseover = function() {
    		clearInterval(timer);
    	}
    
    	carouWrap.onmouseout = function() {
    		timer = setInterval(function () {
    			next_pic();
    			showCurrentDot(index);
    		}, 3000);
    	}
    

    代码整合

    html

    {% fold %}

    <!DOCTYPE html>
    <html>
    
    <head>
        <meta charset="utf-8" />
        <title>Lifeblog.com</title>
        <script type="text/javascript" src="js/friends.js"></script>
        <link rel="stylesheet" type="text/css" href="css/friends.css" />
    </head>
    
    <body>
        <!-- 轮播图 -->
        <div id="content">
            <div id="carousel_wrap">
                <div id="carousel_images">
                    <!-- 前后分别加上一张图片,方便无缝过渡显示。可以使用JS DOM增加节点操作省去该步骤 -->
                    <img src="https://s2.ax1x.com/2019/06/05/VNGTdP.jpg">
                    <img src="https://s2.ax1x.com/2019/06/05/VNG5qI.jpg">
                    <img src="https://s2.ax1x.com/2019/06/05/VNGoZt.jpg">
                    <img src="https://s2.ax1x.com/2019/06/05/VNGTdP.jpg">
                    <img src="https://s2.ax1x.com/2019/06/05/VNG5qI.jpg">
                </div>
                <span class="arrow left-arrow">&lt;</span>
                <span class="arrow right-arrow">&gt;</span>
                <div id="dots">
                    <!-- 使用小点标记实际多少张图片,要添加图片时需要修改carousel_images和此处 -->
                    <span class="dot active"></span>
                    <span class="dot"></span>
                    <span class="dot"></span>
                </div>
            </div>
            <div id="test">
                <h1>blog</h1>
                <h1>blogs</h1>
                <h1>blog</h1>
                <h1>blog</h1>
            </div>
        </div>
        <!-- END 轮播图 -->
    </body>
    </html>
    

    {% endfold %}

    CSS

    {% fold %}

    body {
    	overflow: scroll;
    }
    
    /* 轮播图样式表 */
    
    #content #carousel_wrap {
    	position: relative;
    	margin: 0 auto;
    	width: 100%; /* 轮播图宽度 */
    	overflow: hidden;
    }
    
    #content #carousel_wrap #carousel_images {
    	position: absolute;
    	border: 0;
    	outline: none;
    	white-space: nowrap; /* 将图片一行排列 */
    	width: 100%;
    	font-size: 0; /* 清除white-space间隙 */
    	margin: 0px; 
    }
    
    #content #carousel_wrap #carousel_images img {
    	width: 100%;
    }
    
    #content #carousel_wrap .arrow {
    	position: absolute;
    	font-weight: bold;
    	font-size: 50px;
    	color: lightgray;
    	top: 50%;
    	transform: translateY(-50%);
    	cursor: pointer;
    	transition-property: opacity;
    	transition-duration: 0.5s;
    }
    
    #content #carousel_wrap .arrow:hover {
    	opacity: 0.5;
    }
    
    #content #carousel_wrap .left-arrow {
    	left: 20px;
    }
    
    #content #carousel_wrap .right-arrow {
    	right: 20px;
    }
    
    #content #carousel_wrap #dots {
    	position: absolute;
    	bottom: 20px;
    	left: 50%;
    	transform: translateX(-50%)
    }
    
    #content #carousel_wrap .dot {
    	background-color: white;
    	display: inline-block;
    	width: 10px;
    	height: 10px;
    	border-radius: 50%;
    	margin: 4px;
    	opacity: 0.2;
    	cursor: pointer;
    }
    
    #content #carousel_wrap .active {
    	opacity: 1;
    }
    
    .transition {
    	transition-property: left;
    	transition-duration: 1s;
    }
    /* END 轮播图样式表 */
    

    {% endfold %}

    JS

    {% fold %}

    window.onload = function() {
    	var carouImg = document.getElementById("carousel_images");
    	var carouWrap = document.getElementById("carousel_wrap");
    	var img = carouImg.getElementsByTagName("img")[0];
    	var leftArrow = document.getElementsByClassName("left-arrow")[0];
    	var rightArrow = document.getElementsByClassName("right-arrow")[0];
    	var oBtn = document.getElementsByClassName("dot");
    	var index = 0;
    	var index_length = oBtn.length;
    
    	// 给图片添加过渡效果
    	carouImg.classList.add("transition");
    
    	// 动态获取绝对定位轮播图的高度,设置carousel_wrap的高度,宽度为整个main宽度
    	// 如果mystyle.css中使用overflow:auto->含有滚动条宽度; 故使用overflow:scroll
    	carouImg.style.left = -img.clientWidth + "px"; 
    	console.log(carouImg.style.left);
    	carouWrap.style.height = img.offsetHeight + "px";
    
    	// 监听body大小变化,修改轮播图的图片位置和高度
    	document.body.onresize = function () { 
    		carouImg.style.left = -img.clientWidth + "px";
    		carouWrap.style.height = img.offsetHeight + "px";
    	}
    	
    	// 点击右箭头
    	rightArrow.onclick = function() {
    		next_pic();
    		showCurrentDot(index);
    	}
    
    	// 点击左箭头
    	leftArrow.onclick = function () {
    		pre_pic();
    		showCurrentDot(index);
    	}
    	
    	// 点击小点
    	for (let i = 0; i < oBtn.length; ++ i) {
    		oBtn[i].onclick = function() {
    			var newLeft = (-img.clientWidth) * (i + 1);
    			carouImg.style.left = newLeft + 'px';
    			console.log(i);
    			showCurrentDot(i);
    		}
    	}
    
    	// 下一张图片
    	function next_pic() {
    		var left = parseInt(carouImg.style.left);
    		if (left <= (-img.clientWidth) * (index_length + 1)) {
    			// 临界情况判断
    			carouImg.classList.remove("transition");
    			var newLeft = -img.clientWidth * 1;
    			carouImg.style.left = newLeft + 'px';
    			newLeft = -img.clientWidth * 2;
    			carouImg.classList.add("transition");
    			index = 1;
    		}
    		else {
    			// 一般情况
    			var newLeft = parseInt(carouImg.style.left) - img.clientWidth;
    			(index == (index_length - 1)) ? index = 0 : index += 1;
    		}
    		carouImg.style.left = newLeft + 'px'; // 不要忘记添加'px'
    		console.log(newLeft);
    	}
    
    	// 上一张图片
    	function pre_pic() {
    		var left = parseInt(carouImg.style.left);
    		if (left >= -10) {
    			// 临界情况判断
    			carouImg.classList.remove("transition");
    			var newLeft = -img.clientWidth * index_length;
    			carouImg.style.left = newLeft + 'px';
    			newLeft = -img.clientWidth * (index_length - 1);
    			carouImg.classList.add("transition");
    			index = index_length - 2;
    		}
    		else {
    			// 一般情况
    			var newLeft = parseInt(carouImg.style.left) + img.clientWidth;
    			(index == 0) ? index = (index_length - 1) : index -= 1;
    		}
    		carouImg.style.left = newLeft + 'px';
    		console.log(newLeft);
    	}
    
    	function showCurrentDot(index) {
    		for (let i = 0; i < oBtn.length; ++ i) {
    			(i == index) ? oBtn[i].classList.add("active") : oBtn[i].classList.remove("active");
    		}
    	}
    
    	// 设置轮播定时器
    	var timer = setInterval(function(){
    		next_pic();
    		showCurrentDot(index);
    	}, 3000);
    
    	carouWrap.onmouseover = function() {
    		clearInterval(timer);
    	}
    
    	carouWrap.onmouseout = function() {
    		timer = setInterval(function () {
    			next_pic();
    			showCurrentDot(index);
    		}, 3000);
    	}
    }
    
    

    {% endfold %}


    思考和总结

    总的来说,这一次的轮播图实现,自己还是比较满意的,从代码的优化通用性和逻辑的清晰度来说,都比之前写的那次轮播图好多了,更不用说这一次的轮播图的实现难度比上次的要大,这次的轮播要求是响应式而不是定长的,这么想想自己还是挺高兴的。

    不过也有不足,本以为是“完美”的轮播了,但是还是有十分小的bug:进入第一张和最后一张图片的补充替补图片时,点击提示点,过渡是按照替补图片而不是正式图片过渡的,这里是不足的地方。

    寻思着可以通过index来区分正式和替补图片,然后消除过渡,把正式图片和替补图片互换,再开启过渡效果,这样就可以解决这个小bug了。

    不过最近时间很紧,小作业大作业好多,日后有机会再修改成“完美”的代码。


    更新

    更新:解决了上面提到的bug,思路就是总结里提到的,在关键地方清除过渡,修改图片位置,再添加过渡。

    具体做法:在点击提示点的代码中修改。

    注意click事件里left值修改完毕再添加transition过渡,如果只是修改到实际图片位置,但是没有修改本次点击图片位置就添加transition,bug依然存在,这里没有想明白,难道是因为click事件点击和松开鼠标这个过程的某种原理吗?

    for (let i = 0; i < oBtn.length; ++ i) {
    		oBtn[i].onclick = function() {
    			var left = parseInt(carouImg.style.left);
    			var newLeft;
    
    			// 如果没有临界判断,当图片位于“替补图片”时,点击提示点会有错乱过渡
    			if (left <= (-img.clientWidth) * (index_length + 1)) {
    				// 临界情况判断
    				carouImg.classList.remove("transition");
    				newLeft = -img.clientWidth * 1;
    				carouImg.style.left = newLeft + 'px';
    			}
    			if (left >= -10) {
    				// 临界情况判断
    				carouImg.classList.remove("transition");
    				newLeft = -img.clientWidth * index_length;
    				carouImg.style.left = newLeft + 'px';
    			}
    			
    			newLeft = (-img.clientWidth) * (i + 1);
    			carouImg.style.left = newLeft + 'px';
    			// 注意click事件的执行过程,要在修改完left后添加transition类
    			carouImg.classList.add("transition");
    			index = i;
    			showCurrentDot(i);
    		}
    	}
    
    初学前端,记录学习的内容和进度~
  • 相关阅读:
    python flask学习笔记
    语音识别2 -- Listen,Attend,and Spell (LAS)
    语音识别 1--概述
    keras中seq2seq实现
    ResNet模型
    Bytes类型
    Python操作文件
    Pyhon基本数据类型
    ping
    find
  • 原文地址:https://www.cnblogs.com/xiuhua/p/13398778.html
Copyright © 2011-2022 走看看