作为程序员大家在写代码时谈的最多的就是代码的拓展性、复用性。本文就以大家熟悉的轮播效果为案例,讲一讲写优质代码的思路和实践。

文章分三个步骤。第一步,实现基本功能;第二步,考虑到代码的封装性和复用性;第三步,考虑到代码的拓展性。

实现基本功能

展示HTML结构和JavaScript

    <div class="carousel"><div class="panels"><a href="#"><img src="http://cdn.jirengu.com/book.jirengu.com/img/1.jpg"></a><a href="#"><img src="http://cdn.jirengu.com/book.jirengu.com/img/2.jpg"></a><a href="#"><img src="http://cdn.jirengu.com/book.jirengu.com/img/3.jpg"></a><a href="#"><img src="http://cdn.jirengu.com/book.jirengu.com/img/4.jpg"></a></div><div class="action"><span class="pre">上一个</span><span class="next">下一个</span><div class="dots"><span class="active"></span><span></span><span></span><span></span></div></div></div>web前端开发学习Q-q-u-n: 767273102 ,分享学习的方法和需要注意的小细节,不停更新最新的教程和学习方法(详细的前端项目实战教学视频)

JavaScript

//让document.querySelector 用起来更方便
const $ = s => document.querySelector(s)
const $$ = s => document.querySelectorAll(s)const dotCt = $('.carousel .dots')
const preBtn = $('.carousel .pre')
const nextBtn = $('.carousel .next')//把类数组对象转换为数组,便于之后使用数组方法
//这里对应的是包含图片面板的数组
const panels = Array.from($$('.carousel .panels > a'))
//这里对应的是包含小圆点的数组
const dots = Array.from($$('.carousel .dots span'))//要展示第几页,就先把所有页的z-index设置为0,再把要展示的页面z-index设置为10
const showPage = pageIndex => {panels.forEach(panel => panel.style.zIndex = 0)panels[pageIndex].style.zIndex = 10
}const setDots = pageIndex => {dots.forEach(dot => dot.classList.remove('active'))dots[pageIndex].classList.add('active')
}//根据第几个小点上有active的类来判断在第几页
const getIndex = () => dots.indexOf($('.carousel .dots .active'))
const getPreIndex = () => (getIndex() - 1 + dots.length) % dots.length
const getNextIndex = () => (getIndex() + 1) % dots.lengthdotCt.onclick = e => {if(e.target.tagName !== 'SPAN') returnlet index = dots.indexOf(e.target)setDots(index)showPage(index)
}preBtn.onclick = e => {let index = getPreIndex()setDots(index)showPage(index)
}nextBtn.onclick = e => {let index = getNextIndex()setDots(index)showPage(index)
}

上面的代码使用了原生ES6语法,核心代码逻辑是:当用户点击小圆点,得到小圆点的位置(index),设置小圆点集合的样式,切换到对应页面。页面(.panels的子元素)使用绝对定位相互重叠到一起,我们通过修改z-index把需要展示的页面放到最上层。

对web前端这门技术感兴趣的小伙伴可以加入到我们的学习圈来,工作第六个年头了,与大家分享一些学习方法,实战开发需要注意的细节。767-273-102 秋裙。从零基础开始怎么样学好前端。看看前辈们是如何在编程的世界里傲然前行!不停更新最新的教程和学习方法(web前端系统学习路线,详细的前端项目实战教学视频),有想学习web前端的,或是转行,或是大学生,还有工作中想提升自己能力的,正在学习的小伙伴欢迎加入。我们会一起结伴同行前端前端前端

复用性与封装性

以上代码可以实现轮播基本功能,但做为意大利面条式的代码,并未做封装,无法给他人使用。另外也无法满足页面上有多个轮播的需求。

下面对代码做个封装。

