下面的代码用纯js封装一个轮播组件。功能如下:
可上滑、下滑、左滑、右滑。无缝轮播。
移动端支持手势滑动。
可指定第一个显示的轮播图。
支持放置文字、图片和视频。带导航器。
导航器可自定义样式。若不想自定义样式,系统自带一个样式。
自适应元素大小。图片大小尺寸不限。
如果轮播图只有一张,或者不是轮播而只是一张图片,也处理其样式使其适应窗口。
自动处理了超高图和超宽图,以适应轮播区域。
这个功能大都用在首页顶部,或者商品详情。你甚至不需要写其它代码,通过这个组件你就能正确处理商品详情页顶部轮播或图片的样式问题。一句话调用即可!
用户不需要写额外代码,仅提供要轮播的html元素,以及一句话调用即可实现如上功能。
2022-3-11更新:
增加height参数,用来指定轮播的高度。height可以取数字,也可以带单位px。例如:200 或 '200px' 均可。
修复只有2张轮播图时左右滚动的问题。
先上个效果图:
系统默认的导航器样式也不难看。所以您可以省一段导航器样式的代码了
封装实现:
function wm_Carousel(param) {
this.id = param.id;
this.container = document.getElementById(this.id);
if (this.container == null) {
console.warn("container not found");
return false;
}
this.filter = param.filter;
this.container_box = this.container.querySelector(this.filter);
if (this.container_box == null) {
console.warn("container_box not found");
return false;
}
if (typeof param.height == 'undefined') {
this.height = window.innerWidth;
} else {
var reg = /^\d+$/;
if (reg.test(param.height) != false) {
this.height = param.height;
} else {
this.height = param.height.replace(/px/ig, "");
}
}
this.container_box.style.backgroundColor = '#f2f2f2';
this.interval = (param.interval < 1000 && param.interval > 0) ? param.interval * 1000 : (param.interval == 0 ? 1500 : param.interval);
this.item_el = param.item_element;
this.child = this.container_box.querySelectorAll(this.item_el);
//下面的if语句意思是,如果轮播图只有一个,或者没有轮播图,只有一个封面,
//那么我们可以不认为它是一个轮播,只当作是一个图片。
//这些代码就让这个图片也自适应窗口。
//那么,即使不是轮播,只是一张图片,也可以使用本系统渲染。
//只不过在一句话调用的时候,只实例化对象,不要调用create()方法。
//如果你不想让系统处理这种情况,把下面的if语句删除即可。
if (this.child.length == 1) {
this.container_box.style.height = this.height + 'px';
this.child[0].style.height = this.height + 'px';
this.child[0].style.display = 'flex';
this.child[0].style.alignItems = 'center';
this.child[0].style.justifyContent = 'center';
var img = this.container_box.querySelectorAll('img');
for (var i = 0; i < img.length; i++) {
img[i].style.maxHeight = '100%';
img[i].style.maxWidth = '100%';
}
var video = this.container_box.querySelectorAll('video');
for (var i = 0; i < video.length; i++) {
video[i].style.maxHeight = '100%';
video[i].style.maxWidth = '100%';
}
return;
}
this.index = (typeof param.index == 'undefined' || param.index == '') ? 0 : param.index - 1;
if(this.index > this.child.length){
this.index = 0;
}
if(this.child.length == 0){
console.warn("carousel item not found");
return false;
}
this.child_length = this.child.length;
this.direction = typeof param.direction != 'undefined' ? param.direction : 'left';
["up", "down", "left", "right"].indexOf(this.direction) == -1 ? this.direction = "left" : '';
this.item_width = 0;
this.item_height = 0;
this.duration = param.duration ? param.duration : '0.7s';
this.navigator_class = param.navigator_class;
this.touch_source = {};
}
wm_Carousel.prototype = {
create: function () {
if (this.container == null) {
console.warn("container is null");
return false;
}
if (this.container_box == null) {
console.warn("container_box is null");
return false;
}
if (this.child_length == 0) {
console.warn("carousel item is empty");
return false;
}
this.container.style.overflow = 'hidden';
this.container.style.position = "relative";
this.container.style.height = this.height + 'px';
this.container_box.style.position = "absolute";
if (this.direction == "left" || this.direction == "right") {
this.container_box.style.width = ((this.child_length + 2) * this.container.offsetWidth) + 'px';
this.container_box.style.height = "100%";
} else {
this.container_box.style.height = ((this.child_length + 2) * this.container.offsetHeight) + 'px';
this.container_box.style.width = "100%";
}
this.navigator_element = document.createElement("div");
if (this.navigator_class == null || this.navigator_class == '' || typeof this.navigator_class == 'undefined') {
this.navigator_element.style.position = "absolute";
this.navigator_element.style.bottom = "20px";
this.navigator_element.style.backgroundColor = "rgba(0,0,0,0.2)";
this.navigator_element.style.padding = "5px";
this.navigator_element.style.display = "flex";
this.navigator_element.style.justifyContent = "space-between";
this.navigator_element.style.left = "50%";
this.navigator_element.style.transform = "translate(-50%,0)";
this.navigator_element.style.borderRadius = "10px";
} else {
this.navigator_element.className = this.navigator_class;
}
this.container.appendChild(this.navigator_element);
var img = this.container_box.querySelectorAll('img');
for (var i = 0; i < img.length; i++) {
img[i].style.maxHeight = '100%';
img[i].style.maxWidth = '100%';
}
var video = this.container_box.querySelectorAll('video');
for (var i = 0; i < video.length; i++) {
video[i].style.maxHeight = '100%';
video[i].style.maxWidth = '100%';
}
for (i = 0; i < this.child_length; i++){
this.child[i].style.width = this.container.offsetWidth + 'px'
this.child[i].style.height = this.container.offsetHeight + 'px';
this.child[i].style.display = 'flex';
this.child[i].style.alignItems = 'center';
this.child[i].style.justifyContent = 'center';
if (this.direction == "left" || this.direction == "right") {
this.child[i].style.float = "left";
}
if (this.navigator_class == null || this.navigator_class == '' || typeof this.navigator_class == 'undefined') {
this.navigator_element.innerHTML += "<div style='margin: 0 3px;width: 10px;height: 10px;border-radius: 50%;background: rgba(255,255,255,0.5);"+ (i == 0 ? "background:rgba(255,255,255,1)" : "background:rgba(255,255,255,0.5)") +";' data-index='" + i + "'></div>";
} else {
this.navigator_element.innerHTML += "<div class='wm_carousel_navigator_item" + (i == 0 ? " current" : "") + "' data-index='" + i + "'></div>";
}
}
this.item_width = this.child[0].offsetWidth;
this.item_height = this.child[0].offsetHeight;
var first = this.child[this.child_length - 1].cloneNode(true);
var last = this.child[0].cloneNode(true)
this.container_box.appendChild(last)
this.container_box.insertBefore(first, this.child[0]);
this.index += 1;
if (this.direction == "left" || this.direction == "right") {
this.container_box.style.left = -this.item_width * (this.index) + 'px';
} else {
this.container_box.style.top = -this.item_height * (this.index) + 'px';
}
this.navigator(this.index - 1)
this.index -= 1;
var that = this;
this.container_box.addEventListener('touchstart', function (e) {
that.touch_source['x'] = e.changedTouches[0].clientX;
that.touch_source['y'] = e.changedTouches[0].clientY;
})
this.container_box.addEventListener('touchend', function (e) {
var x = e.changedTouches[0].clientX;
var y = e.changedTouches[0].clientY;
if (that.direction == "left" || that.direction == "right") {
var distance = x - that.touch_source.x;
if(Math.abs(distance) > 50){
that.calcIndex(distance > 0 ? 'right' : 'left');
}
} else {
var distance = y - that.touch_source.y;
if(Math.abs(distance) > 50){
that.calcIndex(distance > 0 ? 'down' : 'up');
}
}
})
this.startInterval();
},
startInterval: function () {
var that = this;
setInterval(function () {
that.calcIndex(that.direction);
}, that.interval);
},
calcIndex: function (direction) {
var nextIndex = 0;
if(this.child_length == 1){
return;
}
if(direction == 'left' || direction == 'up'){
if (this.index < this.child_length){
nextIndex = this.index + 1;
} else {
nextIndex = 1;
}
} else if(direction == 'right' || direction == 'down'){
if (this.index > -1){
nextIndex = this.index - 1;
} else {
nextIndex = this.child_length - 2;
}
}
this.carousel(direction, nextIndex);
this.navigator(nextIndex);
},
carousel: function (direction, nextIndex) {
this.container_box.style.transitionDuration = "";
var target;
console.log("this.index=" + this.index + ",nextIndex=" + nextIndex)
if(direction == 'left' || direction == 'up'){
if (Math.abs(this.index - nextIndex) == 1) {
if (this.index - nextIndex == 1) {
this.container_box.style.transitionDuration = '0s';
if(direction == 'left'){
this.container_box.style.left = -this.item_width + 'px';
target = this.container_box.offsetLeft;
target = -this.item_width * 2;
this.container_box.style.transitionDuration = this.duration;
this.container_box.style.left = target + 'px';
} else {
this.container_box.style.top = -this.item_height + 'px';
target = this.container_box.offsetTop;
target = -this.item_height * 2;
this.container_box.style.transitionDuration = this.duration;
this.container_box.style.top = target + 'px';
}
} else {
if (direction == 'left') {
target = -this.item_width * (nextIndex + 1);
} else {
target = -this.item_height * (nextIndex + 1);
}
}
this.container_box.style.transitionDuration = this.duration;
if(direction == 'left'){
this.container_box.style.left = target + 'px';
} else {
this.container_box.style.top = target + 'px';
}
} else {
this.container_box.style.transitionDuration = '0s';
if(direction == 'left'){
this.container_box.style.left = -this.item_width + 'px';
target = this.container_box.offsetLeft;
target = -this.item_width * 2;
this.container_box.style.transitionDuration = this.duration;
this.container_box.style.left = target + 'px';
} else {
this.container_box.style.top = -this.item_height + 'px';
target = this.container_box.offsetTop;
target = -this.item_height * 2;
this.container_box.style.transitionDuration = this.duration;
this.container_box.style.top = target + 'px';
}
}
} else if(direction == 'right' || direction == 'down'){
if (Math.abs(this.index - nextIndex) == 1) {
if (this.index - nextIndex == 1) {
if(direction == 'right'){
target = -this.item_width * (nextIndex + 1);
} else {
target = -this.item_height * (nextIndex + 1);
}
this.container_box.style.transitionDuration = this.duration;
if(direction == 'right'){
this.container_box.style.left = target + 'px';
} else {
this.container_box.style.top = target + 'px';
}
} else {
this.container_box.style.transitionDuration = '0s';
if(direction == 'right'){
this.container_box.style.left = -this.item_width * this.child_length + 'px';
target = this.container_box.offsetLeft;
target = -this.item_width;
this.container_box.style.transitionDuration = this.duration;
this.container_box.style.left = target + 'px';
} else {
this.container_box.style.top = -this.item_height * this.child_length + 'px';
target = this.container_box.offsetTop;
target = -this.item_height;
this.container_box.style.transitionDuration = this.duration;
this.container_box.style.top = target + 'px';
}
}
} else {
this.container_box.style.transitionDuration = '0s';
if(direction == 'right'){
this.container_box.style.left = -this.item_width * (this.child_length) + 'px';
target = this.container_box.offsetLeft;
target = -this.item_width * (this.child_length - 1);
this.container_box.style.transitionDuration = this.duration;
this.container_box.style.left = target + 'px';
} else {
this.container_box.style.top = -this.item_height * (this.child_length) + 'px';
target = this.container_box.offsetTop;
target = -this.item_height * (this.child_length - 1);
this.container_box.style.transitionDuration = this.duration;
this.container_box.style.top = target + 'px';
}
}
}
this.index = nextIndex;
},
navigator: function (thisIndex) {
var navigator_child = this.navigator_element.querySelectorAll("div");
var navigator_class = this.navigator_class;
var current = null;
if (navigator_class == null || navigator_class == '' || typeof navigator_class == 'undefined') {
for (i = 0; i < navigator_child.length; i++) {
navigator_child[i].style.background = "rgba(255,255,255,0.5)";
}
} else {
for (i = 0; i < navigator_child.length; i++) {
navigator_child[i].classList.remove("current");
}
}
if(thisIndex > 0 && thisIndex < this.child_length){
current = navigator_child[thisIndex];
} else if (thisIndex == -1) {
current = navigator_child[navigator_child.length - 1]
} else if (thisIndex == 0){
current = navigator_child[0];
} else if(thisIndex == this.child_length){
current = navigator_child[0];
}
if (navigator_class == null || navigator_class == '' || typeof navigator_class == 'undefined') {
current.style.background = "rgba(255,255,255,1)";
} else {
current.classList.add("current");
}
}
}
调用方法:
var carousel = new wm_Carousel({id:'carousel', filter: '.carousel_ul', item_element: 'li', direction:'left', interval: 2, duration:'0.7s', navigator_class:'navigator', index: 1});
if(carousel.child_length > 1){
carousel.create();
}
/*
如果只有一张轮播图,或者不是轮播而只有一张图片,不要调用create()方法,这样就不会出现导航器了。
*/
/*参数说明
id:外层容器。必须的
filter:要滚动的元素。必须的。
item_element: 要滚动的元素的子元素的类型,必须的。比如 div li。这个要根据具体是什么来设置
direction:滚动方向,可以不设置,默认为left。只有4个选项:up、down、left、right。即上下左右
interval:多少秒滚动一次。可以不设置。默认1.5秒。单位嘛凭你感觉来设置即可。比如你觉得是秒,那可以设置为3,你要是觉得是毫秒,那可以设置为3000
duration:缓动时长。也就是多长时间滚动就位。可以不设置,默认为0.7秒。
navigator_class 导航器样式类名。可以不设置,系统自带一个。如果这一项设置了值,就需要写针对导航器的样式。具体写法请参见下面的测试代码。
index:第一个界面显示哪一个条目。可以不设置。默认为第一个。若要设置,第一个为1,第二个为2。以此类推。
height:轮播的高度。单位为px。但是可以不写单位。例如: 200 或 '200px'
*/
测试代码如下(把上面封装的代码保存到js文件里。我这里取名为c.js):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
<script src="c.js" type="text/javascript" charset="utf-8"></script>
<title></title>
<style>
*{margin: 0; padding: 0;}
ul li{color:white; list-style: none;}
/*以下代码是自定义导航器样式用的。可以不写。如果不写,在调用的时候,不要指定导航器样式类名*/
.navigator{position: absolute;
bottom: 10px;
background-color: rgba(0, 0, 0, 0.2);
padding: 5px;
display: flex;
justify-content: space-between;
left: 50%;
transform: translate(-50%, 0px);
border-radius: 10px;
}
.navigator .wm_carousel_navigator_item{
margin: 0 2px;
width: 10px;
height: 10px;
border-radius: 50%;
background: rgba(255,255,255,0.5);
}
.navigator .current{
background: rgba(255,255,255,1);
}
</style>
</head>
<body>
<!--不要对下面的div、ul设置高度宽度。以便让系统可以正确适应各种尺寸的图片-->
<div id="carousel">
<ul class="carousel_ul">
<li style="background-color: #f00;"><img src="1.jpg" alt=""></li>
<li style="background-color:blueviolet;"><img src="2.png" alt=""></li>
<li style="background-color:aqua;"><video src="4.mp4" controls="controls"></video></li>
<li style="background-color:#ff00ff;">5555555555</li>
<li style="background-color:#ffaa7f;">66666666666</li>
</ul>
</div>
<script>
var carousel = new wm_Carousel({id:'carousel', filter: '.carousel_ul', item_element: 'li', direction:'left', interval: 2, duration:'0.7s', navigator_class:'', index: 1}).create();
</script>
</body>
</html>
<!--background-color 这个可以不设置。我加上主要是为了好测试。直接写<li>xxxx</li>即可。
头部的 style 部分也可以不写,navigator 类是自定义导航器样式用的。
-->
注意调用代码:
var carousel = new wm_Carousel({id:'carousel', filter: '.carousel_ul', item_element: 'li', direction:'left', interval: 2, duration:'0.7s', navigator_class:'', index: 1}).create();
里面的 navigator_class,如果你不想自定义导航器样式,这里不要赋值,也可以直接不写这个配置。如果想自定义样式,样式类名请和style里写的一样(navigator 这个类名可以随便取名,wm_carousel_navigator_item和current必须这样写,不能取别的名字)。并且在这里指定类名。即:
var carousel = new wm_Carousel({id:'carousel', filter: '.carousel_ul', item_element: 'li', direction:'left', interval: 2, duration:'0.7s', navigator_class:'这里写导航器样式类名', index: 1}).create();
上面这是一种方法,还有另外一种方法,就是不滚动ul,而是每次取需要参与滚动的2个条目,只滚动这两个。有兴趣的可以自己写写。
当然,也可以用canvas来写轮播。用canvas的效率是最高的。
我在项目中是这样使用的:
<div id="carousel">
<ul class="carousel_ul" v-if="sliders.length>0">
<li v-for="sl in sliders">
<img :src="sl" />
</li>
</ul>
<ul class="carousel_ul" v-else>
<li class="goods_detail_img">
<img :src="upload_dir + coverimage">
</li>
</ul>
</div>
上面是vue的html部分。
if(data.result.slider_image != null && data.result.slider_image != ''){
thisdata.sliders = data.result.slider_image;
setTimeout(function () {
var carousel = new Carousel({id: 'carousel', filter: '.carousel_ul', item_element: 'li', direction: 'left', interval: 2, duration: '0.7s', navigator_class: '', index:2});
if(carousel.child_length > 1){
carousel.create();
}
}, 0)
} else {
thisdata.sliders = [];
thisdata.coverimage = data.result.coverimage;
new Carousel({id: 'carousel', filter: '.carousel_ul', item_element: 'li'});
}
上面是获取轮播数据后的处理。
如有问题,请在评论中指出,谢谢。
csdn博客原创首发,转载需要注明出处。