首頁 web前端 js教程 Vue做出內部組件輪播切換步驟詳解

Vue做出內部組件輪播切換步驟詳解

May 02, 2018 am 10:30 AM
切換 步驟 組件

這次帶給大家Vue做出內部組件輪播切換步驟詳解,Vue做出內部組件輪播切換的注意事項有哪些,下面就是實戰案例,一起來看一下。

對於那些不需要路由的內部元件,在切換的時候希望增加一個輪播過渡的效果,效果如下:

##我們可以引入一個輪播組件,但是有個問題,通常輪播組件都會把所有的slide都渲染出來再進行切換,這樣就導致所有的資源都會觸發加載,這可能不是我們所期待的,畢竟如果slide比較多的情況需要一次性加載的圖片等資源太多了。所以我們可以手動簡單地寫一個,滿足需求即可。

現在一步一步來實作這個功能,先寫一個實作基本切換的demo.

#1. 實作切換##先用vue-cli建立一個工程鷹架,使用以下指令:

npm install -g vue-cli
vue init webpack slide-demo # 运行后router等都选择no
登入後複製

這樣就搭了一個webpack vue的工程,進入slide-demo目錄,查看src/App.vue,這個檔案是初始化工具提供的,是整個頁面的組件。還有一個src/components目錄,這個是放子組件的目錄。

在這個目錄裡面新建3個元件:task-1.vue、task-2.vue、task-3.vue,然後在App.vue裡面import進來,如下App.vue所示:

<script>
// import HelloWorld from './components/HelloWorld'
import Task1 from "./components/task-1";
import Task2 from "./components/task-2";
import Task3 from "./components/task-3";
export default {
  name: 'App',
  components: {
    Task1,
    Task2,
    Task3
  }
}
</script>
登入後複製

我們的資料格式questions是這樣的:

[{index: 1, type: 1, content: ''}, {index: 2, type: 1, content : ''},
{index: 3, type: 2, content: ''}, {index: 4, type: 3, content: ''}]


##它是一個數組,數組裡的每個元素代表每題,每題都有一個類型,如選擇題、填空題、判斷題等,分別對應上面的task-1、task-2、task-3,我們用一個currentIndex變數表示目前是在哪題,初始化為0,如下程式碼所示(加到App.vue裡面):

  data() {
    return {
      currentIndex: 0
    };
  },
  created() {
    // 请求question数据
    this.questions = [
      {index: 1, type: 1, question: ''}, /*...*/];
  },
登入後複製
透過改變currentIndex的值,從而切到一下題即下一個元件,要怎麼實現這個切換的效果呢?

可以使用Vue自訂的一個全域元件component,給合它的is屬性,達到動態改變元件的目的,如下程式碼所示:

<template>
<p id="app">
  <p class="task-container">
    <component :is="&#39;task-&#39; + questions[currentIndex].type" 
    </component>
  </p>
</p>
</template>
登入後複製
當currentIndex增加時,就會改變:is裡面的值,依序從task-1變成task-2、task-3等,這樣component就會換成對應的task元件。

接著,再加入一個切換到下一題的按鈕,在這個按鈕的回應函數裡面改變currentIndex的值。同時把question的資料傳給component:

<template>
<p id="app">
  <p class="task-container">
    <component :is="&#39;task-&#39; + questions[currentIndex].type" 
      :question="questions[currentIndex]"></component>
    <button class="next-question" @click="nextQuestion">下一题</button>
  </p>
</p>
</template>
登入後複製
回應函數nextQuestion實作如下:

methods: {
  nextQuestion() {
    this.currentIndex = (this.currentIndex + 1) 
        % this.questions.length;
  }
},
登入後複製
具體每個task的實作參考如task-1.vue範例:

<template>
<section>
  <h2>{{question.index}}. 选择题</h2>
  <p>{{content}}</p>
</section>
</template>
<script>
export default {
  props: ["question"]
}
</script>
登入後複製
最後的效果如下(加上題目內容):