class Carousel {constructor(root) {this.root = rootthis.panels = Array.from(root.querySelectorAll('.panels a'))this.dotCt = root.querySelector('.dots')this.dots = Array.from(root.querySelectorAll('.dots span'))this.pre = root.querySelector('.pre')this.next = root.querySelector('.next')this.bind()}get index() {return this.dots.indexOf(this.root.querySelector('.dots .active'))}get preIndex() {return (this.index - 1 + this.dots.length) % this.dots.length}get nextIndex () {return (this.index + 1) % this.dots.length}bind() {this.dotCt.onclick = e => {if(e.target.tagName !== 'SPAN') returnlet index = this.dots.indexOf(e.target)this.setDot(index)this.showPage(index) }this.pre.onclick = e => {let index = this.preIndexthis.setDot(index)this.showPage(index)  }this.next.onclick = e => {let index = this.nextIndexthis.setDot(index)this.showPage(index)  }}setDot(index) {this.dots.forEach(dot => dot.classList.remove('active'))this.dots[index].classList.add('active')}showPage(index) {this.panels.forEach(panel => panel.style.zIndex = 0)this.panels[index].style.zIndex = 10}
}new Carousel(document.querySelector('.carousel'))

代码里用了getter,便于或者index的值。这里需要注意的是,每次调用setDot后 this.index、this.preIndex、this.nextIndex均会自动发生变化,调用showPage的时候需要留意。

现在轮播可以复用了,但仍有缺憾,轮播的效果太单调。假设轮播想使用fade或者slide效果,我们可以在showPage方法内修改代码。但存在的问题是效果和轮播组件做了强绑定,假设我需要另外一个效果的轮播就得新建一个组件。比如,有这样一个需求,用户可以再切页时可以随时更改效果,用上面的代码就很难办到。

能不能实现组件和效果的解绑呢?当然可以。

代码拓展性

设计模式中的桥接模式可以实现上述的分离。直接给代码

