Vue实现内部组件轮播切换效果的示例代码

这篇文章主要介绍了vue实现内部组件轮播切换效果的示例代码,现在分享给大家,也给大家做个参考。

对于那些不需要路由的内部组件,在切换的时候希望增加一个轮播过渡的效果,效果如下:

Vue实现内部组件轮播切换效果的示例代码

我们可以引入一个轮播组件,但是有个问题,通常轮播组件都会把所有的slide都渲染出来再进行切换,这样就导致所有的资源都会触发加载,这可能不是我们所期待的,毕竟如果slide比较多的情况需要一次性加载的图片等资源太多了。所以我们可以手动简单地写一个,满足需求即可。

现在一步步来实现这个功能,先写一个实现基本切换的demo.

1. 实现切换

立即学习“前端免费学习笔记(深入)”;

先用vue-cli搭建一个工程脚手架,使用以下命令:

npm install -g vue-clivue 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所示:

// 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  }}

登录后复制

我们的数据格式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.里面):

  data() {    return {      currentIndex: 0    };  },  created() {    // 请求question数据    this.questions = [      {index: 1, type: 1, question: ''}, /*...*/];  },

登录后复制

通过改变currentIndex的值,从而切到一下题即下一个组件,要怎么实现这个切换的效果呢?

可以使用Vue自定义的一个全局组件component,给合它的is属性,达到动态改变组件的目的,如下代码所示:

  

      

登录后复制

当currentIndex增加时,就会改变:is里面的值,依次从task-1变到task-2、task-3等,这样component就会换成相应的task组件。

接着,再添加一个切换到下一题的按钮,在这个按钮的响应函数里面改变currentIndex的值。同时把question的数据传给component:

  

          

登录后复制

响应函数nextQuestion实现如下:

methods: {  nextQuestion() {    this.currentIndex = (this.currentIndex + 1)         % this.questions.length;  }},

登录后复制

具体每个task的实现参考如task-1.vue示例:

  

{{question.index}}. 选择题

  

{{content}}

export default { props: ["question"]}

登录后复制

最后的效果如下(加上题目内容):

Vue实现内部组件轮播切换效果的示例代码

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%),如下代码所示:

  

          

 .next-task { display: none; transform: translateX(100%); /* 添加一个动画,当改变transform的值时就会触发这个动画 */ transition: transform 0.5s ease;}

登录后复制

上面代码把具有.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的奇偶性切换。

如下代码所示:

  

          

  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更新了才能操作。

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

Vue实现内部组件轮播切换效果的示例代码

我们发现有些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的问题了。最后的效果如下:

Vue实现内部组件轮播切换效果的示例代码

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

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

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

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

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

jquery通过AJAX从后台获取信息并显示在表格上的实现类

Ajax在请求过程中显示进度的简单实现

详解ajax +jtemplate实现动态分页

以上就是Vue实现内部组件轮播切换效果的示例代码的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2756331.html

(0)
上一篇 2025年3月8日 07:30:38
下一篇 2025年3月8日 07:30:49

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 怎样正确使用vue组件复用功能

    这次给大家带来怎样正确使用vue组件复用功能,正确使用vue组件复用功能的注意事项有哪些,下面就是实战案例,一起来看一下。 一、什么是组件 组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装…

    编程技术 2025年3月8日
    100
  • Vue+BubbleTransition轻松实现页面切换效果

    这次给大家带来Vue+BubbleTransition轻松实现页面切换效果,Vue+BubbleTransition实现页面切换效果的注意事项有哪些,下面就是实战案例,一起来看一下。   CodePen 地址 前端使用 SPA 之后,能获得…

    2025年3月8日
    200
  • express默认日志组件morgan的方法

    morgan是express默认的日志中间件,这篇文章主要介绍了express默认日志组件morgan的方法,现在分享给大家,也给大家做个参考。 章节概览 morgan是express默认的日志中间件,也可以脱离express,作为node…

    编程技术 2025年3月8日
    200
  • React Native悬浮按钮组件的示例代码

    本篇文章主要介绍了react native悬浮按钮组件的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起看看吧。 React Native悬浮按钮组件:react-native-a…

    2025年3月8日
    200
  • 使用Vue制作图片轮播组件思路详解

    这篇文章主要介绍了使用vue制作图片轮播组件思路详解,需要的朋友可以参考下 之前一直都没有认真的写过一个组件。以前在写业务代码的过程中,都是用的别人封装好的组件,这次尝试着写了一个图片轮播组件,虽然比不上知名的轮播组件,但它的功能基本完整,…

    2025年3月8日 编程技术
    200
  • angular4 共享服务在多个组件中数据通信的示例

    本篇文章主要介绍了angular4 共享服务在多个组件中数据通信的示例,现在分享给大家,也给大家做个参考。 应用场景,不同组件中操作统一组数据,不论哪个组件对数据进行了操作,其他组件中立马看到效果。这样他们就要共用一个服务实例,是本次的重点…

    2025年3月8日
    200
  • 怎样实现vue父子组件间通信

    这次给大家带来怎样实现vue父子组件间通信,实现vue父子组件间通信的注意事项有哪些,下面就是实战案例,一起来看一下。 组件是 vue.js 最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。那么组…

    2025年3月8日 编程技术
    200
  • vue文件树组件使用详解

    这篇文章主要为大家详细介绍了vue文件树组件的使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 本文实例为大家分享了vue文件树组件的实现方法,供大家参考,具体内容如下 本文主要是分析vue官方仓库里的文件树组件[vue gith…

    编程技术 2025年3月8日
    200
  • 如何使用Vue内父子组件通讯todolist组件

    这次给大家带来如何使用Vue内父子组件通讯todolist组件,使用Vue内父子组件通讯todolist组件的注意事项有哪些,下面就是实战案例,一起来看一下。 一、todolist功能开发 提交 {{item}} new Vue({ el:…

    2025年3月8日
    200
  • 浅谈React高阶组件

    这篇文章主要介绍了浅谈react高阶组件,现在分享给大家,也给大家做个参考。 前段时间在工作中写Hybrid页面时遇到了这样的一个场景,公司需要一系列的活动组件,在每个组件注册的时候都需要调用App端提供的一个接口。一开始也考虑了几种方式,…

    2025年3月8日
    200

发表回复

登录后才能评论