原生js实现一个轮播组件

最近自己使用vue实现了carousel轮播组件,整体上的思路和之前的原生js有很大的区别,对应的动画效果的实现是一个比较大的难点。这我们用原生js来实现轮播组件,方便我们对框架和原生实现方式有一个认知和对比,也正好复习一下原生和DOM相关的API

在样式方面,大家可以发挥自己的想象力,也可以参考社区其它的类似组件,这里我实现了一个简易样式版本,供大家参考:

  • 预览地址:wangkaiwd.github.io/vue-example…
  • 源码地址:github.com/wangkaiwd/v…

vue版本的在这里,并且支持移动端滑动切换:

  • 预览地址:wangkaiwd.github.io/smile-ui/vi…
  • 源码地址:github.com/wangkaiwd/s…

在实现组件之前,我们需要先了解一下有逢轮播和无缝轮播。

有缝轮播在轮播项正向移动到第一项或反向移动到最后一项时,会有一个回退效果。而无缝轮播会让人感觉不到这个切换效果。

接下来我们一步步通过原生js先实现一个有缝轮播,之后升级到无缝轮播,实现最终效果。

有缝轮播

有缝轮播相对来说在实现思路上会简单一些,主要是利用了css3提供的transitontranslate属性来实现切换的动画效果。

首先搭建初始的页面结构,效果大概是这样的:

之后我们会在这个基础上让1,2,3动起来

现在假设我们是使用组件的用户,我们理想中的使用方式大概是这样的:

<div class="slider"><div class="slider-item">1</div><div class="slider-item">2</div><div class="slider-item">3</div>
</div>
复制代码
const slider = new Slider('.wk-slider',options);
复制代码

为了页面的美观,我们添加一些初始化样式

/*初始化样式*/
* {margin: 0;padding: 0;
}
*,
*::after,
*::before {box-sizing: inherit;
}
/*通配符的css权重是最低的,html的标签都会继承box-sizing并且对应元素进行盒模型更改的时候,对应的子元素也会更改
*/
html {box-sizing: border-box;
}
复制代码

用户自己的样式是这样的:

/*用户样式*/
.demo-wrapper {margin: 40px;
}
.slider {margin: 0 auto;border: 4px solid black;overflow: hidden;
}
.slider-item {width: 400px;height: 200px;background-color: pink;font-size: 80px;text-align: center;line-height: 200px;color: #fff;
}
复制代码

接下来我们进行js逻辑的实现。

由于组件在使用时第一个参数为element,第二个参数为options配置项,所以我们的构造函数需要这样写:

class Slider {constructor (element, options) {this.slider = document.querySelector(element);this.options = options;}
}
复制代码

之后我们要获取到轮播项的对应的宽度赋值到slider元素上,并为对应的元素添加特定前缀的css类名,防止样式冲突。而且由于要让整个子元素进行平移,还要将所有子元素放到一起,然后再进行平移,并通过transition设置过渡动效。

我们在构造函数的原型上添加对应的方法

initSliderStyle () {this.items = [...this.slider.children];// 这里获取宽度时要小心异步加载this.itemWidth = this.items[0].offsetWidth;this.slider.classList.add('wk-slider');this.slider.style.width = `${this.itemWidth}px`;this.createItemsWrapper();
}createItemsWrapper () {this.itemsWrapper = document.createElement('div');this.itemsWrapper.classList.add('wk-slider-items-wrapper');this.slider.appendChild(this.itemsWrapper);this.items.map(item => {this.itemsWrapper.appendChild(item);item.classList.add('wk-slider-item');});
}
复制代码

对应的组件css

/*组件样式*/
.wk-slider {display: flex;
}
.wk-slider-item {flex-shrink: 0;
}
.wk-slider-items-wrapper {display: flex;flex-shrink: 0;transition: all 1s;
}
复制代码

最后我们为子元素容器设置定时器,让子元素动起来:

