使用vue实现排序算法演示动画

简介: 使用vue实现排序算法演示动画

缘起

最近做的一个小需求涉及到排序,界面如下所示:

image.png


因为项目是使用vue的,所以实现方式很简单,视图部分不用管,本质上就是操作数组,代码如下:


{
    // 上移
    moveUp (i) {
        // 把位置i的元素移到i-1上
      let tmp = this.form.replayList.splice(i, 1)
      this.form.replayList.splice(i - 1, 0, tmp[0])
    },
    // 下移
    moveDown (i) {
        // 把位置i的元素移到i+1上
      let tmp = this.form.replayList.splice(i, 1)
      this.form.replayList.splice(i + 1, 0, tmp[0])
    }
}


这样就可以正常的交换位置了,但是是突变的,没有动画,所以不明显,于是一个码农的自我修养(实际上是太闲)让我打开了vue的网站,看到了这个示例:

cn.vuejs.org/v2/guide/tr…

image.pngimage.pngimage.png


这个示例我已看过多遍,但是一直没用过,这里刚好就是我要的效果,于是一通复制粘贴大法:


<template>
    <transition-group name="flip-list" tag="p">
        <!--循环生成列表部分,略-->
    </transition-group>
</template>
<style>
.flip-list-move {
  transition: transform 0.5s;
}
</style>


这样就有交换的过渡效果了,如下:


image.png

image.png

嗯,舒服了很多,这个需求到这里就完了,但是事情并没有结束,我突然想到了以前看一些算法文章的时候通常会配上一些演示的动画,感觉跟这个很类似,那么是不是可以用这个来实现呢,当然是可以的。


实现算法演示动画


先写一下基本的布局和样式:


<template>
  <div class="sortList">
      <transition-group name="flip-list" tag="p">
        <div
          class="item"
          v-for="item in list"
          :key="item.index"
          :style="{height: (item.value / max * 100) + '%'}"
        >
          <span class="value">{{item.value}}</span>
        </div>
      </transition-group>
    </div>
</template>
<style>
.flip-list-move {
  transition: transform 0.5s;
}
</style>


list是要排序的数组,当然是经过处理的,在真正的源数组上加上了唯一的index,因为要能正常过渡的话列表的每一项需要一个唯一的key:


const arr = [10, 43, 23, 65, 343, 75, 100, 34, 45, 3, 56, 22]
export default {
  data () {
    return {
      list: arr.map((item, index) => {
        return {
          index,
          value: item
        }
      })
    }
  }
}


max是这个数组中最大的值,用来按比例显示高度:


{
    computed: {
        max () {
            let max = 0
            arr.forEach(item => {
                if (item > max) {
                    max = item
                }
            })
            return max
        }
  }
}


其他样式可以自行发挥,显示效果如下:



image.png


简约而不简单~,现在万事俱备,只欠让它动起来,排序算法有很多,但是本人比较菜,所以就拿冒泡算法来举例,最最简单的冒泡排序算法如下:


{
    mounted(){
        this.bubbleSort()
    },
    methods: {
        bubbleSort() {
          let len = this.list.length
          for (let i = 0; i < len; i++) {
            for (let j = 0; j < len - i - 1; j++) {
              if (this.list[j] > this.list[j + 1]) {  // 相邻元素两两对比
                let tmp = this.list[j]        // 元素交换
                this.$set(this.list, j, this.list[j + 1])
                this.$set(this.list, j + 1, tmp)
              }
            }
          }
        }
    }
}


但是这样写它是不会动的,瞬间就给你排好了:


image.png


试着加个延时:


{
    mounted () {
        setTimeout(() => {
            this.bubbleSort()
        }, 1000)
    }
}


刷新看效果:


image.png


有动画了,不过这种不是我们要的,我们要的应该是下面这样的才对:


image.png


image.png


image.png


所以来改造一下,因为for循环是只要开始执行就不会停的,所以需要把两个for循环改成两个函数,这样可以控制每个循环什么时候执行:


{
    bubbleSort () {
      let len = this.list.length
      let i = 0
      let j = 0
      // 内层循环
      let innerLoop = () => {
        // 每个内层循环都执行完毕后再执行下一个外层循环
        if (j >= (len - 1 - i)) {
          j = 0
          i++
          outLoop()
          return false
        }
        if (this.list[j].value > this.list[j + 1].value) {
          let tmp = this.list[j]
          this.$set(this.list, j, this.list[j + 1])
          this.$set(this.list, j + 1, tmp)
        }
        // 动画是500毫秒,所以每隔800毫秒执行下一个内层循环
        setTimeout(() => {
          j++
          innerLoop()
        }, 800)
      }
      // 外层循环
      let outLoop = () => {
        if (i >= len) {
          return false
        }
        innerLoop()
      }
      outLoop()
    }
}


