整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          帶你手寫一個(gè)輪播圖之JavaScript邏輯實(shí)現(xiàn)

          帶你手寫一個(gè)輪播圖之JavaScript邏輯實(shí)現(xiàn)

          了控制篇幅,本打算分3篇文章來講述輪播圖實(shí)現(xiàn)的,后來想了想,JS代碼量其實(shí)很少,完全沒必要,所以這篇文章就是收尾。

          昨天,我們完成了結(jié)構(gòu)和布局設(shè)計(jì),今天我們來完成JavaScript邏輯部分。我這人比較有代碼潔癖,不喜歡過多的變量污染全局作用域,習(xí)慣使用自執(zhí)行的匿名函數(shù)包裹一個(gè)功能塊。我們所有的JS代碼都在如下函數(shù)內(nèi)部編寫:

          (function () {
            // ... ... 我們的代碼都寫到這兒
          })();

          我們的程序中需要用到多個(gè)DOM元素,所以我們先拿到它們的引用存儲(chǔ)到變量中。

          /** @type {HTMLDivElement} 輪播圖容器 */
            const carouselWrapper=document.querySelector('.carousel-wrapper')
            /** @type {HTMLUListElement} banner列表元素 */
            const slides=carouselWrapper.querySelector('.slides')
            /** @type {HTMLDivElement} 指示器 */
            const indicator=carouselWrapper.querySelector('.indicators')
            /** @type {NodeListOf<HTMLLIElement>} 指示器節(jié)點(diǎn)列表 */
            const indicatorItems=indicator.querySelectorAll('.indicator-item')
            /** @type {HTMLUListElement} 導(dǎo)航按鈕列表元素 */
            const nav=carouselWrapper.querySelector('.nav')

          接下來,我們定義一些常量和變量,建議大家定義常量名使用全大寫字母下劃線分割。

           /** 活動(dòng)類名 */
            const ACTIVE_CLS_NAME='active'
            /** 左導(dǎo)航按鈕類名 */
            const CLS_NAME_NAV_LEFT='nav-left'
            /** 右導(dǎo)航按鈕類名 */
            const CLS_NAME_NAV_RIGHT='nav-right'
            /** 取消過渡的類名 */
            const CLS_NAME_NO_TRANSITION='no-transition'
            /** 定時(shí)器間隔 */
            const TIMER_DELAY=3000
          
            /** 定時(shí)器id */
            let timerId
            /** 當(dāng)前指示器索引 */
            let currentIndex=0
            /** 當(dāng)前banner索引 */
            let currentslideItemIndex=0
            /** 過渡結(jié)束標(biāo)識(shí) */
            let transitionendFlag=true

          要做到左右滾動(dòng)的無(wú)縫切換,我們必須至少拷貝banner列表中的第一和最后一個(gè)元素,將其追加到banner列表尾部,并更新banner列表的寬度。為了節(jié)省代碼,我完全拷貝了一份banner列表的子元素。我們用一個(gè)函數(shù)來做這件事,代碼如下:

          function updateSlides() {
              slides.innerHTML +=slides.innerHTML
              slides.style.width=`${slides.childElementCount * 100}%`
            }

          當(dāng)用戶單擊指示器項(xiàng)的時(shí)候,我們需要切換當(dāng)前顯示的banner圖。首先需要確定被單擊指示器項(xiàng)的索引。我們用一個(gè)函數(shù)來實(shí)現(xiàn),代碼如下:

          function getTargetIndex(el) {
              for (let i=0, len=indicatorItems.length; i < len; i++) {
                if (indicatorItems[i]===el) {
                  return i
                }
              }
              return -1
            }

          索引確定了,我們需要根據(jù)索引來更新X軸的平移值。當(dāng)然,要實(shí)現(xiàn)左右滾動(dòng),我們還有其它實(shí)現(xiàn)方案,例如:改變可滾動(dòng)元素的滾動(dòng)條位置,改變?cè)氐淖笥叶ㄎ坏取D敲?,為什么使?D平移變換呢?原因之一就是,瀏覽器會(huì)對(duì)3D平移變換開啟GPU硬件加速渲染,以獲得更好的性能,直觀感受就是動(dòng)畫非常絲滑,毫無(wú)拖泥帶水之感。我們用一個(gè)函數(shù)來更新X軸平移,代碼如下:

          function updateTranslateX(index, targetIndex=-1) {
              // 如果過渡沒有結(jié)束,直接返回
              if (!transitionendFlag) return
              // 當(dāng)前指示器移除活動(dòng)類
              indicatorItems[currentIndex].classList.remove(ACTIVE_CLS_NAME)
              // 如果沒有提供目標(biāo)索引,就把index賦給它
              if (targetIndex < 0) {
                targetIndex=index
              }
              // 目標(biāo)指示器添加活動(dòng)類
              indicatorItems[targetIndex].classList.add(ACTIVE_CLS_NAME)
              // 將當(dāng)前索引設(shè)為目標(biāo)索引
              currentIndex=targetIndex
              // 將當(dāng)前banner索引設(shè)為index
              currentslideItemIndex=index
              // 改變banner列表元素的X軸平移變換
              slides.style.transform=`translateX(-${index / slides.childElementCount * 100}%)`
            }

          以下是指示器單擊處理函數(shù)的實(shí)現(xiàn)。這個(gè)代碼很簡(jiǎn)單,首先獲取目標(biāo)指示器項(xiàng)的索引,如果找到了,就根據(jù)該索引更新banner列表的X軸平移。

          /** @param {MouseEvent} e */
            function indicatorClickHandler(e) {
              const index=getTargetIndex(e.target)
              index >=0 && updateTranslateX(index)
            }

          接下來,我們實(shí)現(xiàn)左右導(dǎo)航按鈕的單擊。首先,我們需要一個(gè)向左和向右滾動(dòng)的函數(shù),代碼如下:

          function toNext() {
              // 指示器項(xiàng)的數(shù)量
              const len=indicatorItems.length
              if (currentIndex < len) {
                if (currentIndex===len - 1) {
                  // 繼續(xù)滾動(dòng)到下一個(gè)索引位置,并將目標(biāo)索引設(shè)為0
                  updateTranslateX(currentIndex + 1, 0)
                } else {
                  updateTranslateX(currentIndex + 1)
                }
              } else {
                updateTranslateX(0)
              }
            }
          
            function toPrev() {
              const len=indicatorItems.length
              if (currentIndex < 1) {
                // 去掉過渡
                slides.classList.add(CLS_NAME_NO_TRANSITION)
                // 讓索引為len的banner圖可見
                updateTranslateX(len, len - 1)
                setTimeout(()=> {
                  // 添加過渡
                  slides.classList.remove(CLS_NAME_NO_TRANSITION)
                  // 讓索引為len-1的banner圖可見,此時(shí)有過渡
                  updateTranslateX(len - 1)
                }, 0)
              } else {
                updateTranslateX(currentIndex - 1)
              }
            }

          以下是左右導(dǎo)航按鈕單擊事件處理函數(shù)的實(shí)現(xiàn)。我們根據(jù)是否包含指定的類名,來判斷單擊的哪個(gè)按鈕,進(jìn)而執(zhí)行不同的導(dǎo)航函數(shù)。

          /** @param {MouseEvent} e */
            function navClickHandler(e) {
              /** @type {{target: HTMLElement}} */
              const { target }=e
              if (target.classList.contains(CLS_NAME_NAV_RIGHT)) {
                toNext()
              } else if (target.classList.contains(CLS_NAME_NAV_LEFT)) {
                toPrev()
              }
            }

          我們還需要監(jiān)聽banner列表的過渡開始和結(jié)束事件。過渡開始事件處理函數(shù)非常簡(jiǎn)單,只是將過渡結(jié)束標(biāo)志設(shè)為了false,代碼如下:

          function slidesTransitionstartHandler(e) {
              transitionendFlag=false
            }

          下面是過渡結(jié)束事件處理函數(shù)定義,其實(shí)也很簡(jiǎn)單。

            function slidesTransitionendHandler(e) {
              transitionendFlag=true
              if (currentslideItemIndex >=indicatorItems.length) {
                // 取消過渡
                slides.classList.add(CLS_NAME_NO_TRANSITION)
                updateTranslateX(0)
                setTimeout(()=> {
                  // 再添加回過渡
                  slides.classList.remove(CLS_NAME_NO_TRANSITION)
                }, 0)
              }
            }

          我們還需要讓輪播圖可以間隔一定時(shí)間自動(dòng)切換。所以,我們需要實(shí)現(xiàn)開始和結(jié)束定時(shí)器的函數(shù)。這兩個(gè)函數(shù)非常簡(jiǎn)單,代碼如下:

          function startTimer() {
              timerId=setInterval(toNext, TIMER_DELAY)
            }
          
            function stopTimer() {
              clearInterval(timerId)
              timerId=null
            }

          現(xiàn)在,是時(shí)候讓我們的輪播圖動(dòng)起來了。我們首先調(diào)用更新banner列表函數(shù),然后開始定時(shí)器,最后注冊(cè)事件監(jiān)聽器。

            updateSlides()
            startTimer()
            // 指針進(jìn)入容器的時(shí)候停止定時(shí)器
            carouselWrapper.addEventListener('mouseenter', stopTimer)
            // 指針離開容器的時(shí)候開始定時(shí)器
            carouselWrapper.addEventListener('mouseleave', startTimer)
            nav.addEventListener('click', navClickHandler)
            indicator.addEventListener("click", indicatorClickHandler)
            slides.addEventListener('transitionstart', slidesTransitionstartHandler)
            slides.addEventListener('transitionend', slidesTransitionendHandler)

          最終實(shí)現(xiàn)的效果圖如下:

          童鞋們都學(xué)廢了嗎?是不是很簡(jiǎn)單?感謝閱讀!

          先讓我們回顧一下系列內(nèi)容。

          從零開始學(xué)Bootstrap(1)介紹了BootStrap最簡(jiǎn)單的模板,逐條解釋了每行代碼的含義。

          從零開始學(xué)Bootstrap(2)強(qiáng)調(diào)了邊學(xué)邊做,通過實(shí)際的例子,講解了如何一步一步的實(shí)現(xiàn)自己想要的效果。

          寫到這里,這篇從零開始學(xué)Bootstrap(3)我想寫以下幾個(gè)內(nèi)容:

          1. 基于我對(duì)Bootstrap的理解,做一個(gè)小小的總結(jié)。

          2. 對(duì)從零開始學(xué)Bootstrap(2)例子進(jìn)行UI美化和代碼優(yōu)化,主要是說說我是怎么做出自己想要的效果的。

          3. 授人以魚不如授人以漁,以自己的例子(因?yàn)樽约阂彩切率郑瑢懗鰜淼臇|西可能更適合初學(xué)者),講講代碼編寫過程中遇到的坑和需要注意的點(diǎn)。

          廢話不多說,下面開始:

          一、 Bootstrap的小小總結(jié)

          基于以Bootstrap的官方文檔(http://v3.bootcss.com/)的說明,Bootstrap分為三個(gè)大塊:CSS樣式,組件,Javascript插件。

          首先要明確一點(diǎn):Bootstrap是一個(gè)框架,意思就是別人造好了輪子,你可以直接拿來用,免去了自己去造輪子。所以咱們需要明確兩點(diǎn):這些輪子是什么樣的輪子,怎么樣去使用這些輪子。

          1. CSS樣式:主要介紹了柵格系統(tǒng)和Bootstrap的全局樣式。通過設(shè)定Class的值實(shí)現(xiàn)。

          1.1柵格系統(tǒng):

          讓我們可以很方便的實(shí)現(xiàn)HTML頁(yè)面的布局(http://v3.bootcss.com/css/#grid)。

          我覺得柵格系統(tǒng)很重要。因?yàn)镠TML頁(yè)面的布局是很重要也很繁瑣的一項(xiàng)任務(wù)(你看一下W3School里關(guān)于布局的介紹http://www.w3school.com.cn/html/html_layout.asp,看一下例子里的代碼,就明白了),并且需要考慮到不同瀏覽器、不同設(shè)備的兼容性。

          柵格系統(tǒng)把這一切大大簡(jiǎn)化了。打開上面關(guān)于柵格系統(tǒng)的連接,你會(huì)發(fā)現(xiàn)只需要根據(jù)你想要實(shí)現(xiàn)的效果,給HTML元素Class屬性賦相應(yīng)的值,就可以實(shí)現(xiàn),并且還能設(shè)置針對(duì)不同屏幕大小的設(shè)備展現(xiàn)不同的效果。

          1.2 Bootstrap全局樣式:

          也就是Bootstrap對(duì)常用HTML元素(eg: DIV、Button、 P、 Table、 Img等等)是怎樣美化的。通過給HTML元素的Class屬性賦相應(yīng)的值,就可以得到自己想要的效果。

          舉一個(gè)最簡(jiǎn)單的例子:

          如上圖所示,Bootstrap可以讓你僅僅改變Button元素的class的值就是改變按鈕的大小,而不用很麻煩的去修改css文件,或者給元素內(nèi)嵌style的值。

          2. 組件:我認(rèn)為組件就是Bootstrap把一些元素(HTML元素和Javascript代碼)組合起來,就變成了組件,并且提供了很多很好看很實(shí)用的圖標(biāo)。這些組件基本都是HTML開發(fā)過程中常用的。通過設(shè)定Class的值實(shí)現(xiàn)。(http://v3.bootcss.com/components/)

          舉一個(gè)最簡(jiǎn)單的例子:

          如上圖所示,當(dāng)我們需要實(shí)現(xiàn)導(dǎo)航功能的時(shí)候。找到Boostrap里相應(yīng)的組件,依照其給的代碼模板,根據(jù)自己的需求,賦相應(yīng)的class和ul、li值就可以了

          3. Javascript插件:我認(rèn)為Bootstrap的Javascript插件就是封裝了常用網(wǎng)頁(yè)交互功能的”輪子”。只需要設(shè)定class屬性和data屬性就可以實(shí)現(xiàn)常用的網(wǎng)頁(yè)交互功能,而不用自己寫一大堆javascript代碼。

          首先說一個(gè)小插曲,新手可能誤以為”javascript”和”java”有著很深的聯(lián)系,甚至?xí)J(rèn)為javascript是java的變種。其實(shí)并不是這樣,javascript是網(wǎng)景(Netscape)公司開發(fā)的應(yīng)用于互聯(lián)網(wǎng)的腳本語(yǔ)言,其實(shí)它最先的名字”是livescript”,后來網(wǎng)景公司與Sun公司(也就是發(fā)明Java的公司,后來被Oracle收購(gòu))達(dá)成合作,當(dāng)時(shí)Java語(yǔ)言如日中天,名氣很大,為了搭順風(fēng)車,就把livesript改名為javascript。以至于有人開玩笑:”Java”和”Javascrip”的區(qū)別就好比”雷鋒”和”雷峰塔”的區(qū)別一樣。

          言歸正傳,我們知道,Javascript是為了賦予網(wǎng)頁(yè)交互功能而存在的。所以,Bootsript上豐富的Javascript插件能夠讓你很方便的實(shí)現(xiàn)常用的網(wǎng)頁(yè)交互功能,而不用把精力放在”造輪子”上。

          如上圖所示,利用Bootstrap的輪播插件(http://www.runoob.com/bootstrap/bootstrap-carousel-plugin.html, Bootstrap的官方文檔這里沒有翻譯成中文,runoob上卻有很詳細(xì)的中文翻譯,并且可以在線修改代碼提交觀察效果,強(qiáng)烈推薦),你可以很方便的實(shí)現(xiàn)現(xiàn)在很多網(wǎng)站都采用的圖片輪播功能。這里只需要根據(jù)上述鏈接里面的教程,賦相應(yīng)的class和圖片src值就可以了,連data值都不用設(shè)。

          二、 對(duì)從零開始學(xué)Bootstrap(2)例子進(jìn)行UI美化和代碼優(yōu)化

          下圖是上期教程中實(shí)現(xiàn)的效果:

          我們可以看到有以下幾個(gè)缺點(diǎn)需要改正

          (1) 點(diǎn)擊具體同學(xué),顯示其信息之后,沒有處于被選中的狀態(tài)。(你一開始點(diǎn)擊,會(huì)處于被選中狀態(tài),但那只是button的點(diǎn)擊效果,你點(diǎn)一下空白處,被選中的狀態(tài)就消失了)

          (2) 色彩單調(diào),不太美觀。

          (3) 布局方面需要調(diào)整,信息框是呈現(xiàn)我們需要的信息的地方,應(yīng)該盡可能大,最好其顯示的時(shí)候能把不需要的框給隱藏起來。

          (4) 對(duì)于代碼方面,對(duì)從零開始學(xué)Bootstrap(2)中的Javascript代碼都寫在HTML頁(yè)面里了,并且有重復(fù)的代碼段,應(yīng)該把重復(fù)的代碼段寫成函數(shù),這樣可以減少代碼體積,再一個(gè)修改代碼的時(shí)候,我直接修改相應(yīng)的函數(shù)就可以了,而不用一個(gè)地方一個(gè)地方的找。Javascript代碼可以寫到JS文件里,實(shí)現(xiàn)HTML頁(yè)面和Javascript代碼的分離。

          技術(shù)選型(直白的說就是思考怎樣利用Bootstrap框架里已有的東西,實(shí)現(xiàn)想要的效果):

          1、首先調(diào)整布局,把信息框和同學(xué)框調(diào)整在一起,班級(jí)框顯示在最上面。

          顯然,我們只需要把信息框所在的DIV和同學(xué)框的DIV放在同一個(gè)row的DIV里,并把修改跟柵格系統(tǒng)相關(guān)的class屬性值”col-md”就可以實(shí)現(xiàn)。比如我們想讓他們2:1的比例顯示,那么信息框的class屬性應(yīng)該有col-md-8,而同學(xué)框的應(yīng)該是col-md-4了。

          值得注意的是,前端永遠(yuǎn)不可能是一步到位的開發(fā),往往最開始的代碼都不是我們想要的完美結(jié)果,需要細(xì)細(xì)調(diào)整。以下面為例,大家要學(xué)會(huì)自己搜,去嘗試,去不斷的調(diào)整(后面的調(diào)整,具體過程就不交代了):

          我們修改完代碼后,呈現(xiàn)的效果是下面這樣的:

          很明顯的,上面每行只顯示一個(gè)Class,有點(diǎn)浪費(fèi)空間。下面的兩個(gè)部分沒有對(duì)齊。

          把上面DIV組件的class屬性里的”btn-group-vertical”刪掉,把col-md-6添加到j(luò)s代碼里的tmp_button的class屬性中。另外觀察到這樣設(shè)定了之后,第一行與第二行相比有奇怪的縮進(jìn):

          毫無(wú)疑問,這種外觀、布局類的變化跟CSS有關(guān)。這時(shí)候我們可以看一下元素具體的CSS。

          以Chrome為例

          在這個(gè)元素上點(diǎn)鼠標(biāo)右鍵,選擇Inspect,即審查,你會(huì)在右邊的框里查到相應(yīng)的代碼。通過比對(duì),我們發(fā)現(xiàn)是margin-left的問題,這個(gè)屬性是bootstrap框架里默認(rèn)的,從上層元素繼承而來,有的為 -1px,有的為0px,我們只需要改成一樣就可以了,比如都改成0px:

          在js代碼里的tmp_button,修改style屬性,添加”margin-left:0px;”,有的人覺得字體居中不好看,可以添加text-align:left,設(shè)定按鈕上的文字從左邊起。

          改正后的效果:

          2、添加折疊按鈕,讓用戶可以通過這個(gè)按鈕隱藏/打開class框,點(diǎn)擊classmate顯示詳細(xì)信息的時(shí)候,自動(dòng)隱藏,以把大量的空間省給信息框來顯示。

          另外,我們可以給折疊按鈕加一個(gè)好看的圖標(biāo),參考http://v3.bootcss.com/components/#glyphicons的教程就可以輕松實(shí)現(xiàn)。

          實(shí)現(xiàn)點(diǎn)擊classmate顯示詳細(xì)信息的時(shí)候,自動(dòng)隱藏,需要修改classmate按鈕的點(diǎn)擊事件,即相應(yīng)的js代碼。

          我們查看Bootstrap折疊插件的用法http://www.runoob.com/bootstrap/bootstrap-collapse-plugin.html(這個(gè)更好,官方的這部分還沒翻譯好),查到下面的內(nèi)容:

          原來Class屬性里的cllapse和in值控制了隱藏和顯示功能,那我們只需要在按鈕的click事件js代碼里修改相應(yīng)的要進(jìn)行”顯示/隱藏”操作的HTML元素的class屬性就可以了,于是在classmate按鈕的click函數(shù)里添加以下語(yǔ)句就可以了:

          $("#collapseOne").attr("class","panel-collapse collapse");

          3、修正”點(diǎn)擊具體同學(xué),顯示其信息之后,沒有處于被選中的狀態(tài)”的bug。

          我們通過查閱文檔,http://www.runoob.com/bootstrap/bootstrap-button-plugin.html, 給button的data-toggle屬性設(shè)置為”button”,可以使其變成點(diǎn)擊后自動(dòng)呈現(xiàn)被選中的狀態(tài)。

          所以我們給classmate的button添加屬性data-toggle=”button”。

          此時(shí)又出現(xiàn)了另一個(gè)問題,我想接下去點(diǎn)擊其他classmate的時(shí)候,原來點(diǎn)的還是處于激活狀態(tài),怎么辦?

          通過查閱http://www.runoob.com/bootstrap/bootstrap-buttons.html, button的class賦值有active時(shí),才會(huì)呈現(xiàn)被選中的狀態(tài),也就是說,上面的設(shè)定,也就是bootstrap做了這么一件事情:當(dāng)data-toggle=”button”的按鈕被點(diǎn)擊時(shí),自動(dòng)把a(bǔ)ctive添加到class里,此時(shí)呈現(xiàn)被選中狀態(tài),當(dāng)再次被點(diǎn)擊時(shí),自動(dòng)把a(bǔ)ctive從class里移除,就呈現(xiàn)未被選中的狀態(tài)。

          也就是說,我們只要自己去做這個(gè)操作,比如在點(diǎn)擊classmate時(shí),我可以把所有的classmate按鈕的active都從class屬性里移除,這樣點(diǎn)擊完成后,只有我最新點(diǎn)擊的Button處于active狀態(tài)。

          因此,只需要在classmate按鈕的click函數(shù)里加入這樣的語(yǔ)句:

          $(".btn-classmate").attr("class","btn btn-default btn-classmate btn-info");

          效果如下圖:

          4、美化按鈕外觀

          找到bootstrap里CSS關(guān)于按鈕的部分:

          按照教程修改,我這里只是簡(jiǎn)單的修改了一下按鈕的顏色,大家可以按照需求自己改。效果如下圖:

          5、 HTML頁(yè)面和Javascript代碼的分離

          其實(shí)分為兩步:

          第一步:把javascript代碼放到j(luò)s文件里,并在HTML文件里鏈接上。

          第二步:把javascript里復(fù)用,或者有明確功能的代碼塊,寫進(jìn)一個(gè)函數(shù)里,直接調(diào)用函數(shù)。

          由于這兩步都比較簡(jiǎn)單,任何學(xué)過編程語(yǔ)言的應(yīng)該都會(huì)。我就不展開寫了。

          值得注意的是,在鏈接JS文件時(shí),要注意順序。

          比如Bootstrap的JS文件,里面的代碼是基于Jquery寫的,用了很多Jquery的函數(shù),所以Jquery的JS文件要在Bootstrap的JS文件之前聲明鏈接。

          同理,咱們的JS文件時(shí)基于Bootstrap,所以要在Bootstrap之后,不然代碼就不起作用了。

          最后國(guó)際慣例,貼一下相關(guān)的代碼:

          getClassmate.html:

          <!DOCTYPE html>
          <html lang="zh-CN">
            <head>
              <meta charset="utf-8">
              <meta http-equiv="X-UA-Compatible" content="IE=edge">
              <meta name="viewport" content="width=device-width, initial-scale=1">
              <title>getClassmate</title>
          
              <!-- Bootstrap -->
              <link href="css/bootstrap.min.css" rel="stylesheet">
          
              <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
              <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
              <!--[if lt IE 9]>
                <script src="http://cdn.bootcss.com/html5shiv/3.7.2/html5shiv.min.js"></script>
                <script src="http://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
              <![endif]-->
              
              <style type="text/css">
                  #btn-group-vertical-classes 
                  {
                  overflow-y:auto; 
                  overflow-x:auto; 
                  height:100px;
                  }
                  #btn-group-vertical-classmates 
                  {
                  overflow-y:auto; 
                  overflow-x:auto; 
                  height:500px;
                  }
                  button
                  {
                  text-overflow: ellipsis;
                  overflow: hidden;
                  border-radius: 0px;
                  }
                  #context_div
                  {
                  overflow-y:auto; 
                  overflow-x:auto; 
                  height:500px;
                  }
              </style>
            </head>
            <body>
            <div class="container">
                  <div class="panel panel-default">
           <div class="panel-heading">
           <h4 class="panel-title" style="text-align:right;">
           <a id="collapse_a" data-toggle="collapse" data-parent="#accordion" 
           href="#collapseOne">
           <span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span> Click this to show/hide class
           </a> 
           </h4>
           </div>
           <div id="collapseOne" class="panel-collapse collapse in">
           <div class="panel-body">
           <div class="row" id="div1-classes-classmates">
           <div class="btn-group-lg col-md-12" role="group" aria-label="..." id="btn-group-vertical-classes">
           <!-- where classes show-->
           </div>
           </div>
           </div>
           </div>
                  </div>
                  
                  <br></br>
                  <div class="row">
           <div class="btn-group-vertical btn-group-lg col-md-4 " id="btn-group-vertical-classmates" role="group" aria-label="..."> 
           <!-- where classmates show-->
           <button type="button" style="border-radius: 0px;" class="btn btn-default">Click class to show classmate.</button>
           </div>
           <div class="form-group">
           <div class="col-md-8">
           <div class="jumbotron" id="context_div">
           <p>Click classmate to show details.</p>
           </div>
           </div>
           </div>
                  </div>
              </div>
          
              <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
              <script src="js/jquery-3.0.0.min.js"></script> 
              <!-- Include all compiled plugins (below), or include individual files as needed -->
              <script src="js/bootstrap.min.js"></script>
              <script src="js/script_getClassmate.js"></script>
            </body>
          </html>

          script_getClassmate.js:

          $(document).ready(function{
                  $.getJSON("resource/classmates.json",function(result){
           $.each(result, function(i, field){
           var tmp_button=$('<button type="button" style="border-radius: 0px; text-align:left; margin-left:0px" class="btn btn-default btn-class col-md-6 btn-success" data-toggle="button" chosen_state=0></button>').text(i);
           tmp_button.attr("title",i);
           $("#btn-group-vertical-classes").append(tmp_button);
           });
           $(".btn.btn-default.btn-class").click(function{
           var tmp_chosen=Number($(this).attr("chosen_state"))^1;
           $(this).attr("chosen_state",String(tmp_chosen));
           showClassmates(result);
           $(".btn.btn-default.btn-classmate").click(function{
           $(".btn-classmate").attr("class","btn btn-default btn-classmate btn-info");
           $("#collapseOne").attr("class","panel-collapse collapse");
           var selected_classmate=$(this).text;
           showClassmateDetail(result,selected_classmate);
           });
           });
                  });
          })
          
          function showClassmates(result){
              $("#btn-group-vertical-classmates").empty;
              var chosen_list=new Array;
              
              $(".btn.btn-default.btn-class").filter(function{
                  judgeflag=false;
                  if($(this).attr("chosen_state")=="1"){
           judgeflag=true;
           chosen_list.push($(this).text);
                  }
                  return judgeflag; 
              });
              $.each(result,function(i,field){
                  var chosen_list_x;
                  for (chosen_list_x in chosen_list){
           if(chosen_list[chosen_list_x]==i){
           $.each(field,function(j,field2){
           var tmp_button=$('<button type="button" style="border-radius: 0px;" class="btn btn-default btn-classmate btn-info" data-toggle="button" chosen_state=0></button>').text(j);
           tmp_button.attr("title",j);
           $("#btn-group-vertical-classmates").append(tmp_button);
           });
           }
                  }
              });
          }
          
          function showClassmateDetail(result,selected_classmate){
              var classmate_context_item;
              $("#context_div").empty;
              $.each(result,function(i,field){
                  $.each(field,function(j,field2){
           if(j==selected_classmate){
           $.each(field2,function(k,field3){
           //alert(k);
           //alert(field3);
           classmate_context_item=k + ": " + field3;
           var tmp_p=$('<p></p>').text(classmate_context_item);
           $("#context_div").append(tmp_p);
           });
           }
                  });
              });
          }

          classmates.json:

          {
              "Class 001": {
                  "Xiao Wang": {
           "Gender": "Male",
           "Age": "18",
           "Interest": "Football",
           "Hometown": "Shanghai",
           "Chinese": "78",
           "Math": "90",
           "English": "66",
           "Physics": "81",
           "Chemistry": "88",
           "History": "69",
           "Geography": "92"
                  },
                  "Xiao Li": {
           "Gender": "Male",
           "Age": "20",
           "Interest": "Basketball",
           "Hometown": "Beijing",
           "Chinese": "98",
           "Math": "77",
           "English": "97",
           "Physics": "72",
           "Chemistry": "73",
           "History": "88",
           "Geography": "81"
                  }
              },
              "Class 002": {
                  "Xiao Cai": {
           "Gender": "Female",
           "Age": "19",
           "Interest": "Dance",
           "Hometown": "Gaoxiong",
           "Chinese": "93",
           "Math": "80",
           "English": "92",
           "Physics": "82",
           "Chemistry": "77",
           "History": "89",
           "Geography": "83"
                  }
              },
              "Class 003": {
                  "Xiao Ma": {
           "Gender": "Male",
           "Age": "18",
           "Interest": "Reading",
           "Hometown": "Taibei",
           "Chinese": "91",
           "Math": "93",
           "English": "96",
           "Physics": "97",
           "Chemistry": "100",
           "History": "94",
           "Geography": "92"
                  }
              },
              "Class 005": {
                  "Xiao Zhang": {
           "Gender": "Male",
           "Age": "20",
           "Interest": "Running",
           "Hometown": "Guangzhou",
           "Chinese": "67",
           "Math": "70",
           "English": "66",
           "Physics": "80",
           "Chemistry": 68,
           "History": "79",
           "Geography": "93"
                  }
              }
          }

          主要參考鏈接:

          [1].Bootstrap中文官方文檔

          http://v3.bootcss.com/

          [2].Runoob.com的Bootstrap教程

          http://www.runoob.com/bootstrap/bootstrap-tutorial.html

          章來自iCSS前端趣聞,https://mp.weixin.qq.com/s/Ct-QCJ02K7rhsYCy6kPiOQ

          在 CSS 規(guī)范 Scroll-linked Animations[1] 中,推出了一個(gè)劃時(shí)代的 CSS 功能。也就是 -- The @scroll-timeline[2] at-rule,直譯過來就是滾動(dòng)時(shí)間線。

          本文,就將帶大家一探究竟,從入門到學(xué)會(huì)使用 CSS @scroll-timeline。

          何為 @scroll-timeline 滾動(dòng)時(shí)間線?

          什么是 @scroll-timeline 滾動(dòng)時(shí)間線呢?

          @scroll-timeline 能夠設(shè)定一個(gè)動(dòng)畫的開始和結(jié)束由滾動(dòng)容器內(nèi)的滾動(dòng)進(jìn)度決定,而不是由時(shí)間決定。

          意思是,我們可以定義一個(gè)動(dòng)畫效果,該動(dòng)畫的開始和結(jié)束可以通過容器的滾動(dòng)來進(jìn)行控制

          示意 DEMO

          再系統(tǒng)性學(xué)習(xí)語(yǔ)法之前,我們通過一個(gè) DEMO,簡(jiǎn)單了解一下它的用法:

          我們首先實(shí)現(xiàn)一個(gè)簡(jiǎn)單的字體 F 旋轉(zhuǎn)動(dòng)畫

          <div id="g-box">F</div>
          
          #g-box {
              animation-name: rotate;
              animation-duration: 3s;
              animation-direction: alternate;
              animation-easing-function: linear;
          }
          @keyframes rotate {
              0% {
                  transform: rotate(0);
              }
              100% {
                  transform: rotate(360deg);
              }
          }
          

          正常而言,它是這樣一個(gè)簡(jiǎn)單的動(dòng)畫:

          接下來,我們把這個(gè)動(dòng)畫和 @scroll-timeline 相結(jié)合,需要把它放置到一個(gè)可滾動(dòng)的容器中:

          <div id="g-content">
              <div id="g-box">F</div>
          </div>
          
          #g-content {
              width: 300px;
              height: 170vh;
              background: #999;
          }
          #g-box {
              font-size: 150px;
              margin: 70vh auto 0;
              animation-name: rotate;
              animation-duration: 3s;
              animation-direction: alternate;
              animation-easing-function: linear;
              animation-timeline: box-rotate;
          }
          @keyframes rotate {
              0% {
                  transform: rotate(0);
              }
              100% {
                  transform: rotate(360deg);
              }
          }
          @scroll-timeline box-rotate {
              source: selector("#g-content");
          }
          

          這里,我們實(shí)現(xiàn)了一個(gè)可滾動(dòng)容器 #g-content,它的高度是 170vh,也就是可視界面高度的 1.7 倍,并且把 #g-box 容器放置在一個(gè)距離頂部 70vh 高度的地方:

          有意思的來了,我們?cè)O(shè)置的旋轉(zhuǎn)動(dòng)畫不會(huì)自動(dòng)開始,只有當(dāng)我們向下滾動(dòng)的時(shí)候,動(dòng)畫才會(huì)開始進(jìn)行,實(shí)際效果 Gif:

          CodePen Demo -- @scroll-timeline Demo[3]

          看到這里,大家應(yīng)該能夠理解 @scroll-timeline 的作用及含義了,它賦予了 CSS 能夠基于滾動(dòng)條的滾動(dòng)去控制動(dòng)畫行進(jìn)的能力! Amazing??!

          @scroll-timeline 語(yǔ)法介紹

          接下來,我們先緩一緩,簡(jiǎn)單看一看 @scroll-timeline 的語(yǔ)法。

          使用 @scroll-timeline,最核心的就是需要定義一個(gè) @scroll-timeline 規(guī)則:

          @scroll-timeline moveTimeline {
            source: selector("#g-content");
            orientation: vertical;
            scroll-offsets: 0px, 500px;
          }
          

          其中:

          • source:綁定觸發(fā)滾動(dòng)動(dòng)畫的滾動(dòng)容器
            • source: auto:綁定到 Document,也就是全局 Windows 對(duì)象
            • source: selector("id-selector"),通過 selector(),內(nèi)置一個(gè) #id 選擇器,選取一個(gè)可滾動(dòng)容器
            • source: none:不指的滾動(dòng)容器
          • orientation:設(shè)定滾動(dòng)時(shí)間線的方向
            • orientation: auto:默認(rèn)為 vertical,也就是豎直方向的滾動(dòng)
            • orientation: vertical:豎直方向的滾動(dòng)
            • orientation: horizontal:水平方向的滾動(dòng)
            • orientation: block:不太常用,使用沿塊軸的滾動(dòng)位置,符合書寫模式和方向性
            • orientation: inline:不太常用,使用沿內(nèi)聯(lián)軸的滾動(dòng)位置,符合書寫模式和方向性
          • scroll-offsets:滾動(dòng)時(shí)間線的核心,設(shè)定在滾動(dòng)的什么階段,觸發(fā)動(dòng)畫,可通過三種方式之一進(jìn)行設(shè)置:
            • scroll-offsets: none 這意味著沒有 scroll-offset 指定。
            • 由逗號(hào)分隔的[4]值列表確定。每個(gè)值都映射到animation-duration[5]。例如,如果 ananimation-duration 設(shè)置為 2s 且滾動(dòng)偏移量為 0px, 30px, 100px,則在 1s 時(shí),滾動(dòng)偏移量將為 30px。
            • 第三種確定滾動(dòng)偏移量的方法是使用元素偏移量。這意味著可以指定頁(yè)面內(nèi)的元素,其位置決定了滾動(dòng)時(shí)間線以及要使用這些元素的哪個(gè)邊緣。指定元素是使用 selector() 函數(shù)完成的,該函數(shù)接收元素的 id。邊緣由關(guān)鍵字 start 或確定 end??蛇x的閾值的 0–1 可用于表示元素滾動(dòng)中預(yù)期可見的百分比。

          scroll-offsets 的理解會(huì)比較困難,我們稍后詳述。

          在設(shè)定了一個(gè) @scroll-timeline 之后,我們只需要將它和動(dòng)畫綁定起來即可,通過 animation-timeline

          @scroll-timeline moveTimeline {
            source: selector("#g-content");
            orientation: vertical;
            scroll-offsets: 0px, 500px;
          }
          div {
              animation-name: move;
              animation-duration: 3s;
              animation-timeline: moveTimeline;
          }
          @keyframes move{
              0% {
                  transform: translate(0, 0);
              }
              100% {
                  transform: translate(100%, 0);
              }
          }
          

          使用 @scroll-timeline 實(shí)現(xiàn)滾動(dòng)進(jìn)度指示器

          之前在 不可思議的純 CSS 滾動(dòng)進(jìn)度條效果[6] 一文中,我們介紹了一種使用漸變實(shí)現(xiàn)的純 CSS 滾動(dòng)進(jìn)度指示器效果:

          該方法有些小小的瑕疵。其中一個(gè)就是當(dāng)滾動(dòng)距離太短的時(shí)候,進(jìn)度條右側(cè)會(huì)有明顯的斜邊效果。

          而有了 @scroll-timeline 之后,我們終于可以將滾動(dòng)和動(dòng)畫這兩個(gè)元素綁定起來,再實(shí)現(xiàn)滾動(dòng)進(jìn)度指示器,就已經(jīng)非常輕松了:

          <div id="g-container">
              <p>...文本內(nèi)容...</p>
          </div>
          
          #g-container {
              width: 100vw;
          }
          #g-container::before {
              content: "";
              position: fixed;
              height: 5px;
              left: 0;
              top: 0;
              right: 0;
              background: #ffc107;
              animation-name: scale;
              animation-duration: 1s;
              animation-fill-mode: forwards;
              animation-timeline: box-rotate;
              transform-origin: 0 50%;
          }
          
          @keyframes scale {
              0% {
                  transform: scaleX(0);
              }
              100% {
                  transform: scaleX(1);
              }
          }
          @scroll-timeline box-rotate {
              source: auto;
              orientation: vertical;
          }
          
          1. 我們?cè)陧?yè)面最上方,通過一個(gè)偽元素,實(shí)現(xiàn)一個(gè)占滿屏幕 100%5px 高的進(jìn)度條。正常而言是這樣:

          1. 通過設(shè)定一個(gè) transform: scaleX(0)transform: scaleX(1) 的動(dòng)畫,并且將它與 body 的滾動(dòng)相綁定,即可得到滾動(dòng)指示器,效果如下:

          完整的代碼,你可以戳這里:CodePen Demo - 使用 @scroll-timeline 實(shí)現(xiàn)滾動(dòng)進(jìn)度條[7]

          使用 scroll-offsets 精確控制動(dòng)畫觸發(fā)時(shí)機(jī)

          大家可以再看看上面的 Gif 圖,都有一個(gè)問題,就是動(dòng)畫的開始時(shí)間都是從滾動(dòng)一開始就開始了,剛好在滾動(dòng)結(jié)束時(shí)結(jié)束。那么如果我希望動(dòng)畫在滾動(dòng)的特定階段觸發(fā),那該怎么辦呢?

          這里,就需要借助 scroll-offsets,去更加精確的控制我們的動(dòng)畫。

          在滾動(dòng)過程中,我們可以將一個(gè)元素,劃分為 3 個(gè)區(qū)域:

          • 滾動(dòng)過程中,從上方視野盲區(qū),進(jìn)入視野
          • 滾動(dòng)過程中,處于視野中
          • 滾動(dòng)過程中,從視野中,進(jìn)入下方視野盲區(qū)

          在這里,我們就可以得到兩個(gè)邊界,上方邊界,下方邊界:

          而對(duì)于上下兩個(gè)邊界,又會(huì)有兩種狀態(tài)。以上邊界為例子,會(huì)有:

          • 元素剛剛開始進(jìn)入可視區(qū)
          • 元素完全進(jìn)入可視區(qū)

          對(duì)于這兩種狀態(tài),我們用 start 0start 1 表示,同理,下方的邊界也可以用 end 0end 1 表示:

          這里的 0 和 1 實(shí)際表示的是,元素滾動(dòng)中預(yù)期可見的百分比。

          有了這些狀態(tài)值,配合 scroll-offsets,我們就可以精確控制滾動(dòng)動(dòng)畫的觸發(fā)時(shí)間。

          我們?cè)O(shè)定一個(gè)從左向右并且伴隨透明度變化的動(dòng)畫,的看看下面幾種情況:

          1. 滾動(dòng)動(dòng)畫在元素從下方開始出現(xiàn)時(shí)開始,完全出現(xiàn)后截止。

          動(dòng)畫運(yùn)行范圍:end 0 --> end 1

          @keyframes move {
              0% {
                  transform: translate(-100%, 0);
                  opacity: 0;
              }
              100% {
                  transform: translate(0, 0);
                  opacity: 1;
              }
          }
          @scroll-timeline box-move {
              source: auto;
              orientation: "vertical";
              scroll-offsets: 
                  selector(#g-box) end 0, 
                  selector(#g-box) end 1;
          
              /* Legacy Descriptors Below: */
              start: selector(#g-box) end 0;
              end: selector(#g-box) end 1;
              time-range: 1s;
          }
          #g-box {
              animation-name: move;
              animation-duration: 3s;
              animation-fill-mode: both;
              animation-timeline: box-move;
          }
          

          效果如下:

          1. 滾動(dòng)動(dòng)畫在元素從下方完全出現(xiàn)時(shí)開始,在滾動(dòng)到上方即將離開屏幕后截止:

          動(dòng)畫運(yùn)行范圍:end 1 --> start 1

          // ...
          @scroll-timeline box-move {
              source: auto;
              orientation: "vertical";
              scroll-offsets: 
                  selector(#g-box) end 1, 
                  selector(#g-box) start 1;
          
              /* Legacy Descriptors Below: */
              start: selector(#g-box) end 1;
              end: selector(#g-box) start 1;
              time-range: 1s;
          }
          // ...
          

          效果如下:

          1. 滾動(dòng)動(dòng)畫在元素滾動(dòng)到上方即將離開屏幕后開始,完全離開屏幕后截止:

          動(dòng)畫運(yùn)行范圍:start 1 --> start 0

          // ...
          @scroll-timeline box-move {
              source: auto;
              orientation: "vertical";
              scroll-offsets: 
                  selector(#g-box) start 1, 
                  selector(#g-box) start 0;
          
              /* Legacy Descriptors Below: */
              start: selector(#g-box) start 1;
              end: selector(#g-box) start 0;
              time-range: 1s;
          }
          // ...
          

          效果如下:

          掌握 scroll-offsets 的用法是靈活運(yùn)用滾動(dòng)時(shí)間線的關(guān)鍵,當(dāng)然,在上面你還會(huì)看到 start: selector(#g-box) start 1end: selector(#g-box) start 0 這種寫法,這是規(guī)范歷史遺留問題,最新的規(guī)范已經(jīng)使用了 scroll-offsets 去替代 start: end: 的寫法。

          把上述 3 種情況放在一起,再比較比較:

          完整的代碼,你可以戳這里:CodePen Demo - @scroll-timeline Demo | element-based offset[8]

          使用 @scroll-timeline 實(shí)現(xiàn)各類效果

          在能夠掌握 @scroll-timeline 的各個(gè)語(yǔ)法之后,我們就可以開始使用它創(chuàng)造各種動(dòng)畫效果了。

          譬如如下的,滾動(dòng)內(nèi)容不斷劃入:

          代碼較長(zhǎng),可以戳這里,來自 bramus 的 Codepen CodePen Demo -- Fly-in Contact List (CSS @scroll-timeline version)[9]

          甚至可以結(jié)合 scroll-snap-type 制作一些全屏滾動(dòng)的大屏特效動(dòng)畫:

          要知道,這在以前,是完全不可能利用純 CSS 實(shí)現(xiàn)的。完整的代碼你可以戳這里:CodePen Demo -- CSS Scroll-Timeline Split Screen Carousel[10]

          簡(jiǎn)而言之,任何動(dòng)畫效果,如今,都可以和滾動(dòng)相結(jié)合起來,甚至乎是配合 SVG 元素也不例外,這里我還簡(jiǎn)單改造了一下之前的一個(gè) SVG 線條動(dòng)畫:

          完整的代碼你可以戳這里:CodePen Demo -- SVG Text Line Effect | Scroll Timeline[11]

          @scroll-timeline 的實(shí)驗(yàn)室特性與特性檢測(cè)

          @scroll-timeline 雖好,目前仍處于實(shí)驗(yàn)室特性時(shí)間,Chrome 從 85 版本開始支持,但是默認(rèn)是關(guān)閉的。

          開啟該特性需要:

          1. 瀏覽器 URL 框輸入 chrome://flags
          2. 開啟 #enable-experimental-web-platform-features

          特性檢測(cè)

          基于目前的兼容性問題,我們可以通過瀏覽器的特性檢測(cè) @supports 語(yǔ)法,來漸進(jìn)增強(qiáng)使用該功能。

          特性檢測(cè)的語(yǔ)法也非常簡(jiǎn)單:

          @supports (animation-timeline: works) {
              @scroll-timeline list-item-1 {
           source: selector(#list-view);
           start: selector(#list-item-1) end 0;
           end: selector(#list-item-1) end 1;
                  scroll-offsets:
                      selector(#list-item-1) end 0,
                      selector(#list-item-1) end 1
                  ;
           time-range: 1s;
              }
              // ...
          }
          

          通過 @supports (animation-timeline: works) {} 可以判斷瀏覽器是否支持 @scroll-timeline

          最后

          目前關(guān)于 @scroll-timeline 的相關(guān)介紹還非常少,但是它確是能夠改變 CSS 動(dòng)畫的一個(gè)非常大的革新。隨著兼容性的逐漸普及,未來勢(shì)必會(huì)在 CSS 中占據(jù)一席之地。

          本文到此結(jié)束,希望對(duì)你有幫助 :)

          如果還有什么疑問或者建議,可以多多交流,原創(chuàng)文章,文筆有限,才疏學(xué)淺,文中若有不正之處,萬(wàn)望告知。

          參考資料

          [1]Scroll-linked Animations: https://drafts.csswg.org/scroll-animations-1/

          [2]@scroll-timeline: https://drafts.csswg.org/scroll-animations/#at-ruledef-scroll-timeline

          [3]CodePen Demo -- @scroll-timeline Demo: https://codepen.io/Chokcoco/pen/JjOZMaQ

          [4]: https://developer.mozilla.org/en-US/docs/Web/CSS/length-percentage

          [5]animation-duration: https://developer.mozilla.org/en-US/docs/Web/CSS/animation-duration

          [6]不可思議的純 CSS 滾動(dòng)進(jìn)度條效果: https://www.cnblogs.com/coco1s/p/10244168.html

          [7]CodePen Demo - 使用 @scroll-timeline 實(shí)現(xiàn)滾動(dòng)進(jìn)度條: https://codepen.io/Chokcoco/pen/eYeKLMj

          [8]CodePen Demo - @scroll-timeline Demo | element-based offset: https://codepen.io/Chokcoco/pen/qBVyqob

          [9]CodePen Demo -- Fly-in Contact List (CSS @scroll-timeline version): https://codepen.io/bramus/pen/bGwJVzg

          [10]CodePen Demo -- CSS Scroll-Timeline Split Screen Carousel: https://codepen.io/Chokcoco/pen/QWOrPdM

          [11]CodePen Demo -- SVG Text Line Effect | Scroll Timeline: https://codepen.io/Chokcoco/pen/wvPxbRm


          主站蜘蛛池模板: 在线观看免费视频一区| 一区二区三区在线观看中文字幕| 国产精品美女一区二区三区| 中文字幕精品一区二区精品| 日韩精品一区二区三区国语自制 | 久久久久国产一区二区 | 国产剧情国产精品一区| 国产精品一区二区久久精品无码| 成人久久精品一区二区三区| 91大神在线精品视频一区| 久久免费精品一区二区| 国产一区中文字幕在线观看 | 久久精品国产一区二区三区 | 精品人妻码一区二区三区| 国产精品毛片一区二区三区| 久久久精品一区二区三区| 一区二区中文字幕| 国产在线观看一区精品| 免费一区二区三区在线视频| 精品无码国产一区二区三区麻豆| 精品无人区一区二区三区在线| 久久精品一区二区三区资源网| 国产一区二区三区久久精品| 国产一区二区电影| 无码精品人妻一区二区三区免费看| 国产美女精品一区二区三区| 日韩一区二区电影| 久久国产一区二区三区| 日韩精品区一区二区三VR| 精品一区二区三区无码免费视频| 91精品一区二区三区在线观看| 麻豆一区二区在我观看| 亚洲熟妇AV一区二区三区浪潮| 亚洲综合色一区二区三区| 国产精品美女一区二区三区| 亚洲国产精品一区二区第一页免| 国产在线精品一区免费香蕉| 99久久精品国产一区二区成人| 亚洲国产成人久久一区二区三区 | 精品一区二区三区波多野结衣| 久久精品无码一区二区无码|