第二十八节:Vue过渡与动画

前言:

Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。包括以下工具:

  • 在 CSS 过渡和动画中自动应用 class
  • 可以配合使用第三方 CSS 动画库,如 Animate.css
  • 在过渡钩子函数中使用 JavaScript 直接操作 DOM
  • 可以配合使用第三方 JavaScript 动画库,如 Velocity.js


那么接下来就让我们好好Vue中的过度与动画.


1. 过渡或动画的了解

Vue 提供了 transition 的封装组件,来处理过渡以及动画


1.1 使用过渡或动画的场景

在下列情形中,可以给任何元素和组件添加进入/离开过渡

  1. 条件渲染(使用v-if)
  2. 条件展示(使用v-show)
  3. 动态组件
  4. 组件的根节点


1.2 使用说明
  1. 只有dom从隐藏到显示,或从显示到隐藏, 才能使用vue的过渡或动画

  2. 动画的元素需要被Vue所提供的组件transition所包裹

  3. 在插入或删除transition包裹元素时,Vue会在恰当的实际添加或删除类名

  4. 如果过渡的组件提供了JavaScript钩子函数,则Vue会在恰当的时机调用这些钩子函数

  5. 如果没有找到JavaScrip钩子函数,并且也没有检测到CSS过渡/动画,则会在下一帧中立即执行


2. Vue 过渡的使用

2.1 认识过渡

Vue过渡/动画两个过程,分别为进入和离开.

Vue会在不同的需要过渡的元素在进入/离开的不同时机给元素添加不同的类名

不同阶段的类名:

Enter Enter Leave Leave
Opacity:0 Opacity:1 Opacity:1 Opacity:0
v-enter v-enter-to v-leave v-leave-to
v-enter-active v-enter-active v-leave-active v-leave-active
过度.png


2.2 过度类名的了解

在进入/离开的过渡中,会有 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 被删除),在过渡/动画完成之后移除。


现在我们对于这些类名有了一定的了解之后,我们通过案例来加深对于这些类名的理解和记忆


2.3 示例:
<style>
    div{
        font-size:30px;
        font-weight: bold;
    }
    .v-enter{
        color:red;
    }
    .v-enter-to{
        color:blue;
    }
    .v-enter-active{
        transition:all 5s;
    }
    .v-leave{
        color: blue;
    }
    .v-leave-to{
        color: purple;
    }
    .v-leave-active{
        transition:all 3s;
    }
</style>



<div id="app">
    <!-- 按钮,点击切换元素的显示和隐藏 -->
    <button @click="isShow = !isShow">点击切换</button>

    <!-- 显示元素 -->
    <transition>
        <div v-if="isShow">需要动画的元素</div>
    </transition>

</div>


<script>

    //  实例
    const vm = new Vue({
        el:"#app",
        data:{
            isShow:true
        }
    })
</script>


2.4 过渡前缀

过渡前缀说明

  1. 如果你使用一个没有名字的 <transition>,则 v- 是这些类名的默认前缀。
  2. 如果使用了 <transition name="myname">,那么 v-enter 会替换为 myname-enter

示例:

<style>
    div{
        font-size:30px;
        font-weight: bold;
    }
    .fade-enter,
    .fade-leave-to{
        color:red;
    }
    .fade-enter-to{
        color:blue;
    }
    .fade-enter-active,
    .fade-leave-active{
        transition:all 3s;
    }

</style>


<div id="app">
    <!-- 按钮,点击切换元素的显示和隐藏 -->
    <button @click="isShow = !isShow">点击切换</button>

    <!-- 显示元素 -->
    <transition name="fade">
        <div v-if="isShow">需要动画的元素</div>
    </transition>

</div>


<script>

    //  实例中注册组件
    const vm = new Vue({
        el:"#app",
        data:{
            isShow:true
        }
    })
</script>