这样就实现了每一步的动画效果:


image.png


但是这样不太直观,因为有些相邻不用交换的时候啥动静也没有,不知道当前具体排到了哪两个,所以需要突出当前正在比较交换的两个元素,首先模板部分给当前正在比较的元素加一个类名,用来高亮显示:


<div
     :class="{sortingHighlight: sorts.includes(item.index)}"
     >
    <span class="value">{{item.value}}</span>
</div>


js部分定义一个数组sorts来装载当前正在比较的两个元素的唯一的index值:


{
    data() {
        return {
            sorts: []
        }
    },
    methods: {
        bubbleSort () {
            // ...
            // 内层循环
            let innerLoop = () => {
                // 每个内层循环都执行完毕后再执行下一个外层循环
                if (j >= (len - 1 - i)) {
                    // 清空数组
                    this.sorts = []
                    j = 0
                    i++
                    outLoop()
                    return false
                }
                // 将当前正在比较的两个元素的index装到数组里
                this.sorts = [this.list[j].index, this.list[j + 1].index]
                // ...
            }
            // 外层循环
            // ...
        }
    }
}


修改后效果如下:


image.png


image.png



最后,再参考刚才别人的示例把已排序的元素也加上高亮:


{
    data() {
        return {
            sorted: []
        }
    },
    methods: {
        bubbleSort () {
            // ...
            // 内层循环
            let innerLoop = () => {
                // 每个内层循环都执行完毕后再执行下一个外层循环
                if (j >= (len - 1 - i)) {
                    this.sorts = []
                    // 看这里,把排好的元素加到数组里就ok了
                    this.sorted.push(this.list[j].index)
                    j = 0
                    i++
                    outLoop()
                    return false
                }
                // ...
            }
            // 外层循环
            // ...
        }
    }
}


最终效果如下:


image.png


接下来看一下选择排序,这是选择排序的算法:


{
    selectSort() {
        for (let i = 0; i < len - 1; i++) {
            minIndex = i
            for (let j = i + 1; j < len; j++) {
                if (this.list[j].value < this.list[minIndex].value) {
                    minIndex = j
                }
            }
            tmp = this.list[minIndex]
            this.$set(this.list, minIndex, this.list[i])
            this.$set(this.list, i, tmp)
        }
    }
}


选择排序涉及到一个当前最小元素,所以需要新增一个高亮:


<div
     :class="{minHighlight: min === item.index , sortingHighlight: sorts.includes(item.index), sortedHighlight: sorted.includes(item.index)}"
     >
    <span class="value">{{item.value}}</span>
</div>


{
    data () {
        return {
            min: 0
        }
    },
    methods: {
        selectSort () {
            let len = this.list.length
            let i = 0; let j = i + 1
            let minIndex, tmp
      // 内层循环
            let innerLoop = () => {
                if (j >= len) {
                    // 高亮最后要交换的两个元素
                    this.sorts = [this.list[i].index, this.list[minIndex].index]
                    // 延时是用来给高亮一点时间
                    setTimeout(() => {
                        // 交换当前元素和比当前元素小的元素的位置
                        tmp = this.list[minIndex]
                        this.$set(this.list, minIndex, this.list[i])
                        this.$set(this.list, i, tmp)
                        this.sorted.push(this.list[i].index)
                        i++
                        j = i + 1
                        outLoop()
                    }, 1000)
                    return false
                }
                // 高亮当前正在寻找中的元素
                this.sorts = [this.list[j].index]
                // 找到比当前元素小的元素
                if (this.list[j].value < this.list[minIndex].value) {
                    minIndex = j
                    this.min = this.list[j].index
                }
                setTimeout(() => {
                    j++
                    innerLoop()
                }, 800)
            }
            let outLoop = () => {
                if (i >= len - 1) {
                    this.sorted.push(this.list[i].index)
                    return false
                }
                minIndex = i
                this.min = this.list[i].index
                innerLoop()
            }
            outLoop()
        }
    }
}


效果如下:


