Vue - Todo案例(五)

1,770 阅读4分钟

这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战

前言

终于来到了这一天了啊!期待不期待呢?前面搞完了事件、渲染指令、过渡。那么今天就来搞个基础版的Todos啊。后面会讲到计算属性这一知识点。我们再搞个升级版的吧!

介绍

今天我们利用transitoin-group实现一个简单的todos啊。

主要功能

  • 新增
  • 删除
  • 切换todo的状态

实现步骤

  • 布局
  • 过渡动画
  • 逻辑

实现步骤

1、简单布局

en,首先这里的话呢我是没有写任何的样式的啊。样式自己私下画画,主要在于实现功能。

其次有三点:

  1. 新增用的是回车事件(修饰符) --- @keyup.enter.exact
  2. transition-group实现过渡动画
  3. 通过动态绑定class进行状态的切换
<div id="app">
    <input type="text" @keyup.enter.exact="addTodo">
    <ul>
        <transition-group name="move">
            <li v-for="t in todos" :key="t.id" @click.self="toggleTodo(t.id)" :class="{completed: t.completed}">
                {{t.text}}
                <button @click="removeTodo(t.id)">删除</button>
            </li>
        </transition-group>
    </ul>
</div>

2、书写过渡动画

<style>
    /* 已完成todo的样式 */
    .completed {
        color: #777;
        text-decoration: line-through;
    }

    .move-leave,
    .move-enter-to {
        transform: translateX(0);
        opacity: 1;
    }

    .move-leave-active,
    .move-enter-active
     {
        transition: all .5s linear;
    }

    .move-leave-to,
    .move-enter {
        /* 从右边进入,从右到左的一个过渡,负数就与之相反 */
        transform: translateX(200px);
        opacity: 0;
    }
</style>

我们来看看官网是怎么描述的呢? 在进入/离开的过渡中,会有 6 个 class 切换。

  1. v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。

  2. v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。

  3. v-enter-to2.1.8 版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。

  4. v-leave:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。

  5. v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。

  6. v-leave-to2.1.8 版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。

transition.png

这里看下来可能有的复杂啊,其实我们只需记住enter是进入,leave是离开,active是生效时,to代表就是结束 疑问: 那为什么不是 v- ,而是 move- 呢?

答: 在过渡中切换的类名来说,如果你使用的是一个没有名字(name)的 <transition>v- 就是这些类名的默认前缀,那么一旦你使用了name,那么 v- 就会被替换成 name-。就比如我们上面用的是<transition-group name="move">,就会变成 move-enter

3、方法逻辑

根据开头我们说的todo有啥功能啊,我们就知道要写什么样的方法,从哪入手。一个是新增,切换状态,删除。

那就从最简单的新增开始吧,有了新增才能搞其他的对吧!这里我们把切换状态的放最后吧!

新增

这里我们通过事件对象来拿到我们input中输入的值,然后使用push改变原数组进行新增一条数据。

addTodo(evt) {
    console.log(evt.target.value)

    // 去除首尾空格
    let text = evt.target.value.trim()

    // 删除字符串首尾连续空白
    if (!text) {
        return
    }

    // 添加一条新的todo
    this.todos.push({
        id: new Date().getTime(),
        text,
        completed: false
    })

    // 清除输入框文本内容
    evt.target.value = ""
},

删除

这还不简单嘛,直接filter搞起就完事了。

removeTodo(id) {
   this.todos = this.todos.filter(t => t.id !== id)
}

切换状态

这里我们使用map对todos原数组进行操作,判断id是否相等,如果当前点击todo的id相等,那我们直接改变当前状态不就完事了么,其他的不变给他return回去。

toggleTodo(id) {
    this.todos = this.todos.map(t => {
        if (t.id === id) {
            return {
                ...t,
                completed: !t.completed
            }
        }
        return t
    })
},

效果

a2.gif

完整代码

这里把代码整理到一起,方便大家进行测试,也可以点击在线测试查看效果。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        /* 已完成todo的样式 */
        .completed {
            color: #777;
            text-decoration: line-through;
        }

        .move-leave,
        .move-enter-to {
            transform: translateX(0);
            opacity: 1;
        }

        .move-leave-active,
        .move-enter-active
         {
            transition: all .5s linear;
        }

        .move-leave-to,
        .move-enter {
            /* 从右边进入,从右到左的一个过渡,负数就与之相反 */
            transform: translateX(200px);
            opacity: 0;
        }
    </style>
</head>

<body>
    // 引入vue
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

    <div id="app">
        <input type="text" @keyup.enter.exact="addTodo">
        <ul>
            <transition-group name="move">
                <li v-for="t in todos" :key="t.id" @click.self="toggleTodo(t.id)" :class="{completed: t.completed}">
                    {{t.text}}
                    <button @click="removeTodo(t.id)">删除</button>
                </li>
            </transition-group>
        </ul>
    </div>

    <script>
        new Vue({
            el: '#app',
            data: {
                // 定义默认的todos数据
                todos: [{
                        id: 1,
                        text: '学习vue',
                        completed: false,
                    },
                    {
                        id: 2,
                        text: '学习webpack',
                        completed: true,
                    },
                    {
                        id: 3,
                        text: '学习ES6',
                        completed: true,
                    }
                ]
            },
            methods: {
                // 新增
                addTodo(evt) {
                    console.log(evt.target.value)

                    // 去除首尾空格
                    let text = evt.target.value.trim()

                    // 删除字符串首尾连续空白
                    if (!text) {
                        return
                    }

                    // 添加一条新的todo
                    this.todos.push({
                        id: new Date().getTime(),
                        text,
                        completed: false
                    })

                    // 清除输入框文本内容
                    evt.target.value = ""
                },

                // 切换状态
                // 这里我们使用map对todos原数组进行操作,如果当前点击todo的id相等,那我们改变状态不就完事了么,其他的不变给他返回去。
                toggleTodo(id) {
                    this.todos = this.todos.map(t => {
                        if (t.id === id) {
                            return {
                                ...t,
                                completed: !t.completed
                            }
                        }
                        return t
                    })
                },

                // 删除
                removeTodo(id) {
                   this.todos = this.todos.filter(t => t.id !== id)
                }
            }
        })
    </script>
</body>

</html>

结尾

今天就先到这里啦!我们下期再见!码字不易,觉得不错的可以动动小指头点点赞啥的哟~

系列文章

Vue系列

Vue-Router系列

Vuex系列