如何用面向对象的思维去封装一个小型轮播图插件

1、面向对象与面向过程

既然说到面向对象的思维,那就免不了要对面向过程的编程思维与面向对象的思维做一番比较。

笔者有 一段时间天真的认为有一个类,然后new一个对象就是面向对象编程了,现在想想还是太naive了呀。

其实面向对象的编程思维和面向过程的编程思维重要的不是对象和过程,而是思维。

比如说在生活中我们要做一道西红柿炒鸡蛋,很多人的第一反应就是西红柿炒鸡蛋第一步、第二步、第三步应该怎么做,每一步放什么调料。然后在做的过程中找每一步需要的调料。这就是很典型的面向过程的思维方式:怎么做,需要什么。

而有些人的先想到的是做西红柿炒鸡蛋我需要哪些东西,调料等。然后才考虑的是怎么做。所以一个面向对象的思维方式是:需要什么,怎么做。等一下,“怎么做”指的不是向过程嘛?这就验证了前面那句话:重要的不是对象和过程,而是思维。不管是哪种编程思维,最终落到敲代码的层面,都是面向过程的。

咋一看,好像两种思维方式并没有很明显的优劣势之分呀。这个笔者也曾经纠结好久,其实对于平时一些非常简单的需求(就是完成一件事所需要的步骤比较少)两者好像都差不多,甚至面向过程更甚一筹;但是对于一些复杂的需求的话,面向对象可以让我们的项目更加易于维护和扩展。(还记得面向对象的三大特性嘛:继承、封装、多态。好吧,JS的世界没有多态)

--------------------------------------------------------------- 分割线,下面正式开始。

源码获取链接请点这里

侃完了面向对象和面向过程,下面我们就来正式的讲一下:如何用面向对象的思维去封装一个小型轮播图插件。(用jQuery实现)

废话不多说,先看最终实现的效果。

2、需求分析

其实在实现一个插件时,我们想要的实现的需求就是"谁去干一件什么事"。那么在这里就是让这个container容器里面的图片实现轮播。

既然需求确定了,接下来就是实现的环节。既然是这样的话,第一件事就是把通过HTML和CSS把静态页面搭建起来。

页面建好了就到了使用JS进行动态交互的时候了。这个时候才是真正的重头戏。让我们暂时忘掉这样一个效果是怎么实现的。我们先来看一看要实现这样的效果我们需要干什么(即需要什么)。

  • 首先我们要知道整个轮播图显示范围:即整个轮播图的宽高。width和height
  • 其次我们需要居中显示的图片的宽高,这样我们才能确定后面的图片的摆放位置。posterWidth和posterHeight
  • 后面图片的大小百分比也得根据前面图片的大小来确定。scale
  • 每一张图片是顶端对齐、居中对齐还是底端对齐。verticalAlign
  • 轮播图是否需要自动轮播呀,如果需要,那么轮播时自动切换的时间是多少。autoTime
  • 当然,我们还得设置图片的切换速度。speed

3、具体实现

好了,现在大致的需求已经确定了,我们先来定一个默认的需求。

setting = {
    "width": 1000,
    "height": 270,
    "posterWidth": 640,//这里我们没有设置居中图片的高度,因为一般情况下,居中图片的高度与容器的高度相同
    "scale": 0.9,
    "speed": 500,
    "autoPlay": false,
    "autoTime": 2000,
    "verticalAlign": "middle",
}

既然是面向对象的方式实现,我们首先需要一个Carousel函数对象。这个对象的作用是:让我们的容器里的图片根据我们传进去的配置实现动画效果。很显然,这个对象应该接收两个参数:container和config。

let Carousel = function(poster,config){//这里的poster相当于container
    let _this = this;
    this.poster = poster;
    this.config = config;
    //获取容器内要操作的DOM元素
    this.posterItemMain = poster.find(‘ul.poster-list‘);
    this.nextBtn = poster.find(‘div.next-btn‘);
    this.prevBtn = poster.find(‘div.prev-btn‘);
    this.posterItems = this.posterItemMain.find(‘.poster-item‘);
    this.length = this.posterItems.length;
    //第一张幻图片(居中图片)
    this.posterFirstItem = this.posterItems.first();
    this.posterLastItem = this.posterItems.last();
    //配置默认参数
    this.setting = {
        "width": 1000,
        "height": 270,
        "posterWidth": 640,
        "scale": 0.9,
        "speed": 500,
        "autoPlay": false,
        "autoTime": 2000,
        "verticalAlign": "middle",
    }
}