image.png


image.png

其他的排序也是同样的套路,将for循环或while循环改写成可以控制的函数形式,然后可能需要稍微修改一下显示逻辑,如果你也有打算写排序文章的话现在就可以给自己加上动图展示了!


总结


之前看到这些动图的时候也有想过怎么实现,但是都没有深究,这次业务开发无意中也算找到了其中的一种实现方式,其实核心逻辑很简单,关键是很多时候没有想到可以这么做,这也许是框架带给我们的另一些好处吧。



相关文章
|
27天前
|
算法 JavaScript
Vue 中的 Diff 算法
【10月更文挑战第18天】需要注意的是,Diff 算法虽然能够提高性能,但在某些复杂的场景下,可能仍然会存在一些性能瓶颈。因此,在实际开发中,我们需要根据具体情况合理地使用 Diff 算法,并结合其他优化手段来提高应用的性能。
12 1
|
1月前
|
JavaScript 算法 前端开发
vue 中diff算法
【10月更文挑战第10天】
29 1
|
1月前
|
JavaScript 算法 前端开发
【VUE】Vue的diff算法和React的diff算法
【VUE】Vue的diff算法和React的diff算法
|
29天前
|
JavaScript 前端开发 Java
vue2知识点:Vue封装的过度与动画
vue2知识点:Vue封装的过度与动画
14 0
|
1月前
|
JavaScript
vue尚品汇商城项目-day03【16.开发Search组件模块中的TypeNav商品分类菜单(过渡动画效果)+17.(优化)针对三级菜单联动进行优化,优化方向为减少查询】
vue尚品汇商城项目-day03【16.开发Search组件模块中的TypeNav商品分类菜单(过渡动画效果)+17.(优化)针对三级菜单联动进行优化,优化方向为减少查询】
35 0
|
3月前
|
JavaScript 前端开发 API
【Vue 3】一个小巧玲珑的 Vue 组件切换动画库,开源且免费!!
【Vue 3】一个小巧玲珑的 Vue 组件切换动画库,开源且免费!!
【Vue 3】一个小巧玲珑的 Vue 组件切换动画库,开源且免费!!
|
3月前
|
JavaScript 前端开发 UED
Vue.js动画魔法:解锁流畅过渡,让每一次交互都成为用户心中的小确幸!
【8月更文挑战第30天】在Vue.js中,动画与过渡效果不仅是视觉点缀,更是提升用户体验的关键。通过流畅的动态效果,应用的互动性和吸引力得以增强,从而提高用户满意度和参与度。`&lt;transition&gt;`和`&lt;transition-group&gt;`组件结合CSS过渡,可轻松实现元素的进入、离开及列表变化动画。合理的性能优化,如使用硬件加速,能避免页面卡顿,确保动画既美观又高效。下面是一个简单的淡入淡出效果示例,展示了如何利用Vue.js实现平滑的动画过渡。总之,恰当的动画设计能显著提升应用的用户体验。
56 0
Vue.js动画魔法:解锁流畅过渡,让每一次交互都成为用户心中的小确幸!
|
3月前
|
JavaScript
Vue3数值动画(NumberAnimation)
该文档介绍了一个基于 Vue 的数值动画组件 `NumberAnimation`,提供了丰富的配置选项,如起始值、目标值、动画时长等,并支持自定义前缀、后缀及样式。通过简单的方法和事件,可以轻松控制动画的播放与停止。
119 0
Vue3数值动画(NumberAnimation)
|
3月前
|
JavaScript 算法 索引
【Vue面试题二十三】、你了解vue的diff算法吗?说说看
这篇文章深入分析了Vue中的diff算法,解释了其在新旧虚拟DOM节点比较中的工作机制,包括同层节点比较、循环向中间收拢的策略,并通过实例演示了diff算法的执行过程,同时提供了源码层面的解析,说明了当数据变化时,如何通过Watcher触发patch函数来更新DOM。
【Vue面试题二十三】、你了解vue的diff算法吗?说说看
|
4月前
|
JavaScript
vue 组件封装 | s-fullpage 全屏滚动 (内含绑定鼠标滑轮滚动事件、避免鼠标滑轮连续滚动、滑动过渡动画等实用技巧)
vue 组件封装 | s-fullpage 全屏滚动 (内含绑定鼠标滑轮滚动事件、避免鼠标滑轮连续滚动、滑动过渡动画等实用技巧)
102 4