2.5 在过渡中使用贝塞尔曲线
<style>
    div{
        position: fixed;
        font-size:30px;
        font-weight: bold;
    }
    .fade-enter,
    .fade-leave-to{
        left:200px;
    }
    .fade-enter-to{
        left:10px;
    }
    .fade-leave{

        left:10px;
    }
    .fade-enter-active{
        transition:all 3s linear;
    }

    .fade-leave-active{
        transition:all 3s cubic-bezier(.94,.1,.93,.08);
    }

</style>



<div id="app">
    <!-- 按钮,点击切换元素的显示和隐藏 -->
    <button @click="isShow = !isShow">点击切换</button>

    <!-- 显示元素 -->
    <transition name="fade">
        <div v-if="isShow">需要动画的元素</div>
    </transition>

</div>


<script>

    //  实例中注册组件
    const vm = new Vue({
        el:"#app",
        data:{
            isShow:true
        }
    })
</script>


3. CSS动画

我们处理可以在Vue中使用过渡transition,还可以使用CSS动画animation

<style>
    div{
        font-size:30px;
        font-weight: bold;
    }
    @keyframes fade-in{
        0%{
            transform: scale(0);
        }
        50%{
            transform: scale(1.5);
        }
        100%{
            transform: scale(1);
        }
    }
    .fade-enter-active{
        animation: fade-in 10s linear;
    }

    .fade-leave-active{
        animation: fade-in 10s linear reverse;
    }

</style>



<div id="app">
    <!-- 按钮,点击切换元素的显示和隐藏 -->
    <button @click="isShow = !isShow">点击切换</button>

    <!-- 显示元素 -->
    <transition name="fade">
        <div v-if="isShow">需要动画的元素</div>
    </transition>

</div>


<script>

    const vm = new Vue({
        el:"#app",
        data:{
            isShow:true
        }
    })
</script>


3. 自定义过度动画的类名

我们可以通过以下 attribute 来自定义过渡类名:

  • enter-class
  • enter-active-class
  • enter-to-class (2.1.8+)
  • leave-class
  • leave-active-class
  • leave-to-class (2.1.8+)

他们的优先级高于普通的类名,这对于 Vue 的过渡系统和其他第三方 CSS 动画库,如 Animate.css 结合使用十分有用。

示例:

<style>
    div{
        font-size:30px;
        font-weight: bold;
    }
    @keyframes fade-in{
        0%{transform: scale(0);}
        50%{transform: scale(1.5);}
        100%{transform: scale(1);}
    }
    .enter{
        transform-origin:left center;
        animation: fade-in 3s linear;
    }

    .leave{
        transform-origin:left center;
        animation: fade-in 3s linear reverse;
    }

</style>



<div id="app">
    <!-- 按钮,点击切换元素的显示和隐藏 -->
    <button @click="isShow = !isShow">点击切换</button>

    <!-- 显示元素 -->
    <transition 
                name="fade"
                enter-active-class="enter"
                leave-active-class="leave"
                >
        <div v-if="isShow">需要动画的元素</div>
    </transition>

</div>


<script>

    const vm = new Vue({
        el:"#app",
        data:{
            isShow:true
        }
    })
</script>


4.动画插件库

animated.css 使用的是keyframes 的动画

4.1 安装

安装

npm install animate.css --save-dev

引入

  <link rel="stylesheet" href="./node_modules/animate.css/animate.css">


4.2 使用cdn
<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">


4.3 使用
<transition enter-active-class="animated rotateIn" leave-active-class="animated rotateIn">
    <div v-show="show" class='box'>Hello world</div>
</transition>

使用animation动画库的好处就是不需要我们自己去写负责的动画


4.4 最开始入场动画
<transition
            name="fade"
            enter-active-class="animated swing"
            leave-active-class="animated flipInX"
            >
  <div v-show="show">Hello world</div>
</transition>

但是你会发现最开始是没有入场动画的,如果我也希望网页一打开最开始就有入场动画

应该如下处理,添加appear属性

<transition
            name="fade"
            appear
            enter-active-class="animated swing"
            leave-active-class="animated flipInX"
            appear-active-class="animated swing"
            >
  <div v-show="show">Hello wold</div>
</transition>


4.5 animation动画和transition过渡一起使用
4.5.1 动画过渡一起使用

示例:

<style>
  .fade-enter,
  .fade-leave-to{
    opacity:0;
  }
  .fade-enter-active,
  .fade-leave-active{
    transition: opacity 3s
  }
</style>
<div id="app">
  <transition
              
              name="fade"
              appear
              enter-active-class="animated swing fade-enter-active"
              leave-active-class="animated flipInX fade-leave-active"
              appear-active-class="animated swing"
              >
    <div v-show="show">Hello wold</div>
  </transition>


此时你会发现animated.css的animation动画的时长和transition的时长不一样,那么要以哪个为准呢,其实vue也不知道,所以我们可以添加一个type属性来制定动画时长以谁为准

<transition
            type="transition"   
            name="fade"
            appear
            enter-active-class="animated swing fade-enter-active"
            leave-active-class="animated flipInX fade-leave-active"
            appear-active-class="animated swing"
            >
  <div v-show="show">Hello wold</div>
</transition>


4.5.2 自定义过渡/动画时间

同时我们也可以自定义动画时长,通过绑定duration,5秒后

<transition
            :duration="5000"  
            name="fade"
            appear
            enter-active-class="animated swing fade-enter-active"
            leave-active-class="animated flipInX fade-leave-active"
            appear-active-class="animated swing"
            >
  <div v-show="show">Hello wold</div>
</transition>


我们也可以单独定义入场动画时长和出场动画时长

<transition
            :duration="{enter:5000,leave:10000}"  
            name="fade"
            appear
            enter-active-class="animated swing fade-enter-active"
            leave-active-class="animated flipInX fade-leave-active"
            appear-active-class="animated swing"
            >
  <div v-show="show">Hello wold</div>
</transition>


5. Vue中的JS动画

vue中的js动画是通过vue提供给的动画钩子函数来绑定事件,然后在事件函数中处理对应的动画

5.1 动画入场的钩子函数

before-enter 动画入场运动前一刻执行

enter 动画运动时执行

after-enter 在动画enter函数中运行完毕并调用回调done时执行

<div id="app">
  <transition
              name="fade"
              @before-enter="handleBeforeEnter"
              @enter="handleEnter"
              @after-enter="handleAfterEnter"
              >
    <div v-show="show">Hello wold</div>
  </transition>
  <button @click="handleChange">点击</button>
</div>

<script>
  var app = new Vue({
    el:'#app',
    data:{
      show: true
    },
    methods:{
      handleChange(){
        this.show= !this.show
      },
      handleBeforeEnter(el){
        // 动画入场的前一刻执行before-enter动画钩子函数
        el.style.color="red"
      },
      handleEnter(el,done){
        // 动画运行的钩子函数
        // done是动画运行完成后的回调函数
        // 当动画运行完成以后执行done函数会触发after-enter动画钩子函数的运行
        setTimeout(() => {
          el.style.color = "blue";
        },2000);
        setTimeout(() => {
          done()
        },4000)
      },
      handleAfterEnter(el){
        // 动画完成后执行的动画钩子函数
        // 只用当动画钩子函数enter运行完毕后并且调用done时才会触发次函数
        el.style.color="#000"
      }
    }
  })
</script>


5.2 动画出场的钩子函数

同样动画出场的钩子函数与动画入场的钩子函数使用是一致的

before-leave

leave

after-leave

我们发现js动画没有动画时长需要我么自己调整,我们可以借助Velocity.js处理js动画



注意:

  1. 使用JavaScript 过渡的时候,在 enter 和 leave 中必须使用 done 进行回调。否则过渡会立即完成。
  2. 对于仅使用 JavaScript 过渡的元素添加 v-bind:css="false",Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响。

例如:

<transition
            :css="false"
            name="fade"
            @before-enter="handleBeforeEnter"
            @enter="handleEnter"
            @after-enter="handleAfterEnter"
            >
    <div v-show="show">Hello wold</div>
</transition>


5.3 Vue中JS动画与Velocity.js的结合

首先进入官网下载Velocity.js

然后引入

<script src="./velocity.js"></script>

再次使用