获得要操作的元素之后,我们还得把图片设置在正确的位置。

let Carousel = function(poster,config){
    this.getSetting();//匹配真实配置参数
    this.setSettingValue();//设置容器和居中图片宽高
    this.setPosterPos();//设置后面图片的位置关系
}

Carousel.prototype = {

    setPosterPos: function() {
        let sliceItems = this.posterItems.slice(1);
        let sliceRight = sliceItems.slice(0,(sliceItems.length)/2);
        let sliceLeft = sliceItems.slice((sliceItems.length)/2);
        //设置zindex层级
        let level = Math.floor(this.length / 2);
        //左右间隙宽度
        let gapWidth = (this.setting.width - this.setting.posterWidth) / 2 / level;

        let rw = this.setting.posterWidth,
            rh = this.setting.height,
            _this = this,
            firstLeft = (this.setting.width - this.setting.posterWidth) / 2,//第一帧距左端的距离
            fixLeft = firstLeft + rw;//第一帧宽加上距左端距离

        //设置右边帧的位置关系,如高度、宽度等
        sliceRight.each(function(index,item){
            rw = rw * _this.setting.scale;
            rh = rh * _this.setting.scale;
            //console.log(fixLeft );
            //获取到的每一个元素是DOM对象,先转换为jQuery对象
            $(item).css({
                width:rw,
                height:rh,
                zIndex: level,
                top: _this.setVerticalAlign(rh),
                left:fixLeft + (index + 1) * gapWidth - rw,
                opacity:0.5,
            });
            level--;
        })

        let lw = sliceRight.last().width(),//最左边图片的宽度
            lh = sliceRight.last().height(),//最左边图片的高度
            level1 = 0;//在Index层级

        //设置左边帧的位置关系
        sliceLeft.each(function(index,item){
            //获取到的每一个元素是DOM对象,先转换为jQuery对象
            $(item).css({
                width:lw,
                height:lh,
                zIndex: level1,
                top: _this.setVerticalAlign(lh),
                left: index * gapWidth,
                opacity:0.5,
            });
            lw = lw / _this.setting.scale;
            lh = lh / _this.setting.scale;
            level1++;
        })

    },

    setSettingValue: function() {
        this.poster.css({
            width: this.setting.width,
            height: this.setting.height,
        });
        this.posterItemMain.css({
            width: this.setting.width,
            height: this.setting.height,
        });

        //计算上下切换按钮的宽度
        let w = (this.setting.width - this.setting.posterWidth) / 2;
        this.prevBtn.css({
            width: w,
            height: this.setting.height,
            zIndex: Math.ceil(this.length / 2),
        }) ;
        this.nextBtn.css({
            width: w,
            height: this.setting.height,
            zIndex: Math.ceil(this.length / 2),
        })
        //console.log(1);
        //设置第一张幻灯片的位置
        this.posterFirstItem.css({
            left: w,
            height: this.setting.height,
            width: this.setting.posterWidth,
            zIndex: Math.ceil(this.length / 2),
        })
    },

    getSetting: function() {
        this.setting = $.extend(this.setting,this.config);
    },
}

图片的位置都设置好了以后,接下来就是真正的动态交互的逻辑的设置了。

let Carousel = function(poster,config){
    this.nextBtn.click(function() {//为按钮绑定事件
         _this.carouseRotate(‘right‘);
    });

    this.prevBtn.click(function() {
         _this.carouseRotate(‘left‘);
    });

    if (this.setting.autoPlay){//判断是否自动轮播
        let _this = this;
        this.autoPlay();
        this.poster.hover(function() {
            window.clearInterval(_this.timer);
        },function(){
            _this.autoPlay();
        })
    }
}