2. 新增輪播切換效果

輪播切換通常是把所有的slide都拼起來,拼成一張長長的橫圖,然後改變這個橫圖在顯示容器裡面的位置,如老牌jQuery插件flipsnap.js,它是把所有的slide都float: left,形成一張長圖,然後改變這張長圖的translate值

,達到切換的目的。這個插件的缺點是沒有辦法從最後一張切回第一張,解決這個問題的方法之一是不斷地移動DOM:每次切的時候都把第一張移到最後一張的後面,這樣就實現了最後一張點下一張的時候回到第一張的目的,但是這樣移來移去地對性能消耗比較大,不是很優雅。另外一個輪播插件jssor slider,它也是把所有的slide都渲染出來,然後每次切換的時候都

動態地計算每張slide的translate的值 ,而不是整體長圖的位置,這樣就不用移動DOM節點,相對較優雅。還有很多Vue的輪播插件的實作也是類似上面提到的方式。

不管怎么样,上面的轮播模式都不太适用于我们的场景,其中一个是这种答题的场景不需要切回上一题,每道题做完就不能回去了,更重要的一个是我们 不希望一次性把所有的slide都渲染出来 ,这样会导致每张幻灯片里的资源都触发加载,就比如img标签虽然你把它display: none了,但是只要它的src是一个正常的url,它就会请求加载。 由于slide往往会比较多,就不使用这种轮播插件了。

还可以使用Vue自带的transition,但是transition的问题是,切下一个的时候,上一个不见了,因为被销毁了,只有下一个的动画,并且不能预加载下一个slide的资源。

所以我们手动实现一个。

我的想法是每次都准备两个slide,第1个slide是当前展示用的,第2个slide拼在它的后面,准备切过来,当第2个slide切过来之后,删掉第1个slide,然后在第2个的后面再接第3个slide,不断地重复这个过程。如果我们没有使用Vue,而是自己增删DOM,那么没什么问题,可以很任性地自己发挥。 使用Vue可以怎么优雅地实现这个功能呢

在上面一个component的基础上,再添加一个component,刚开始第1个component是当前展示的,而第2个component是拼在它右边的,当第2个切过去之后,就把第1个移到第2的后面,同时把内容改成第3个slide的内容,依此类推。使用Vue不太好动态地改DOM,但是可以 借助jssor slider的思想 ,不移动DOM,只是改变component的translate的值。

给其中一个component套一个next-task的类,具有这个类的组件就表示它是下一张要出现的,它需要translateX(100%),如下代码所示:

<template>
<p id="app">
  <p class="task-container">
    <component :is="&#39;task-&#39; + questions[currentIndex].type" 
      ></component>
    <component :is="&#39;task-&#39; + questions[currentIndex + 1].type" 
      class="next-task"></component>
  </p>
</p>
</template>
 
<style>
.next-task {
  display: none;
  transform: translateX(100%);
  /* 添加一个动画,当改变transform的值时就会触发这个动画 */
  transition: transform 0.5s ease;
}
</style>
登入後複製

上面代码把具有.next-task类的component隐藏了,这样是做个优化,因为display: none的元素只会构建DOM,不会进行layout和render渲染。

所以就把问题转换成怎么在这两个component之间,切换next-task的类。一开始next-task是在第2个,当第2个切过来之后,next-task变成加在第1个上面,这样轮流交替。

进而,发现一个规律,如果currentIndex是偶数话,如o、2、4…,那么next-task是加在第2个component的,而如果currentIndex是奇数,则next-task是加在第1个component的。所以可以根据currentIndex的奇偶性切换。

如下代码所示:

<template>
<p id="app">
  <p class="task-container">
    <component :is="&#39;task-&#39; + questions[evenIndex].type" 
      :class="{&#39;next-task&#39;: nextIndex === evenIndex}"
      ref="evenTask"></component>
    <component :is="&#39;task-&#39; + questions[oddIndex].type" 
      :class="{&#39;next-task&#39;: nextIndex === oddIndex}
      ref="oddTask"></component>
  </p>