autoPlay () {if (!this.options.autoPlay) return;setInterval(() => {this.index++;this.go(this.index);}, 2000);
}go (index) {const lastIndex = this.items.length - 1;if (index > lastIndex) {this.index = 0;}if (index < 0) {this.index = lastIndex;}this.itemsWrapper.style.transform = `translateX(${-this.itemWidth * this.index}px)`;
}
复制代码

最终效果:

完整代码如下:

class Slider {constructor (element, options) {this.slider = document.querySelector(element);this.options = options;this.index = 0;this.initSliderStyle();this.autoPlay();}initSliderStyle () {this.items = [...this.slider.children];// 这里获取宽度时要小心异步加载this.itemWidth = this.items[0].offsetWidth;this.slider.classList.add('wk-slider');this.slider.style.width = `${this.itemWidth}px`;this.createItemsWrapper();}createItemsWrapper () {this.itemsWrapper = document.createElement('div');this.itemsWrapper.classList.add('wk-slider-items-wrapper');this.slider.appendChild(this.itemsWrapper);this.items.map(item => {this.itemsWrapper.appendChild(item);item.classList.add('wk-slider-item');});}autoPlay () {if (!this.options.autoPlay) return;setInterval(() => {this.index++;this.go(this.index);}, 2000);}go (index) {const lastIndex = this.items.length - 1;const { itemsWrapper, itemWidth } = this;if (this.index > lastIndex) { this.lastToFirst(); }if (this.index < 0) { this.firstToLast(lastIndex); }itemsWrapper.style.transform = `translateX(${-itemWidth * this.index}px)`;}
}const slider = new Slider('.slider', { autoPlay: true });
复制代码
/*初始化样式*/
* {margin: 0;padding: 0;
}
*,
*::after,
*::before {box-sizing: inherit;
}
/*通配符的css权重是最低的,html的标签都会继承box-sizing并且对应元素进行盒模型更改的时候,对应的子元素也会更改
*/
html {box-sizing: border-box;
}/*用户样式*/
.demo-wrapper {margin: 40px;
}
.slider {margin: 0 auto;border: 4px solid black;overflow: hidden;
}
.slider-item {width: 400px;height: 200px;background-color: pink;font-size: 80px;text-align: center;line-height: 200px;color: #fff;
}/*组件样式*/
.wk-slider {display: flex;
}
.wk-slider-item {flex-shrink: 0;
}
.wk-slider-items-wrapper {display: flex;flex-shrink: 0;transition: all 1s;
}
复制代码

这样一个简单的有缝轮播组件就初步完成了。但是这里我们忽略了一个问题:当我们的slide-item是图片的时候,由于图片的异步加载,会导致获取的宽度并不准确,我想到的解决方法是在onload事件之后再进行组件的使用:

window.onload = () => {const slider = new Slider('.slider', { autoPlay: true });
}
复制代码

小伙伴也可以自己想一些其它的解决方法,展现奇思妙想的时候到了。

无缝轮播

有缝轮播其实是无缝轮播的一个升级版本,当图片轮播到最后一项的时候,可以继续像之前一样轮播到第一项,并不会有回退效果。

大概的一个思路如下:

  • 分别复制轮播的第一项和最后一项,然后再分别插入到最后一项和第一项
  • 如果是正向轮播:轮播到最后一项时,继续轮播会进入复制的第一项,之后再闪动到真正的第一项,继续轮播
  • 如果是逆向轮播:轮播到第一项时,会继续轮播到复制的最后一项,之后闪动到真正的最后一项,继续轮播

这里我们先复习几个原生js语法:

