• 原生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);
    		}
    	}
    
    初学前端,记录学习的内容和进度~
  • 相关阅读:
    LeetCode15 3Sum
    LeetCode10 Regular Expression Matching
    LeetCode20 Valid Parentheses
    LeetCode21 Merge Two Sorted Lists
    LeetCode13 Roman to Integer
    LeetCode12 Integer to Roman
    LeetCode11 Container With Most Water
    LeetCode19 Remove Nth Node From End of List
    LeetCode14 Longest Common Prefix
    LeetCode9 Palindrome Number
  • 原文地址:https://www.cnblogs.com/xiuhua/p/13398778.html
Copyright © 2020-2023  润新知