如何用面向對象的思維去封裝一個小型輪播圖插件

来源:https://www.cnblogs.com/yuliangbin/archive/2018/08/08/9434450.html
-Advertisement-
Play Games

1、面向對象與面向過程 既然說到面向對象的思維,那就免不了要對面向過程的編程思維與面向對象的思維做一番比較。 筆者有 一段時間天真的認為有一個類,然後new一個對象就是面向對象編程了,現在想想還是太naive了呀。 其實面向對象的編程思維和麵向過程的編程思維重要的不是對象和過程,而是思維。 比如說在 ...


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:如果這篇文章對您有一些啟發,希望您點一下推薦哦!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 嘗試著用3台虛擬機搭建了偽分散式系統,完整的搭建步驟等熟悉了整個分散式框架之後再寫,今天寫一下用python寫wordcount程式(MapReduce任務)的具體步驟。 MapReduce任務以來HDFS存儲和Yarn資源調度,所以執行MapReduce之前要先啟動HDFS和Yarn。我們都知道M ...
  • 一. 列常用操作 ① 添加新的一列test_column,並將其作為主鍵,FIRST將其放在表中第一行,auto_increement是自動增長 1 可以使用SQL語句“alter table ai3 add id0 int auto_increment primary key first;”來添加 ...
  • coalesce( expression [ ,...n ] )返回其參數中第一個非空表達式。 Select coalesce(null,null,'1','2') //結果為 1 coalesce(expression1,...n) 與此 case函數等效: CASEWHEN (expressio ...
  • Preface Yesterday I implemented a three-nodes PXC,but there were some errors when proceeding it.So I decide to research what's wrong with it.The opera ...
  • Android遠程桌面助手除了支持Android界面的顯示及控制外,還支持Android文件系統的管理,包括文件的快速上傳(push)、下拉(pull)和查看(cat)。 ...
  • 本文由葡萄城技術團隊於博客園原創並首發 轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。 在上篇中,我們介紹了什麼是Flexbox佈局,以及如何使用Flexbox佈局。還沒有看過的小伙伴歡迎回到文章列表點擊查看之前的文章瞭解。 那麼,當我們有了基本的佈局概念 ...
  • 首先要說明 [].slice.call() 與 Array.prototype.slice.call() 有什麼區別? []為創建數組,當[].slice的時候,自然會去找原型鏈 1. Array.prototype.slice是定義的方法,可以被重寫 [].silce是使用定義的方法 2. 自身的 ...
  • 簡介 Location 對象存儲在 Window 對象的 Location 屬性中,表示那個視窗中當前顯示的文檔的 Web 地址。通過Location對象,可以獲取URL中的各項信息,調用對象方法也可以重新載入或替換當前文檔。 在控制台輸入 可以獲取Location對象的詳細信息: 對象屬性 hre ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...