  • Node.cloneNode(): 克隆一个节点,返回调用该方法的节点的一个副本。如果参数传入true,会克隆当前节点的所有后代节点。如果为false,则只克隆该节点本身。默认参数为false
  • parentNode.insertBefore(newNode,referenceNode): 在参考节点之前插入一个拥有指定父节点的子节点。

接下来我们一步一步来操作。

首先我们分别复制第一项和最后一项插入到对应的位置:

appendCloneNode() {const first = this.items[0];const last = this.items[this.items.length - 1];const firstClone = first.cloneNode(true);const lastClone = last.cloneNode(true);this.slider.insertBefore(lastClone, first);this.slider.appendChild(firstClone);this.items = [...this.slider.children];
}
复制代码

页面布局如下:

之后,我们要分别对最后一项到第一项以及第一项到最后一项进行处理。

// 最后一张到第一张
lastToFirst () {const { itemsWrapper, itemWidth } = this;// 将transition效果取消itemsWrapper.style.transition = 'none';// 将索引设置为1this.index = 1;// 直接移动到第二张(没有过渡效果)itemsWrapper.style.transform = `translateX(${-itemWidth * this.index}px)`;// 索引+1this.index++;itemsWrapper.offsetWidth;// 添加动画效果,通过go方法移动到第三章itemsWrapper.style.transition = 'all 1s';
}
// 第一张到最后一张(代码含义同上)
firstToLast (lastIndex) {const { itemsWrapper, itemWidth } = this;// 将transition效果取消itemsWrapper.style.transition = 'none';this.index = lastIndex - 1;itemsWrapper.style.transform = `translateX(${-itemWidth * this.index}px)`;this.index--;itemsWrapper.offsetWidth;itemsWrapper.style.transition = 'all 1s';
}
复制代码

上边的代码是轮播的一个重要思路:将最后一张/第一张“闪动”到自己复制出来的哪一项,之后恢复过渡,继续轮播

而这里特别令人疑惑的一个地方:itemsWrapper.offsetWidth。整体上来看,这一行代码和整个的逻辑并没有任何关系,但是这里涉及到有关浏览器重排、重绘的一个重要知识点:

DOM变动和样式变动,都会触发浏览器的重新渲染。但是,浏览器比较智能,会尽量把所有的变动集中在一起,排成一个队列,然后一次性执行,尽量避免多次重新渲染。

接下来我们回到上边的代码:浏览器智能的将itemsWrapper.style.transition="none"itemsWrapper.style.transition = "all 1s"进行合并后执行,而我们要做的是强制浏览器进行重新渲染,让这俩行代码分别执行,而itemsWrapper.offsetWidth就可以强制浏览器进行重新渲染。类似的属性和方法还有很多,以下是我收集的一些文章,希望对大家的知识理解有帮助:

  • What forces layout / reflow
  • Force browser to trigger reflow while changing CSS
  • 网页性能管理详解

到这里,主要的逻辑功能已经基本上完成了。接下来的工作相对来说就会很简单了,我这里不再赘述,大概的工作是这样的:

  • 添加左右箭头
  • 添加下侧点击切换的控制条
  • 在鼠标移入轮播区域的时候停止轮播,鼠标离开轮播区域时继续轮播

之后我们可以通过传入的options来让用户自己控制是否支持自动轮播,是否需要左右箭头和下侧控制条,也可以让用户自定义轮播的时间间隔。

9102,用原生js造一个轮播组件相关推荐

  1. html多图轮播淡入淡出js,原生JS实现图片轮播与淡入效果的简单实例

    最近对css的兴趣提不起来,因为以前对图片轮播一直耿耿于怀苦于学艺不精,所以,花了点时间熟悉了一下js.然后一条道走到黑,用jquery和js写了一下轮播和图片淡入的效果.以后学习的路很长,希望自己在 ...

  2. 用原生JS实现3D轮播效果

    用原生JS实现3D轮播效果 实现思路: 先实现无缝轮播效果 添加3D效果 完善代码 增加自动轮播效果 效果如下: 视图中显示3张图片,并通过CSS的透视和旋转实现3D效果,当无任何操作时,图片自动循环 ...

  3. html原生js实现图片轮播,原生JS实现图片轮播切换效果

    原生JS实现图片轮播切换效果 2019-01-06 编程之家 https://www.jb51.cc 编程之家收集整理的这篇文章主要介绍了原生JS实现图片轮播切换效果,编程之家小编觉得挺不错的,现在分 ...

  4. 原生js进阶版轮播图实现(走马灯效果,无缝衔接)

    原生js进阶版轮播图实现(走马灯效果,无缝衔接) 利用原生js手写一个轮播图,是上一篇文章的简易版的一个进阶,本次轮播图主要是利用定位和定时器实现了走马灯效果,并且是左右轮播.实现过程与代码也是很简单 ...