class Carousel {constructor(root, animation) {this.animation = animation || ((to, from, onFinish) => onFinish())this.root = root...}...//showPage传递2个参数,toIndex 表示要切换到的页面(终点页面)序号,fromIndex 表示从哪个页面(起始页面)切换过来showPage(toIndex, fromIndex) {//animation函数传递3个参数,分别为终点页面dom元素,起始页面dom元素,动画执行完毕后的回调this.animation(this.panels[toIndex], this.panels[fromIndex], () => {//这里是动画执行完成后的回调          })}
}const Animation = {fade(during) {return function(to, from, onFinish) {//to表示终点页面dom元素,from表示起始页面dom元素//对这两个元素进行适当的处理即可实现平滑过渡效果...}},zoom(scale) {return function(to, from, onFinish) { /*todo...*/}}}new Carousel(document.querySelector('.carousel'), Animation.fade(300))

上述代码中,我们把动画类型作为参数传递给Carousel,在执行setPage的时候调用动画。 而动画函数本身做的事情比较简单:处理两个绝对定位并且相互重叠的DOM元素,以特定效果让一个元素消失另外一个元素出现。

动画的实现

动画可以用JS来实现(requestAnimationFrame来实现动画),也可以用CSS3来实现。相比JS实现动画,用CSS3性能更好并且代码更简单。

const Animation = (function(){const css = (node, styles) => Object.entries(styles).forEach(([key, value]) => node.style[key] = value)const reset = node => node.style = ''return {fade(during = 400) {return function(to, from, onFinish) {css(from, {opacity: 1,transition: `all ${during/1000}s`,zIndex: 10})css(to, {opacity: 0,transition: `all ${during/1000}s`,zIndex: 9})setTimeout(() => {css(from, {opacity: 0,})css(to, {opacity: 1,})              }, 100)setTimeout(() => {reset(from)reset(to)onFinish && onFinish()}, during)}},zoom(scale = 5, during = 600) {return function(to, from, onFinish) {css(from, {opacity: 1,transform: `scale(1)`,transition: `all ${during/1000}s`,zIndex: 10})css(to, {zIndex: 9})setTimeout(() => {css(from, {opacity: 0,transform: `scale(${scale})`})             }, 100)setTimeout(() => {reset(from)reset(to)onFinish && onFinish()}, during)}}}
})()

以下是最终代码,大家可以再Animation对象里增加更多特效,比如把前段时间流行的灭霸特效加进去。

class Carousel {constructor(root, animation) {this.animation = animation || ((to, from, onFinish) => onFinish())this.root = rootthis.panels = Array.from(root.querySelectorAll('.panels a'))this.dotCt = root.querySelector('.dots')this.dots = Array.from(root.querySelectorAll('.dots span'))this.pre = root.querySelector('.pre')this.next = root.querySelector('.next')this.bind()}get index() {return this.dots.indexOf(this.root.querySelector('.dots .active'))}get preIndex() {return (this.index - 1 + this.dots.length) % this.dots.length}get nextIndex () {return (this.index + 1) % this.dots.length}bind() {this.dotCt.onclick = e => {if(e.target.tagName !== 'SPAN') returnlet lastIndex = this.indexlet index = this.dots.indexOf(e.target)this.setDot(index)this.showPage(index, lastIndex) }this.pre.onclick = e => {let index = this.preIndexthis.setDot(index)this.showPage(index, this.nextIndex)  }this.next.onclick = e => {let index = this.nextIndexthis.setDot(index)this.showPage(index, this.preIndex)  }}setDot(index) {this.dots.forEach(dot => dot.classList.remove('active'))this.dots[index].classList.add('active')}showPage(toIndex, fromIndex) {//执行动画,执行完成后最终结果//如果没传递动画,直接执行结果this.animation(this.panels[toIndex], this.panels[fromIndex], () => {this.panels.forEach(panel => panel.style.zIndex = 0)this.panels[toIndex].style.zIndex = 10            })}setAnimation(animation) {this.animation = animation}
}const Animation = (function(){const css = (node, styles) => Object.entries(styles).forEach(([key, value]) => node.style[key] = value)const reset = node => node.style = ''return {fade(during) {return function(to, from, onFinish) {css(from, {opacity: 1,transition: `all ${during/1000}s`,zIndex: 10})css(to, {opacity: 0,transition: `all ${during/1000}s`,zIndex: 9})setTimeout(() => {css(from, {opacity: 0,})css(to, {opacity: 1,})              }, 100)setTimeout(() => {reset(from)reset(to)onFinish && onFinish()}, during)}},zoom(scale = 5, during = 1000) {return function(to, from, onFinish) {css(from, {opacity: 1,transform: `scale(1)`,transition: `all ${during/1000}s`,zIndex: 10})css(to, {zIndex: 9})setTimeout(() => {css(from, {opacity: 0,transform: `scale(${scale})`})             }, 100)setTimeout(() => {reset(from)reset(to)onFinish && onFinish()}, during)}}}
})()const carousel = new Carousel(document.querySelector('.carousel'), Animation.fade(300))
//new Carousel(document.querySelector('.carousel'), Animation.zoom(3, 500))document.querySelector('select').onchange = function(e) {carousel.setAnimation(Animation[this.value]())
}web前端开发学习Q-q-u-n: 767273102 ,分享学习的方法和需要注意的小细节,不停更新最新的教程和学习方法(详细的前端项目实战教学视频)

web前端入门到实战:以轮播效果为案例谈如何写优质代码相关推荐

  1. html前端页面的基本骨架是,web前端入门到实战:css实现的骨架屏方案

    web前端入门到实战:css实现的骨架屏方案 发布时间:2020-08-04 01:32:03 来源:51CTO 阅读:152 作者:前端向南 优点 简单,不需要工程,不用puppeteer生成骨架d ...

  2. 转圈加载html,web前端入门到实战:纯CSS实现加载转圈样式

    web前端入门到实战:纯CSS实现加载转圈样式 发布时间:2020-05-27 18:11:33 来源:51CTO 阅读:134 作者:前端向南 不同的项目中对于等待加载时转圈圈的样式是不同的,有的是 ...

  3. web前端入门到实战:HTML5 VideoAPI,打造自己的Web视频播放器

    本文将使用HTML5提供的VideoAPI做一个自定义的视频播放器,需要用到HTML5提供的video标签.以及HTML5提供的对JavascriptAPI的扩展. 一.基础知识 1.用法 <v ...

  4. web前端入门到实战:简单的图片轮播

    效果: 功能: 1.左右箭头切换 2.状态控制点切换 3.鼠标悬念 4.自动轮播 HTML: <div class="zh-carousel"><div clas ...

  5. web前端入门到实战:CSS动画之旋转魔方轮播

    下面我将一步一步详解如何利用纯CSS实现一个旋转魔方轮播的效果. 总的来说我们需要实现以下两个主要功能: 构建一个能够旋转的立方体 让立方体拥有基本轮播所具有的特性 但在完成以上两点之前我们需要再次了 ...

  6. web前端入门到实战:实现图形验证码

    什么是图形验证码 图形验证码是验证码的一种.验证码(CAPTCHA)是"Completely Automated Public Turing test to tell Computers a ...

  7. web前端入门到实战:HTML属性选择器(下)

    一.格式 标签[属性=值]:{属性:值:} 1.属性的取值是以什么开头的 attribute |= value(CSS2)attribute^=value(CSS3)两者之间的区别:CSS2中只能找到 ...

  8. web前端入门到实战:CSS新属性实现特殊的图片显示效果

    1 概述 1.1 前言 使用一个或多个图像相关的CSS属性(background-blend-mode, mix-blend-mode, or filter)可以实现许多特殊的图片显示效果,共列举了2 ...

  9. html弧形列表效果,web前端入门到实战:html5网页特效-弧形菜单

    效果: 弧形菜单,文字按规则变形以及变换透明度 简单的javascript,上手难度:简单 学习笔记: text-decoration: 最主要的功能就是给文字加上附着在文字底部,上方,或者中间的线( ...

最新文章

  1. DL之Attention-ED:基于TF NMT利用带有Attention的 ED模型训练、测试(中英文平行语料库)实现将英文翻译为中文的LSTM翻译模型过程全记录
  2. mysql存储过程模糊查询_Mysql之存储过程“模糊查询drop表”
  3. 淘宝Hadoop现有测试框架探幽
  4. BZOJ4997 [Usaco2017 Feb]Why Did the Cow Cross the Road III
  5. javascript arguments 特殊 对象
  6. python 多层for循环转递归/迭代
  7. asp小偷转html,ASP “小偷”程序(抓取程序)
  8. 神药克星!读完本文,你的父母将彻底摆脱权健类神药的骗局
  9. Android Multimedia框架总结(十三)CodeC部分之OpenMAX框架初识及接口与适配层实现
  10. r语言 小树转化百分数_魅力语言小课堂|绕口令《说日》
  11. android sdk 转移_腾讯微博java(android)sdk关系链api详细介绍
  12. 2020 计蒜客蓝桥杯省赛 B 组模拟赛(一)题解1.有趣的数字
  13. 电脑COM串口管理芯片75232、75185及电路(两者可代换)
  14. 2019-2021 文本生成图片 Text To Image(T2I) Synthesis 论文整理
  15. es routing 简介
  16. MATLAB系列笔记:三维绘图(一)
  17. 网站关键词SEO排名,SEO长尾关键词排名工具
  18. Thinkphp框架的源码通读1
  19. B站 URL转16进制防止评论贴URL被屏蔽
  20. 区间再现公式的理解与应用

热门文章

  1. vscode在控制台出现“无法初始化设备 PRN“情况的解决方法
  2. 标准差分进化算法(DE)
  3. 用Python写一个随机数字生成代码,5行代码超简单
  4. 机器学习(九) K-Means(K-均值)聚类算法介绍
  5. 无界零售在618的牛刀小试,可能会让其成为新零售的新方向
  6. chrome设置http自动跳转https
  7. 福建C语言省考成绩什么出,福建省公务员考试成绩何时出来
  8. Firebird的备份工具(nbackup)介绍
  9. crf的Python实现代码
  10. 周亚军 红宝书 案例 3 SSH远程管理协议