</p>
</template>
 
<script>
 
export default {
  name: 'App',
  data() {
    return {
      currentIndex: 0, // 当前显示的index
      nextIndex: 1,  // 表示下一张index,值为currentIndex + 1
      evenIndex: 0,  // 偶数的index,值为currentIndex或者是currentIndex + 1
      oddIndex: 1   // 奇数的index,值同上
    };
  },
}
登入後複製

第1个component用来显示偶数的slide,第2个是用来显示奇数的slide(分别用一个evenIndex和oddIndex代表),如果nextIndex是偶数的,那么偶数的component就会有一个next-task的类,反之则亦然。然后在下一题按钮的响应函数里面改变这几个index的值:

methods: {
  nextQuestion() {
    this.currentIndex = (this.currentIndex + 1) 
      % this.questions.length;
    this._slideToNext();
  },
  // 切到下一步的动画效果
  _slideToNext() {
 
  }
}
登入後複製

nextQuestion函数可能还有其它一些处理,在它里面调一下_slideToNext函数,这个函数的实现如下:

_slideToNext() {
  // 当前slide的类型(currentIndex已经加1了,这里要反一下)
  let currentType = this.currentIndex % 2 ? "even" : "odd",
    // 下一个slide的类型
    nextType = this.currentIndex % 2 ? "odd": "even";
  // 获取下一个slide的dom元素
  let $nextSlide = this.$refs[`${nextType}Task`].$el;
  $nextSlide.style.display = "block";
  // 把下一个slide的translate值置为0,原本是translateX(100%)
  $nextSlide.style.transform = "translateX(0)";
  // 等动画结束后更新数据
  setTimeout(() => {
    this.nextIndex = (this.currentIndex + 1) 
      % this.questions.length;
    // 原本的next是当前显示的slide
    this[`${nextType}Index`] = this.currentIndex;
    // 而原本的current slide要显示下下张的内容了
    this[`${currentType}Index`] = this.nextIndex;
  }, 500);
}
登入後複製

代码把下一个slide的display改成block,并把它的translateX的值置为0,这个时候不能马上更新数据触发DOM更新,要等到下一个slide移过去的动画结束之后再开始操作,所以加了一个setTimeout,在回调里面调换nextTask的类,加到原本的current slide,并把它的内容置成下下张的内容。这些都是通过改变相应的index完成的。

这样基本上就完成了,但是我们发现一个问题,切是切过去了,就是没有动画效果。这个是因为从display: none变到display: block是没有动画的,要么改成visibility: hidden到visible,要么触发动画的操作加到$nextTick或者setTimeout 0里面,考虑到性能问题,这里使用第二种方案:

$nextSlide.style.display = "block";
// 这里使用setimeout,因为$nextTick有时候没有动画,非必现
setTimeout(() => {
  $nextSlide.style.transform = "translateX(0)";
  // ...
}, 0);
登入後複製

经过这样的处理之后,点下一题就有动画了,但是又发现一个问题,就是偶数的next-task会被盖住,因为偶数的是使用第一个component,它是会被第二个compoent盖住的,所以需要给它加一个z-index:

.next-task { 
  display: none;
  transform: translateX(100%);
  transition: transform 0.5s ease;
  z-index: 2;
}
登入後複製

这个问题还比较好处理,另外一个不太好处理的问题是:动画的时间是0.5s,如果用户点下一题的速度很快在0.5s之内,上面的代码执行就会有问题,会导致数据错乱。如果每次切到下一题之后按钮初始化都是disabled,因为当前题还没答,只有答了才能变成可点状态,可以保证0.5s的时间是够的,那么就可以不用考虑这种情况。但是如果需要处理这种情况呢?

3. 解决点击过快的问题

