轮播图(pc端)
网页轮播图也叫焦点图,是比较常见的网页特效。虽然使用插件可以非常快速方便实现该功能,但个人觉得自己用源码写一遍受益匪浅。所以看完老师视频后自己写了一遍并总结一下思路。
功能需求
- 鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮。
- 点击右侧按钮一次,图片往左播放一张,以此类推, 左侧按钮同理。
- 图片播放的同时,下面小圆圈模块跟随一起变化。
- 点击小圆圈,可以播放相应图片。
- 鼠标不经过轮播图, 轮播图也会自动播放图片。
- 鼠标经过,轮播图模块, 自动播放停止。
html 结构
<div class="focus fl">
<ul class="clearfix">
<li><a href="#"><img src="upload/focus1.jpg" alt=""></a></li>
<li><a href="#"><img src="upload/focus2.jpg" alt=""></a></li>
<li><a href="#"><img src="upload/focus3.jpg" alt=""></a></li>
<li><a href="#"><img src="upload/focus4.jpg" alt=""></a></li>
</ul>
<ol class="circle"></ol>
<button class="arrow_left"></button>
<button class="arrow_right"></button>
</div>
原理分析
// 将ul设置得足够宽使li能够一行显示并对ul进行移动(使用缓慢动画)
// focus盒子溢出隐藏
点击左右按钮时,先调用 circleChange() 使小圆圈改变,参数是 pictureIndex(在最后一张和第一张之间的切换特殊处理);这样便达到了小圈圈随图片而改变的需求。在点击小圈圈时,必须将小圈圈的自定义属性 data-index 的值传递给 pictureIndex;这样才能在点击按钮时正确移动 ul 的位置(确定下张图片)。
js实现代码
缓慢动画函数的封装和使用(考虑兼容性问题不使用 c3 移动变换样式属性)
1、元素获取、创建 ol 中的 li 并设置自定义属性 data-index 来标识该 li 。在创建的同时为 li (小圆圈) 绑定点击事件。
window.addEventListener('load', function() {
var focus = document.querySelector('.focus');
var ul = focus.querySelector('ul');
var ol = focus.querySelector('ol');
var arrow_l = focus.querySelector('.arrow_left');
var arrow_r = focus.querySelector('.arrow_right');
// 声明并初始化两个变量,pictureIndex用于标识当前显示图片的索引
// flag为函数锁状态(节流阀参数),用来标识左右按钮的点击事件程序是否处于执行状态。
var pictureIndex = 0;
var flag = 1;
for (var i = 0; i < ul.children.length; i++) {
var li = document.createElement('li');
ol.appendChild(li);
li.setAttribute('data-index', i);
li.addEventListener('click', function() {
// dataIndex获取自定义属性data-index标识点击了哪个小圆圈
var dataIndex = this.getAttribute('data-index');
// 改变小圆圈的当前选中状态
circleChange(dataIndex);
// 将当前需要显示的图片的索引标识变量pictureIndex重新赋值,并实现焦点图随小圆圈滚动
pictureIndex = dataIndex;
animate(ul, -pictureIndex * focus.offsetWidth);
})
}
// 在小圆圈创建好之后要先默认第一个的选中状态
ol.children[0].className = 'current';
2、鼠标经过、离开时焦点图,左右按钮实现显示和隐藏。经过时焦点图停止自动播放,离开时恢复自动播放。
focus.addEventListener('mouseenter', function() {
arrow_l.style.visibility = 'visible';
arrow_r.style.visibility = 'visible';
// 清除自动播放的定时器
clearInterval(focus.timer);
})
focus.addEventListener('mouseleave', function() {
arrow_l.style.visibility = 'hidden';
arrow_r.style.visibility = 'hidden';
// 恢复自动播放的定时器,即每1.5秒调用右按钮点击处理程序
this.timer = setInterval(function() {
arrow_r.click();
}, 1500);
})
3、左右按钮功能实现。当到最后一张即第四张时,再点一下右按钮,pictureIndex 会变成4 此时应该重新将它设置为0,但这样跑回第一张的话不能实现无缝衔接,所以要将第一个元素克隆到最后面先让其以动画效果移动到最后一个克隆元素的位置,我们再利用回调函数将它快速不以动画的形式移动回第一张的位置即可。从第一张到最后一张点击左按钮时则反过来,先快速回到克隆元素再做动画。
var first = ul.children[0].cloneNode(true);
ul.appendChild(first);
// 3、右边按钮功能的实现
arrow_r.addEventListener('click', function() {
if (flag) {
flag = 0;
pictureIndex++;
// 包含克隆的图片一共5张,第4张时pictureIndex = 3,点击后pictureIndex++ = 4(这个要捋顺)。
if (pictureIndex == ul.children.length - 1) {
// 小圆圈随图片变化,放在动画前面,不然会有点滞后变化的感觉
circleChange(0);
animate(ul, -pictureIndex * focus.offsetWidth, function() {
pictureIndex = 0;
ul.style.left = 0;
flag = 1;
});
} else {
circleChange(pictureIndex); // 第55行
animate(ul, -pictureIndex * focus.offsetWidth, function() {
flag = 1;
});
}
}
})
// 4、左边按钮功能的实现
arrow_l.addEventListener('click', function() {
if (flag) {
flag = 0;
pictureIndex--;
if (pictureIndex < 0) {
circleChange(ol.children.length - 1); // 放在定时器前面,不然会有点滞后变化的感觉
ul.style.left = -ol.children.length * focus.offsetWidth + 'px';
pictureIndex = ol.children.length - 1;
// 要先瞬间回到最后克隆的那一张再调用动画函数
animate(ul, -pictureIndex * focus.offsetWidth, function() {
flag = 1;
});
} else {
circleChange(pictureIndex);
animate(ul, -pictureIndex * focus.offsetWidth, function() {
flag = 1;
});
}
}
})
4、上面使用到的小圆圈变化函数和一开始焦点就要自动播放所以要定义定时器。
// 5、定义小圆圈随图片滚动发生变化的函数,可以在图片滚动时(左右按钮点击时调用),将pictureIndex(dataIndex)作为参数传入即可
function circleChange(circle) {
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
ol.children[circle].className = 'current'; // 第66行
}
// 6、自动轮播功能其实就是设置一个定时器鼠标离开时每过一段事件就自动触发右边按钮的点击事件
focus.timer = setInterval(function() {
arrow_r.click();
}, 1500);
})
题外话:
- 当没有添加节流阀时,用户快速点击左右按钮时。会出现在最后一张图片到第一张图片时出现 bug 。(出现所有小圆点都没选中的情况,再次点击时会小圆圈又正常)。错误信息:如下,相应的行已在代码中标明。
// index.js:66 Uncaught TypeError: Cannot set property ‘className’ of undefined
// at circleChange (index.js:66)
// at HTMLButtonElement.<anonymous> (index.js:55)
个人想法是由于点击太快导致触发另一个动画而结束前一个动画时里面的回调函数没执行 。看了挺久代码没看出个所以然,所以先放下。- 总结:自己单独写时建议按需求步骤实现(我没注意到这个问题,所以当时是想到什么写什么。),写完之后发现自己的代码不够简洁,在写笔记时不断进行了改善。
轮播图(移动端)
移动端和 pc 端的原生 js 代码实现轮播图有点差异(因为使用的是触屏事件),以后有时间在补上。