<div id="app">
  <transition
              name="fade"
              @before-enter="handleBeforeEnter"
              @enter="handleEnter"
              @after-enter="handleAfterEnter"
              >
    <div v-show="show">Hello wold</div>
  </transition>
  <button @click="handleChange">点击</button>
</div>
<script src="./vue.js"></script>
<script>
  var app = new Vue({
    el:'#app',
    data:{
      show: true
    },
    methods:{
      handleChange(){
        this.show= !this.show
      },
      handleBeforeEnter(el){

        el.style.opacity=0  // 动画开始样式
      },
      handleEnter(el,done){


        Velocity(el,{
          opacity: 1   // 动画结束后的样式
        },{
          duration: 2000,  // 动画运行的时间
          complete: done   // 动画运动完成后执行的函数
        })

      },
      handleAfterEnter(el){
        el.style.fontSize="50px"
      }
    }
  })
</script>


6.Vue中多个元素之间的动画

6.1 多个元素之前切换动画

多元素过渡,就是利用v-if或者v-show等指令,切换元素的显示, 需要显示的元素就会有入场动画,需要隐藏的就有出场动画.

过渡示例:

<style>
    div{
        font-size:30px;
        font-weight: bold;
        opacity:1;
    }
    .fade-enter,.fade-leave-to{
        opacity: 0;
    }
    .fade-enter-active,.fade-leave-active{
        transition:all 3s;
    }

</style>

<div id="app">
    <!-- 按钮,点击切换元素的显示和隐藏 -->
    <button @click="isShow = !isShow">点击切换</button>

    <!-- 显示元素 -->
    <transition 
                name="fade"
                >
        <div v-if="isShow" key="hello">Hello wold</div>
        <div v-else key="bye">Bye wold</div>
    </transition>
</div>

<script>

    const vm = new Vue({
        el:"#app",
        data:{
            isShow:true
        }

    })
</script>

一定要注意动画内部两个div的标签复用,如果不加key,只会看到内容改变了,透明度样式并没有发生变化,这就是vue中的dom复用的机制,就是为了提升效率, 所以我们要加可以表示 唯一的dom

注意:

  1. 当有相同标签名的元素切换时,需要通过 key 属性设置唯一的值来标记以让 Vue 区分它们
  2. key属性的作用,在讲v-for已经介绍过来, 为了不让DOM元素复用
  3. DOM元素复用是Vue提升性能的手段,但是因为DOM元素复用了,也就没有动画效果了,
  4. 因此通过key 属性阻止DOM元素复用


6.2 出场入场动画先后顺如

默认出场入场动画是同时进行的,同时生效的进入和离开的过渡不能满足所有要求,所以Vue提供了过渡模式

mode属性说明

  1. Vue在transition标签上提供了一个mode属性,来设置出场入场动画的先后顺序

  2. mode的属性值为in-out,则为先执行入场动画,等入场动画执行完毕后在执行出场动画,

  3. mode属性值为out-in 则先执行出场动画,在执行入场动画

示例:

<style>
    div{
        font-size:30px;
        font-weight: bold;
        opacity:1;
    }
    .fade-enter,.fade-leave-to{
        opacity: 0;
    }
    .fade-enter-active,.fade-leave-active{
        transition:all 3s;
    }
</style>


<div id="app">
    <!-- 按钮,点击切换元素的显示和隐藏 -->
    <button @click="isShow = !isShow">点击切换</button>

    <!-- 显示元素 -->
    <transition 
                name="fade"
                mode="in-out"
                >
        <div v-if="isShow" key="hello">Hello wold</div>
        <div v-else key="bye">Bye wold</div>
    </transition>

</div>


<script>

    const vm = new Vue({
        el:"#app",
        data:{
            isShow:true
        }

    })
</script>


7. Vue中组件之间的动画

7.1. 不利用动态组件的写法

示例:

<style>
    .fade-enter,.fade-leave-to{
        opacity:0
    }
    .fade-enter-active,.fade-leave-active{
        transition: opacity 1s;
    }
</style>
<div id="app">
    <transition
                name="fade"
                mode="out-in"
                >
        <child v-if="show"></child>
        <child-one v-else></child-one>
    </transition>
    <button @click="handleChange">点击切换</button>