我想到两个方法,第一个方法是用一个sliding的变量标志当前是否是在进行切换的动画,如果是的话,点击按钮的时候就直接更新数据,同时把setTimeout 0.5s的计时器清掉。这个方法可以解决数据错乱的问题,但是切换的效果没有了,或者是切换到一半的时候突然就没了,这样体验不是很好。

第二个方法是延后切换,即如果用户点击过快的时候,把这些操作排队,等着一个个做切换的动画。

我们用一个数组表示队列,如果当前已经在做滑动的动画,则入队暂不执行动画,如下代码所示:

methods: {
  nextQuestion() {
    this.currentIndex = (this.currentIndex + 1) 
      % this.questions.length;
    // 把currentIndex插到队首
    this.slideQueue.unshift(this.currentIndex);
    // 如果当前没有滑动,则执行滑动
    !this.sliding && this._slideToNext();
  },
}
登入後複製

每次点击按钮都把待处理的currentIndex插到队列里面,如果当前已经在滑动了,则不立刻执行,否则执行滑动_slideToNext函数:

_slideToNext() {
  // 取出下一个要处理的元素
  let currentIndex = this.slideQueue.pop();
  // 下一个slide的类型
  let nextType = currentIndex % 2 ? "odd" : "even";
  let $nextSlide = this.$refs[`${nextType}Task`].$el;
  $nextSlide.style.display = "block";
  setTimeout(() => {
    $nextSlide.style.transform = "translateX(0)";
    this.sliding = true;
    setTimeout(() => {
      this._updateData(currentIndex);
      // 如果当前还有未处理的元素,
      // 则继续处理即继续滑动
      if (this.slideQueue.length) {
        // 要等到两个component的DOM更新了
        this.$nextTick(this._slideToNext);
      } else {
        this.sliding = false;
      }
    }, 500);
  }, 0);
},
登入後複製

这个函数每次都先取出当前要处理的currentIndex,然后接下来的操作和第2点提到的一样,只是在0.5s动画结束后的异步回调里面需要判断一下,当前队列是否还有未处理的元素,如果有的话,需要继续执行_slideToNext,直到队列空了。这个执行需要挂在nextTick里面,因为需要等到两个component的DOM更新了才能操作。

这样理论上就没问题了,但实际上还是有问题,感受如下:

我们发现有些slide没有过渡效果,而且不是非必现的,没有规律。经过一番排查,发现如果把上面的nextTick改成setTimeout情况就会好一些,并且setTimeout的时间越长,就越不会出现失去过渡效果的情况。但是这个不能从根本上解决问题,这里的原因应该是Vue的自动更新DOM和transition动画不是很兼容,有可能是Vue的异步机制问题,也有可能是JS结合transition本身就有问题,但以前没有遇到过,具体没有深入排查。不管怎么样,只能放弃使用CSS的transition做动画。

如果有使用jQuery的话,可以使用jQuery的animation,如果没有的话,那么可以使用原生dom的animate函数,如下代码所示:

_slideToNext(fast = false) {
  let currentIndex = this.slideQueue.pop();
  // 下一个slide的类型
  let nextType = currentIndex % 2 ? "odd" : "even";
  // 获取下一个slide的dom元素
  let $nextSlide = this.$refs[`${nextType}Task`].$el;
  $nextSlide.style.display = "block";
  this.sliding = true;
  // 使用原生animate函数
  $nextSlide.animate([
    // 关键帧
    {transform: "translateX(100%)"},
    {transform: "translateX(0)"}
  ], {
    duration: fast ? 200 : 500,
    iteration: 1,
    easing: "ease"
  // 返回一个Animate对象,它有一个onfinish的回调
  }).onfinish = () => {
    // 等动画结束后更新数据
    this._updateData(currentIndex);
    if (this.slideQueue.length) {
      this.$nextTick(() => {
        this._slideToNext(true);
      });
    } else {
      this.sliding = false;
    }
  };
},
登入後複製