Carousel.prototype = {
    //自动播放
    autoPlay: function() {
        let _this = this;
        this.timer = window.setInterval(function() {
            _this.nextBtn.click()
        },_this.setting.autoTime)
    },
    //轮播
    carouseRotate:function(dir) {
    let that = this;
    let zIndexArr = [];//存储zIndex的值
    if (dir == "left"){
        let arr = [],
        zIndexArr = [];
    this.posterItems.each(function(item,index) {//遍历图片,将图片的位置等信息存放在一个数组里
        let _this = $(this),
            prev = _this.prev().get(0) ? _this.prev() : that.posterLastItem,
            width = prev.width(),
            height = prev.height(),
            zIndex = prev.css(‘zIndex‘),
            opacity = prev.css(‘opacity‘),
            left = prev.css(‘left‘),
            top = prev.css(‘top‘);
        arr.push({
            width: width,
            height: height,
            left: left,
            opacity: opacity,
            top: top,
        })
        zIndexArr.push(zIndex);
    })
    this.posterItems.each(function(index) {
        let _this = $(this);
        _this.css(‘zIndex‘,zIndexArr[index]);
    })
    this.posterItems.each(function(index,item) {//实现轮播效果
        let _this = $(this);
        //console.log(this);//each循环里的this指向当前元素
        _this.animate(arr[index],that.setting.speed,function() {
            that.flag = true;
        })
    })
    }else {
       //这里省略了右边按钮的轮播代码
       //但整体思路和左边按钮的轮播代码类似
    }   

},
    setVerticalAlign: function(height) {
        if (this.setting.verticalAlign == ‘top‘) {
            return 0;
        }
        if (this.setting.verticalAlign == ‘middle‘ || this.setting.verticalAlign == undefined) {
            return (this.setting.height - height) / 2;
        }

        if (this.setting.verticalAlign == ‘bottom‘){
            return (this.setting.height - height);
        }
    },

}

至此,一个面向对象的轮播图组件已经做好了。

3.1、一个小bug

但是还存在着一个小bug。那就是:当多次快速点击按钮的时候,轮播图的切换会变得很乱。

这主要是因为当前一个轮播图切换还在切换过程中的时候,这时你再点击,轮播图会在当前位置再次进行切换,从而造成轮播图的混乱。为此,我们希望当点击后,轮播图的切换还未完成之前,无论点击多少次按钮都不再触发轮播。

这个很像多线程中的多个线程同时操作一个公共资源的问题,为此,我们可以给需要操作的公共资源(这里指轮播图切换函数)添加一个锁,当点击按钮时,给该资源上锁,当轮播图切换完成后,再给这个资源解锁。

let Carousel = function(poster,config){
    this.flag = true;//轮播图锁标识
    this.nextBtn.click(function() {//为按钮绑定事件
        if (_this.flag) {
            _this.flag = false;//给轮播图函数上锁
            _this.carouseRotate(‘right‘);
        }
    });

    this.prevBtn.click(function() {
        if (_this.flag) {
            _this.flag = false;
            _this.carouseRotate(‘left‘);
        }
    });
}

//做了一点点小改动
carouseRotate:function(dir) {
    let that = this;
    let zIndexArr = [];//存储zIndex的值
    if (dir == "left"){
        let arr = [],
        zIndexArr = [];
    this.posterItems.each(function(item,index) {
        let _this = $(this),
            prev = _this.prev().get(0) ? _this.prev() : that.posterLastItem,
            width = prev.width(),
            height = prev.height(),
            zIndex = prev.css(‘zIndex‘),
            opacity = prev.css(‘opacity‘),
            left = prev.css(‘left‘),
            top = prev.css(‘top‘);
        arr.push({
            width: width,
            height: height,
            left: left,
            opacity: opacity,
            top: top,
        })
        zIndexArr.push(zIndex);
    })
    this.posterItems.each(function(index) {
        let _this = $(this);
        _this.css(‘zIndex‘,zIndexArr[index]);
    })
    this.posterItems.each(function(index,item) {
        let _this = $(this);
        _this.animate(arr[index],that.setting.speed,function() {
            that.flag = true;//加了一个回调函数
        })
    })
    }else {
      //省略右轮播代码
    }
},

3.2、注册为jQuery方法

下面,我们将其作为一个扩展功能注册为jQuery方法。

$.fn.extend({//注册为jQuery方法
    carousel: function(config) {
        new Carousel($(this),config)
    }
})

好了,大功告成。

PS:如果这篇文章对您有一些启发,希望您点一下推荐哦!

原文地址:https://www.cnblogs.com/yuliangbin/p/9434450.html

时间: 2024-11-03 21:00:08

如何用面向对象的思维去封装一个小型轮播图插件的相关文章

封装一个简单的原生js焦点轮播图插件