  5. php cms 轮播图,原生JS运动实现轮播图

    原生JS运动实现轮播图 **基本原理:**通过控制包含n张图片的ul的left值来实现图片自动运动的效果,其中列表中li元素的个数为n,第一个li和最后一个li里存放的图片应为同一张图片,当图片运动到 ...

  6. 前端原生js实现图片轮播效果,超级简单,备注详细

    原生js实现简单轮播图,效果如下 纯生js实现轮播图 链接: link. 图片: 我们可以通过左右两边的箭头来播放图片,在我们的鼠标放在图片上时,自动播放结束,转化为手动播放,可以通过小圆点来点击切换 ...

  7. html轮播图原理,30_用js实现一个轮播图效果,简单说下原理

    一.原理 将一些图片在一行中平铺,然后计算偏移量再利用定时器实现定时轮播. 步骤一:建立html基本布局 如下所示: 轮播图 1 2 3 4 5 < > 只有五张图片,却使用7张来轮播,这 ...

  8. html原生js实现图片轮播,原生js实现简单轮播图

    本文实例为大家分享了js实现简单轮播图的具体代码,供大家参考,具体内容如下 一.写入网页基本结构 body: 网页的最外部设置一个id为wrap的容器,取代body,这样方便我们做一些初始化 css: ...

  9. 单机按钮来图片轮播_原生js如何实现轮播图效果?

    第一种: 这个是之前写的,比较草率,没有注释,如果这个看不懂就去看第二个,比较仔细,主要是了解他,后面都会有一些插件来使用,很方便,只要几行代码就可写出各种各样的代码,所以,不懂的话,不要太在意, 第 ...

  10. 原生js+css 实现轮播图 完整代码

    利用原生的js实现轮播图,可以添加到自己的UI库中,在以后的项目中对其进行修改然后添加到已有项目中. 先写出css部分和html部分,直接上代码 <!DOCTYPE html> <h ...

最新文章

  1. signature=16ceadeb007b12c6b3bcab834073ab21,Distributed Backscattering
  2. Java提高篇 —— Java关键字之final的几种用法
  3. spark中saveAsTextFile如何最终生成一个文件
  4. 学习UpdatePanel控件
  5. python多线程调度_python并发编程之进程、线程、协程的调度原理(六)
  6. C#中(int),int.Parse,int.TryParse,Convert.ToInt32四则之间的用法
  7. 零基础带你学习MySQL—MySQL常用的数据类型(列类型)(五)
  8. MySQL特有的SQL语句 第一弹
  9. VMware visio制图形状大全
  10. 怎么查看个人CSDN账号积分-最靠谱!
  11. 阿铭Linux_网站维护学习笔记201903022
  12. hysys动态模拟教程_(转载)HYSYS-过程模拟软件-稳态模拟-第一部分(一)
  13. python battleship_codecademy的Python里的battleship报错
  14. vs2017 c#代码生成期间遇到了错误,值不在预期的范围内
  15. 访问学者博士后面签后的几种情况?
  16. 余数大法写了个手机虚拟号
  17. Ubuntu安装常用Linux桌面系统
  18. 02 数据定义语言DDL
  19. OpenCvSharp人脸检测(一) HaarCascade与LbpCascade人脸检测
  20. 海神祭司被机器人拉出来_美深海机器人意外殉职 水下10公里被挤碎(图)

热门文章

  1. android应用程序优化之布局优化
  2. 再见,2016,你好,2017
  3. Android支付实践(二)之微信支付详解与Demo
  4. 同步方案java_【Java基础】多线程中同步的两种解决方案
  5. css3中插入地图,CSS3 地图展开动画
  6. python元组可以修改吗_python元组元素可以修改吗
  7. broker druid 查询_即时查询工具| Druid
  8. python怎么让图片旋转45度_是否有方法将matplotlib打印旋转45度?
  9. java案例代码8--最终要随机输出一组出来做为排名
  10. Linux之如何启动tomcat服务