使用animate函数达到了和transition同样的效果,并且还有一个onfinish的动画结束回调函数。上面代码还做了一个优化,如果用户点得很快的时候,缩短过渡动画的时间,让它切得更快一点,这样看起来更自然一点。使用这样的方式,就不会出现transition的问题了。最后的效果如下:

这个体验感觉已经比较流畅了。

原生animate不兼容IE/Edge/Safari,可以装一个polyfill的库,如这个 web-animation ,或者使用其它一些第三方的动画库,或自己用setInterval写一个。

如果你要加上一题的按钮,支持返回上一题,那么可能需要准备3个component,中间那个用于显示,左右两边各跟着一个,准备随时切过来。具体读者可以自行尝试。

这种模式除了答题的场景,还有多封邮件预览、PPT展示等都可以用到,它除了有一个过渡的效果外,还能提前预加载下一个slide需要的图片、音频、视频等资源,并且不会像传统的轮播插件那样一下子把所有的slide都渲染了。适用于slide比较多的情况,不需要太复杂的切换动画。

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

vue-cli怎样做出跨域请求

nodejs中如何使用websocket(附代码)

以上是Vue做出內部組件輪播切換步驟詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

如何在iPhone中使Google地圖成為預設地圖 如何在iPhone中使Google地圖成為預設地圖 Apr 17, 2024 pm 07:34 PM

iPhone上的預設地圖是Apple專有的地理位置供應商「地圖」。儘管地圖越來越好,但它在美國以外的地區運作不佳。與谷歌地圖相比,它沒有什麼可提供的。在本文中,我們討論了使用Google地圖成為iPhone上的預設地圖的可行性步驟。如何在iPhone中使Google地圖成為預設地圖將Google地圖設定為手機上的預設地圖應用程式比您想像的要容易。請依照以下步驟操作–先決條件步驟–您必須在手機上安裝Gmail。步驟1–開啟AppStore。步驟2–搜尋“Gmail”。步驟3–點選Gmail應用程式旁

升級微信最新版本的步驟(輕鬆掌握微信最新版本的升級方法) 升級微信最新版本的步驟(輕鬆掌握微信最新版本的升級方法) Jun 01, 2024 pm 10:24 PM

不斷推出新版本以提供更好的使用體驗,微信作為中國的社交媒體平台之一。升級微信至最新版本是非常重要的,家人和同事的聯繫、為了保持與朋友、及時了解最新動態。 1.了解最新版本的特性與改進了解最新版本的特性與改進非常重要,在升級微信之前。效能改進和錯誤修復,透過查看微信官方網站或應用程式商店中的更新說明、你可以了解新版本所帶來的各種新功能。 2.檢查目前微信版本我們需要檢查目前手機上已安裝的微信版本、在升級微信之前。點擊,打開微信應用“我”然後選擇,菜單“關於”在這裡你可以看到當前微信的版本號,。 3.打開應

此 Apple ID 尚未在 iTunes Store 中使用:修復 此 Apple ID 尚未在 iTunes Store 中使用:修復 Jun 10, 2024 pm 05:42 PM

使用AppleID登入iTunesStore時,可能會在螢幕上拋出此錯誤提示「此AppleID尚未在iTunesStore中使用」。沒有什麼可擔心的錯誤提示,您可以按照這些解決方案集進行修復。修正1–更改送貨地址此提示出現在iTunesStore中的主要原因是您的AppleID個人資料中沒有正確的地址。步驟1–首先,開啟iPhone上的iPhone設定。步驟2–AppleID應位於所有其他設定的頂部。所以,打開它。步驟3–在那裡,打開“付款和運輸”選項。步驟4–使用面容ID驗證您的存取權限。步驟

Shazam應用程式在iPhone中無法運作:修復 Shazam應用程式在iPhone中無法運作:修復 Jun 08, 2024 pm 12:36 PM