</div>

<script>
    Vue.component("child",{
        template:"<div>chile</div>"
    })
    Vue.component("child-one",{
        template:"<div>chile-on</div>"
    })

    var app = new Vue({
        el:'#app',
        data:{
            show: true
        },
        methods:{
            handleChange(){
                this.show= !this.show
            }
        }
    })
</script>


7.2. 利用动态组件的方式切换组件
<style>
    .fade-enter,.fade-leave-to{
        opacity:0
    }
    .fade-enter-active,.fade-leave-active{
        transition: opacity 1s;
    }
</style>
<div id="app">
    <transition name="fade" mode="out-in">
        <component :is="type"></component> 
    </transition>
    <button @click="handleChange">点击切换</button>
</div>

<script>
    Vue.component("child",{
        template:"<div>chile</div>"
    })
    Vue.component("child-one",{
        template:"<div>chile-on</div>"
    })

    var app = new Vue({
        el:'#app',
        data:{
            type: "child"
        },
        methods:{
            handleChange(){
                this.type = this.type=="child"?"child-one":"child"
            }
        }
    })
</script>


8. Vue中的列表过度

目前为止,关于过渡我们已经讲到:

  1. 单个节点
  2. 同一时间渲染多个节点中的一个

那么怎么同时渲染整个列表,比如使用 v-for?在这种场景中,使用 <transition-group> 组件。

特点:

  1. <transition>标签会以一个真实元素呈现:默认是 <span>。也可以通过 tag 属性 更换为其他元素。
  2. 过渡模式不可用,因为我们不再相互切换特有的元素。
  3. 内部元素总是需要提供唯一的 key 属性 值。
  4. CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。

示例如下:

vue中的列表过渡使用的是transition-group标签嵌套

<style>
    .fade-enter,.fade-leave-to{
        opacity:0
    }
    .fade-enter-active,.fade-leave-active{
        transition: opacity 1s;
    }
</style>
<div id="app">
    <transition-group name="fade">
        <div v-for="item in list" :key="item.id">
            {{item.title}}
        </div>
    </transition-group>

    <button @click="handleChange">点击新增</button>
</div>

<script>

    var app = new Vue({
        el:'#app',
        data:{
            count: 0,
            list: []
        },
        methods:{
            handleChange(){
                this.list.push({
                    id: this.count++,
                    title: "Hello World"
                })
            }
        }
    })
</script>

其实transition-group的原理就是给每一个列表循环的单独的元素套了一层transition标签,说白了,还是单个元素的过渡动画

就像这样

<transition>
  <div>Hello World</div>
</transition>
<transition>
  <div>Hello World</div>
</transition>
<transition>
  <div>Hello World</div>
</transition>
....

注意,for循环的key值尽量不要用index索引,会导致性能降低,其次项目功能上也会造成一定的影响,如果能不用index作为key值,尽量不要用index作为key值


9. 封装Vue动画

利用组件和slot的方式,将动画封装成一个组件,这样可以通过组件调用发方式复用相同动画

<style>
    .box{
        font-size:30px;
        font-weight:bold;
    }
    .fade-enter,.fade-leave-to{
        opacity:0
    }
    .fade-enter-active,.fade-leave-active{
        transition: all 3s
    }
</style>

<div id="app">
    <button @click="handleChange">点击切换</button>
    <fade :show="show" name="fade">
        <div class="box">Hello wold</div>
    </fade>
</div>

<!-- 组件模板 -->
<template id="myTransition">
    <transition 
                :name="name"
                >
        <slot v-if="show"></slot>
    </transition>
</template>

<script>
    // 定义全局组件
    Vue.component("fade",{
        props:["show","name"],
        template:`#myTransition`,
    })
    
    // Vue 实例
    const vm = new Vue({
        el:'#app',
        data:{
            show: true
        },
        methods:{
            handleChange(){
                this.show= !this.show
            }
        }
    })
</script>

当然你还可以继续进一步封装完善, 达到你想要的目的

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349