a标签active不起效果_以轮播效果为案例谈如何写优质代码
发布日期:2021-10-31 12:52:01 浏览次数:6 分类:技术文章

本文共 9476 字,大约阅读时间需要 31 分钟。

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

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

实现基本功能

完整代码查看这里 ,下面只展示HTML结构和JavaScript

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设置为10const 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.lengthconst getNextIndex = () => (getIndex() + 1) % dots.lengthdotCt.onclick = e => {
if(e.target.tagName !== 'SPAN') return let 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把需要展示的页面放到最上层。

复用性与封装性

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

下面对代码做个封装。

class Carousel {
constructor(root) {
this.root = root this.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') return let index = this.dots.indexOf(e.target) this.setDot(index) this.showPage(index) } this.pre.onclick = e => {
let index = this.preIndex this.setDot(index) this.showPage(index) } this.next.onclick = e => {
let index = this.nextIndex this.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 = root this.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') return let lastIndex = this.index let index = this.dots.indexOf(e.target) this.setDot(index) this.showPage(index, lastIndex) } this.pre.onclick = e => {
let index = this.preIndex this.setDot(index) this.showPage(index, this.nextIndex) } this.next.onclick = e => {
let index = this.nextIndex this.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]())}

查看效果 查看源码

完。

欢迎进群探讨技术,点此 微信扫码进群。

饥人谷新课程来了,点此查看

转载地址:https://blog.csdn.net/weixin_39965881/article/details/112127269 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:$.ajax data怎么处理_第3部分-异步使用2-1:Ajax
下一篇:directx 9.0 3d游戏开发编程基础_树——冒险游戏

发表评论

最新留言

关注你微信了!
[***.104.42.241]2024年04月15日 03时13分31秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章