iPhone上的Shazam應用程式有問題? Shazam可協助您透過聆聽歌曲找到歌曲。但是,如果Shazam無法正常工作或無法識別歌曲,則必須手動對其進行故障排除。修復Shazam應用程式不會花費很長時間。因此,無需再浪費時間,請按照以下步驟解決Shazam應用程式的問題。修正1–禁用粗體文字功能iPhone上的粗體文字可能是Shazam無法正常運作的原因。步驟1–您只能從iPhone設定執行此操作。所以,打開它。步驟2–接下來,開啟其中的「顯示和亮度」設定。步驟3–如果您發現啟用了“粗體文本

Win11系統管理員權限取得步驟詳解 Win11系統管理員權限取得步驟詳解 Mar 08, 2024 pm 09:09 PM

Windows11作為微軟最新推出的作業系統,深受廣大用戶喜愛。在使用Windows11的過程中,有時候我們需要取得系統管理員權限,以便進行一些需要權限的操作。接下來將詳細介紹在Windows11中取得系統管理員權限的步驟。第一步,點擊“開始功能表”,在左下角可以看到Windows圖標,點擊該圖標即可開啟“開始功能表”。第二步,在「開始功能表」中尋找並點擊「

iPhone中缺少時鐘應用程式:如何修復 iPhone中缺少時鐘應用程式:如何修復 May 03, 2024 pm 09:19 PM

您的手機中缺少時鐘應用程式嗎?日期和時間仍將顯示在iPhone的狀態列上。但是,如果沒有時鐘應用程序,您將無法使用世界時鐘、碼錶、鬧鐘等多項功能。因此,修復時鐘應用程式的缺失應該是您的待辦事項清單的首位。這些解決方案可以幫助您解決此問題。修復1–放置時鐘應用程式如果您錯誤地從主畫面中刪除了時鐘應用程序,您可以將時鐘應用程式放回原位。步驟1–解鎖iPhone並開始向左側滑動,直到到達「應用程式庫」頁面。步驟2–接下來,在搜尋框中搜尋「時鐘」。步驟3–當您在搜尋結果中看到下方的「時鐘」時,請按住它並

iPhone螢幕截圖不起作用:如何修復 iPhone螢幕截圖不起作用:如何修復 May 03, 2024 pm 09:16 PM

螢幕截圖功能在您的iPhone上不起作用嗎?截圖非常簡單,因為您只需同時按住「提高音量」按鈕和「電源」按鈕即可抓取手機螢幕。但是,還有其他方法可以在設備上捕獲幀。修復1–使用輔助觸控使用輔助觸控功能截取螢幕截圖。步驟1–轉到您的手機設定。步驟2–接下來,點選以開啟「輔助功能」設定。步驟3–開啟「觸摸」設定。步驟4–接下來,開啟「輔助觸控」設定。步驟5–打開手機上的「輔助觸控」。步驟6–打開“自訂頂級選單”以存取它。步驟7–現在,您只需將這些功能中的任何一個連結到螢幕擷取即可。因此,點擊那裡的首

iPhone上的蜂窩數據網路速度慢:修復 iPhone上的蜂窩數據網路速度慢:修復 May 03, 2024 pm 09:01 PM

在iPhone上面臨滯後,緩慢的行動數據連線?通常,手機上蜂窩互聯網的強度取決於幾個因素,例如區域、蜂窩網絡類型、漫遊類型等。您可以採取一些措施來獲得更快、更可靠的蜂窩網路連線。修復1–強制重啟iPhone有時,強制重啟設備只會重置許多內容,包括蜂窩網路連線。步驟1–只需按一次音量調高鍵並放開即可。接下來,按降低音量鍵並再次釋放它。步驟2–過程的下一部分是按住右側的按鈕。讓iPhone完成重啟。啟用蜂窩數據並檢查網路速度。再次檢查修復2–更改資料模式雖然5G提供了更好的網路速度,但在訊號較弱

See all articles