轮播图实现的效果为,鼠标移入左右箭头会出现,可以点击切换图片,下面的小圆点会跟随,可以循环播放.本篇文章的主要目的是分享封装插件的思路. 轮播图的我一开始是写成非插件形式实现的效果,后来才改成了封装成插件的形式. 首先要明白轮播图的实现原理和基本布局,大概就是外面有一个容器包裹着(通常是div),容器设置宽高,以及overflow为hidden,超出宽高部分隐藏, 容器里面又包含着另一个容器,包裹着所有的图片,宽为所有图片的总宽度,ul的position为absolute,通过改变ul的left

基于ionic框架封装一个图片轮播指令的几点

在这里我想在项目中封装一个图片轮播的指令 (本项目使用的是ionic框架) 1)定义指令 define(['app'],function(myapp){ myapp.directive('myslidebanner',['$state',function(s){ return{ templateUrl:'directives/slide-banner/slide-banner.html', scope:{ banimg:'=',//数据的来源 }, link:function(s,el,atr)

一个用原生JS造的轮播图插件

a native-js slider 一个无缝轮播图的小轮子 ( ?° ?? ?°)?? 前言 自己弄这个轮子是来自之前vue做一个音乐播放器项目时,用到了一个第三方vue组件better-scroll,当时参考这个文档做轮播图的时候,发现slider-item真实渲染出来的多了两个节点,向作者提问了下,回答当传入 snap:{loop:true} 的时候,前后各 clone 一个节点,保证可以无缝循环轮播,那么多克隆这两个节点是怎么实现无无缝轮播图呢,查阅了相关原理,就做了下这个轮子. 在线演

js-BOM之offset家族、移动函数的封装升级(轮播图)

Obj.style.width/obj.style.height与obj.offsetWidth/obj.offsetHeight的区别: <style> #div1{ height: 200px; background-color: red; } #div2{ width: 200px; height: 200px; background-color: green; } </style> </head> <body> <div style="

通过一个轮播图插件来了解构造函数

例子:https://github.com/wayaha/rotateChart 在ES5中,构造函数的使用可以说是很能体现面向对象的编程思想,有学过c的同学,可以很明显体会到面向过程和面向对象的区别,不多瞎扯,这次通过一个轮播图的插件来理解一下构造函数: 1.关于对象的创建方式 常见的创建对象方式有:字面量和通过new + 构造函数的方式: 字面量的方式相对灵活.简单:缺点也很明显,用一个需要造一个,不便于复用和属性的继承如下: var person = { name: 'Picker', a

一个焦点轮播图

*{ padding:0; margin:0; } .box { width:600px; height:375px; overflow:hidden; position:relative; left:50%; top:60px; margin-left:-300px; } .box:hover { cursor:pointer; } img { width:100%; height:100%; } img.active { position:absolute; top:0; left:0; }

使用vue的v-show和transition制作一个简单轮播图

<template> <!--轮播图--> <div class="carousel-wrap" id="carousel"> <transition-group tag="ul" class='slide-ul' :name="transitionName"> <li v-for="(list,index) in slideList" :key=&qu

原生JS面向对象思想封装轮播图组件

原生JS面向对象思想封装轮播图组件 在前端页面开发过程中,页面中的轮播图特效很常见,因此我就想封装一个自己的原生JS的轮播图组件.有了这个需求就开始着手准备了,代码当然是以简洁为目标,轮播图的各个功能实现都分别分为不同的模块.目前我封装的这个版本还不适配移动端,只适配PC端. 主要的功能有:自动轮播,点击某一张图片对应的小圆点就跳转到指定图片,有前后切换按钮.使用的时候只需要传入图片的路径以及每张图片分别所对应的跳转路径还有目标盒子ID就可以了,还可以自定义每张图轮播的延时,不过延时参数不是必须

08第二种定时器_封装动画函数_轮播图_offset系列

前面复习: 下面会说第二种定时器. 第二种定时器: 第一种的定时器回顾: 另一个定时器 setTimeout() 它是一个一次性的定时器: 因为,代码是从上往下执行的,btn 还没有生成,所以getElementById("btn").onclick = 肯定是会报错的. 它是一次性的定时器,如果没有取消的话,它会一直占着空间,所以一般都要写按钮btn 去取消timeId  . 1 <!DOCTYPE> 2 <html lang="en">