Flutter了解之入门篇13-1(动画)

  1. 内置动画组件
  2. 转换动画(通常命名为xxTransition)

内置动画组件

1. AnimatedPadding(缩放效果,改变padding)
  AnimatedPadding({ 
    Key? key,
    required this.padding,  // 不允许为负,否则异常
    this.child,
    Curve curve = Curves.linear,
    required Duration duration,
    VoidCallback? onEnd,
  });

2. AnimatedPositioned(配合Stack使用,改变定位)
  AnimatedPositioned({
    Key? key,
    required this.child,
    this.left,
    this.top,
    this.right,
    this.bottom,
    this.width,
    this.height,
    // 相比Positioned组件多了如下3个属性
    Curve curve = Curves.linear,
    required Duration duration,
    VoidCallback? onEnd,
  }) 

3. AnimatedOpacity (图片渐现过渡效果,改变透明度)
  AnimatedOpacity({
    Key? key,
    this.child,
    required this.opacity,  // 0-1
    Curve curve = Curves.linear,
    required Duration duration,  // 持续时间
    VoidCallback? onEnd,  // 动画结束后的回调
    this.alwaysIncludeSemantics = false,  // 默认是 false。用于辅助访问,如果是 true,则不管透明度是多少,都会显示语义信息(可以辅助朗读)。
  }) 

4. AnimatedAlign(改变alignment)

5. AnimatedContainer(改变container属性)
  AnimatedContainer({
    Key? key,
    this.alignment,
    this.padding,
    Color? color,
    Decoration? decoration,
    this.foregroundDecoration,
    double? width,
    double? height,
    BoxConstraints? constraints,
    this.margin,
    this.transform,
    this.transformAlignment,
    this.child,
    this.clipBehavior = Clip.none,
    // 相比Container多了如下3个属性
    Curve curve = Curves.linear,  // 动画曲线,默认是线性
    required Duration duration,  // 持续时间
    VoidCallback? onEnd,  // 动画结束后的回调
  });

6. AnimatedDefaultTextStyle(改变字体样式)
  AnimatedDefaultTextStyle({
    Key? key,
    required this.child,
    required this.style,
    this.textAlign,
    this.softWrap = true,
    this.overflow = TextOverflow.clip,
    this.maxLines,
    this.textWidthBasis = TextWidthBasis.parent,
    this.textHeightBehavior,
    Curve curve = Curves.linear,
    required Duration duration,
    VoidCallback? onEnd,
  })

7. AnimatedDecoratedBox

示例(AnimatedOpacity图片渐显过渡,一张图逐渐消失另一张图逐渐显示)

class _SwtichImageDemoState extends State<SwtichImageDemo> {
  var opacity1 = 1.0;
  var opacity2 = 0.0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('图片切换'),
        brightness: Brightness.dark,
        backgroundColor: Colors.black,
      ),
      backgroundColor: Colors.black,
      body: Center(
        child: Stack(
          alignment: Alignment.center,
          children: [
            AnimatedOpacity(
              duration: Duration(milliseconds: 5000),
              opacity: opacity1,
              child: ClipOval(
                child: Image.asset(
                  'images/beauty.jpeg',
                  width: 300,
                  height: 300,
                ),
              ),
              curve: Curves.ease,
            ),
            AnimatedOpacity(
              duration: Duration(milliseconds: 5000),
              opacity: opacity2,
              child: ClipOval(
                child: Image.asset(
                  'images/beauty2.jpeg',
                  width: 300,
                  height: 300,
                ),
              ),
              curve: Curves.ease,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Text(
          '变',
          style: TextStyle(
            color: Colors.white,
          ),
          textAlign: TextAlign.center,
        ),
        onPressed: () {
          setState(() {
            opacity1 = 0.0;
            opacity2 = 1.0;
          });
        },
      ),
    );
  }
}

示例(AnimatedPositioned 火箭发射)

class RocketLaunch extends StatefulWidget {
  RocketLaunch({Key? key}) : super(key: key);
  @override
  _RocketLaunchState createState() => _RocketLaunchState();
}
class _RocketLaunchState extends State<RocketLaunch> {
  var rocketBottom = -80.0;
  var rocketWidth = 160.0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('火箭发射'),
        brightness: Brightness.dark,
        backgroundColor: Colors.black,
      ),
      backgroundColor: Colors.black,
      body: Center(
        child: Stack(
          alignment: Alignment.bottomCenter,
          children: [
            Image.asset(
              'images/earth.jpeg',
              height: double.infinity,
              fit: BoxFit.fill,
            ),
            AnimatedPositioned(
              child: Image.asset(
                'images/rocket.png',
                fit: BoxFit.fitWidth,
              ),
              bottom: rocketBottom,
              width: rocketWidth,
              duration: Duration(seconds: 5),
              curve: Curves.easeInCubic,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Text(
          '发射',
          style: TextStyle(
            color: Colors.white,
          ),
          textAlign: TextAlign.center,
        ),
        onPressed: () {
          setState(() {
            rocketBottom = MediaQuery.of(context).size.height;
            rocketWidth = 40.0;
          });
        },
      ),
    );
  }
}

示例(AnimatedPadding、AnimatedPositioned、AnimatedAlign、AnimatedContainer、AnimatedDefaultTextStyle、AnimatedDecoratedBox)

import 'package:flutter/material.dart';
class AnimatedWidgetsTest extends StatefulWidget {
  @override
  _AnimatedWidgetsTestState createState() => _AnimatedWidgetsTestState();
}
class _AnimatedWidgetsTestState extends State<AnimatedWidgetsTest> {
  double _padding = 10;
  var _align = Alignment.topRight;
  double _height = 100;
  double _left = 0;
  Color _color = Colors.red;
  TextStyle _style = TextStyle(color: Colors.black);
  Color _decorationColor = Colors.blue;
  @override
  Widget build(BuildContext context) {
    var duration = Duration(seconds: 5);
    return SingleChildScrollView(
      child: Column(
        children: <Widget>[
          RaisedButton(
            onPressed: () {
              setState(() {
                _padding = 20;
              });
            },
            child: AnimatedPadding(
              duration: duration,
              padding: EdgeInsets.all(_padding),
              child: Text("AnimatedPadding"),
            ),
          ),
          SizedBox(
            height: 50,
            child: Stack(
              children: <Widget>[
                AnimatedPositioned(
                  duration: duration,
                  left: _left,
                  child: RaisedButton(
                    onPressed: () {
                      setState(() {
                        _left = 100;
                      });
                    },
                    child: Text("AnimatedPositioned"),
                  ),
                )
              ],
            ),
          ),
          Container(
            height: 100,
            color: Colors.grey,
            child: AnimatedAlign(
              duration: duration,
              alignment: _align,
              child: RaisedButton(
                onPressed: () {
                  setState(() {
                    _align = Alignment.center;
                  });
                },
                child: Text("AnimatedAlign"),
              ),
            ),
          ),
          AnimatedContainer(
            duration: duration,
            height: _height,
            color: _color,
            child: FlatButton(
              onPressed: () {
                setState(() {
                  _height = 150;
                  _color = Colors.blue;
                });
              },
              child: Text(
                "AnimatedContainer",
                style: TextStyle(color: Colors.white),
              ),
            ),
          ),
          AnimatedDefaultTextStyle(
            child: GestureDetector(
              child: Text("hello world"),
              onTap: () {
                setState(() {
                  _style = TextStyle(
                    color: Colors.blue,
                    decorationStyle: TextDecorationStyle.solid,
                    decorationColor: Colors.blue,
                  );
                });
              },
            ),
            style: _style,
            duration: duration,
          ),
          AnimatedDecoratedBox(
            duration: duration,
            decoration: BoxDecoration(color: _decorationColor),
            child: FlatButton(
              onPressed: () {
                setState(() {
                  _decorationColor = Colors.red;
                });
              },
              child: Text(
                "AnimatedDecoratedBox",
                style: TextStyle(color: Colors.white),
              ),
            ),
          )
        ].map((e) {
          return Padding(
            padding: EdgeInsets.symmetric(vertical: 16),
            child: e,
          );
        }).toList(),
      ),
    );
  }
}

转换动画(通常命名为xxTransition)

苹果风格的全屏转换动效
CupertinoFullscreenDialogTransition({
  Key? key,
  required Animation<double> primaryRouteAnimation,
  required Animation<double> secondaryRouteAnimation,
  required this.child,
  required bool linearTransition,
}) 
/*
看一下CupertinoFullscreenDialogTransition的build方法:
// 使用了两个SlideTransition实现该动效。
Widget build(BuildContext context) {
  assert(debugCheckHasDirectionality(context));
  final TextDirection textDirection = Directionality.of(context);
  return SlideTransition(
    position: _secondaryPositionAnimation,
    textDirection: textDirection,
    transformHitTests: false,
    child: SlideTransition(
      position: _positionAnimation,
      child: child,
    ),
  );
}
*/

横向转换(可实现抽屉效果)
CupertinoPageTransition({
  Key? key,
  required Animation<double> primaryRouteAnimation,
  required Animation<double> secondaryRouteAnimation,
  required this.child,
  required bool linearTransition,
})

更改 子组件的外框的特性来实现动效
DecoratedBoxTransition

滑动转换
SlideTransition({  // AnimatedWidget的子类
  Key? key,
  // 使用AnimationController控制,是一个比例偏移。
  // new_x = width * dx; new_y = height * dy;
  // 如果想让组件从左边滑入,可以设置dx为负值。
  required Animation<Offset> position, 
  this.transformHitTests = true,
  this.textDirection,
  this.child,
})

旋转转换
RotationTransition({
  Key? key, 
  required Animation<double> turns, 
  Alignment alignment, 
  FilterQuality? filterQuality, 
  Widget? child
})

尺寸转换
SizeTransition({
  Key? key,
  this.axis = Axis.vertical,  // vertical则更改高度;horizontal则更改宽度。
  required Animation<double> sizeFactor, // 控制组件尺寸变化的 Animation 对象,乘数。
  this.axisAlignment = 0.0, // 子组件的对齐位置,默认为0.0从中间开始更改尺寸(横向时可实现卷轴从中间向两边打开效果)。当axis为vertical时,-1.0代表顶部对齐开始动画(即尺寸从上到下开始变大);当 axis 为horizontal 时,开始的方向和文本的反向有关(TextDirection.ltr 还是 TextDirection.rtl),当文本为从左到右时(TextDirection.ltr,默认),-1.0表示从左侧开始动画(即尺寸从左到右开始变大)。
  this.child,
}) 

缩放转换
ScaleTransition({
  Key? key,
  required Animation<double> scale,
  this.alignment = Alignment.center,
  this.child,
})

更改组件在Stack中的位置
PositionedTransition

渐显转换
FadeTransition

示例(FadeTransition 渐显转换)

Widget build(BuildContext context) {
  return Container(
    color: Colors.white,
    child: FadeTransition(
      opacity: _animation,
      child: const Padding(padding: EdgeInsets.all(8), child: FlutterLogo()),
    ),
  );
}

示例(PositionedTransition 更改组件在Stack中的位置)

@override
Widget build(BuildContext context) {
  const double smallLogo = 100;
  const double bigLogo = 200;
  return LayoutBuilder(
    builder: (BuildContext context, BoxConstraints constraints) {
      final Size biggest = constraints.biggest;
      return Stack(
        children: <Widget>[
          PositionedTransition(
            rect: RelativeRectTween(
              begin: RelativeRect.fromSize(
                  const Rect.fromLTWH(0, 0, smallLogo, smallLogo), biggest),
              end: RelativeRect.fromSize(
                  Rect.fromLTWH(biggest.width - bigLogo,
                      biggest.height - bigLogo, bigLogo, bigLogo),
                  biggest),
            ).animate(CurvedAnimation(
              parent: _controller,
              curve: Curves.elasticInOut,
            )),
            child: const Padding(
                padding: EdgeInsets.all(8), child: FlutterLogo()),
          ),
        ],
      );
    },
  );
}

示例(DecoratedBoxTransition 更改外框)

class _MyStatefulWidgetState extends State<MyStatefulWidget>
    with TickerProviderStateMixin {
  final DecorationTween decorationTween = DecorationTween(
    begin: BoxDecoration(
      color: const Color(0xFFFFFFFF),
      border: Border.all(style: BorderStyle.none),
      borderRadius: BorderRadius.circular(60.0),
      shape: BoxShape.rectangle,
      boxShadow: const <BoxShadow>[
        BoxShadow(
          color: Color(0x66666666),
          blurRadius: 10.0,
          spreadRadius: 3.0,
          offset: Offset(0, 6.0),
        )
      ],
    ),
    end: BoxDecoration(
      color: const Color(0xFFFFFFFF),
      border: Border.all(
        style: BorderStyle.none,
      ),
      borderRadius: BorderRadius.zero,
      // No shadow.
    ),
  );
  late final AnimationController _controller = AnimationController(
    vsync: this,
    duration: const Duration(seconds: 3),
  )..repeat(reverse: true);
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      child: Center(
        child: DecoratedBoxTransition(
          position: DecorationPosition.background,
          decoration: decorationTween.animate(_controller),
          child: Container(
            width: 200,
            height: 200,
            padding: const EdgeInsets.all(10),
            child: const FlutterLogo(),
          ),
        ),
      ),
    );
  }
}

示例(SlideTransition 滑动)

// 一张图片左侧滑入,一张图片右侧划出。
class SlideTransitionDemo extends StatefulWidget {
  SlideTransitionDemo({Key? key}) : super(key: key);
  @override
  _SlideTransitionDemoState createState() => _SlideTransitionDemoState();
}
class _SlideTransitionDemoState extends State<SlideTransitionDemo>
    with SingleTickerProviderStateMixin {
  bool _forward = true;
  final begin = Offset.zero;
  // 第一张图片结束位置移出右侧屏幕
  final end1 = Offset(1.1, 0.0);
  // 第二张图片的初始位置在左侧屏幕
  final begin2 = Offset(-1.1, 0.0);
  late Tween<Offset> tween1 = Tween(begin: begin, end: end1);
  late Tween<Offset> tween2 = Tween(begin: begin2, end: begin);
  late AnimationController _controller =
      AnimationController(duration: const Duration(seconds: 1), vsync: this);
  // 使用自定义曲线动画过渡效果
  late Animation<Offset> _animation1 = tween1.animate(
    CurvedAnimation(
      parent: _controller,
      curve: Curves.easeInOut,
    ),
  );
  late Animation<Offset> _animation2 = tween2.animate(CurvedAnimation(
    parent: _controller,
    curve: Curves.easeInOut,
  ));
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('SlideTransition'),
        brightness: Brightness.dark,
        backgroundColor: Colors.black,
      ),
      backgroundColor: Colors.black,
      body: Center(
        child: Container(
          padding: EdgeInsets.all(10.0),
          child: Stack(
            children: [
              SlideTransition(
                child: ClipOval(
                  child: Image.asset('images/beauty.jpeg'),
                ),
                position: _animation1,
              ),
              SlideTransition(
                child: ClipOval(
                  child: Image.asset('images/beauty2.jpeg'),
                ),
                position: _animation2,
              ),
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.swap_horizontal_circle_sharp),
        onPressed: () {
          setState(() {
            if (_forward) {
              _controller.forward();
            } else {
              _controller.reverse();
            }
            _forward = !_forward;
          });
        },
      ),
    );
  }
}

示例(SizeTransition)

// 图片从左向右飞入
class SizeTransitionDemo extends StatefulWidget {
  SizeTransitionDemo({Key? key}) : super(key: key);
  @override
  _SizeTransitionDemoState createState() => _SizeTransitionDemoState();
}
class _SizeTransitionDemoState extends State<SizeTransitionDemo>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller =
      AnimationController(duration: const Duration(seconds: 3), vsync: this)
        ..repeat();
  late Animation<double> _animation = CurvedAnimation(
      parent: _controller, curve: Curves.fastLinearToSlowEaseIn);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('SizeTransition'),
        brightness: Brightness.dark,
        backgroundColor: Colors.blue,
      ),
      body: SizeTransition(
        child: Center(
          child: Image.asset(
            'images/superman.png',
            width: 300.0,
            height: 300.0,
          ),
        ),
        sizeFactor: _animation,
        axis: Axis.horizontal,
        axisAlignment: 1.0,
      ),
    );
  }
  @override
  void dispose() {
    _controller.stop();
    _controller.dispose();
    super.dispose();
  }
}

示例(ScaleTransition)

class ScaleTransitionDemo extends StatefulWidget {
  ScaleTransitionDemo({Key? key}) : super(key: key);
  @override
  _ScaleTransitionDemoState createState() => _ScaleTransitionDemoState();
}
class _ScaleTransitionDemoState extends State<ScaleTransitionDemo>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller =
      AnimationController(duration: const Duration(seconds: 10), vsync: this)
        ..repeat();
  late Animation<double> _animation =
      CurvedAnimation(parent: _controller, curve: Curves.easeOut);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('ScaleTransition'),
        brightness: Brightness.dark,
        backgroundColor: Colors.blue,
      ),
      body: Center(
        child: balloon(),
      ),
    );
  }
  @override
  void dispose() {
    _controller.stop();
    _controller.dispose();
    super.dispose();
  }
  Widget balloon() {
    return ScaleTransition(
      alignment: Alignment.bottomCenter,
      child: Image.asset(
        'images/balloon.png',
      ),
      scale: _animation,
    );
  }
}

其他

Transform 组件

对子组件进行转换操作

定义如下:
Transform({
    Key? key,
    required this.transform,  // 一个Matrix4 对象,用于定义三维空间的变换操作。
    this.origin,  // 一个坐标偏移量,实际会加入到 Matrix4 的 translation(平移)中。
    this.alignment,  // 转变进行的参考方位
    this.transformHitTests = true,
    Widget? child,  // 
  }) : assert(transform != null),
       super(key: key, child: child);

TweenAnimationBuilder组件(由用户触发动画)

TweenAnimationBuilder({
  Key? key,
  required this.tween,  // Twee<T>类型,动画过程中会把 Tween 的中间插值传给 builder 来构建子组件,从而可以实现过渡动画效果。
  required Duration duration,
  Curve curve = Curves.linear,
  // 构建组件。value参数为tween动画过程中的中间插值,动画期间会不断调用builder重新绘制子组件。从源码中可看出初始化时tween起始值和结束值不一致就会启动动画。
  // typedef ValueWidgetBuilder<T> = Widget Function(BuildContext context, T value, Widget? child);
  required this.builder,  
  VoidCallback? onEnd,
  this.child,
}) 

示例(滤镜)

class TweenAnimationDemo extends StatefulWidget {
  TweenAnimationDemo({Key? key}) : super(key: key);
  @override
  _TweenAnimationDemoState createState() => _TweenAnimationDemoState();
}
class _TweenAnimationDemoState extends State<TweenAnimationDemo> {
  var _sliderValue = 0.0;
  Color _newColor = Colors.orange;
  @override
  void initState() {
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('TweenAnimationBuilder'),
        brightness: Brightness.dark,
        backgroundColor: Colors.black,
      ),
      backgroundColor: Colors.black,
      body: Center(
        child: Column(
          children: [
            TweenAnimationBuilder(
              tween: ColorTween(
                begin: Colors.white,
                end: _newColor,
              ),
              duration: Duration(seconds: 1),
              builder: (_, color, child) {
                // 对子组件的每一个像素进行颜色过滤。实际上是插入了一个颜色层,从而看起来有滤镜效果。
                return ColorFiltered(  
                  colorFilter:
                      ColorFilter.mode(color as Color, BlendMode.modulate),
                  child: ClipOval(
                    child: ClipOval(
                      child: Image.asset(
                        'images/beauty.jpeg',
                        width: 300,
                      ),
                    ),
                  ),
                );
              },
            ),
            Slider.adaptive(
              value: _sliderValue,
              onChanged: (value) {
                setState(() {
                  _sliderValue = value;
                });
              },
              onChangeEnd: (value) {
                setState(() {
                  _newColor = _newColor.withRed((value * 255).toInt());
                });
              },
            )
          ],
        ),
      ),
    );
  }
}

AnimatedModelBarrier

ModalBarrier 的替换,可以挡住它下层的组件使得这些组件无法与用户交互,并且在组件上加一层颜色动画过渡遮罩。

AnimatedModalBarrier({  
  Key? key,
  required Animation<Color?> color,
  bool dismissible,  // 为true时点击遮罩会退出当前页返回到上一页
  String? semanticsLabel,
  bool? barrierSemanticsDismissible
})

AnimatedPhysicalModel

控制组件的阴影、颜色、边框圆弧等物理模型,但组件自身的形状不发生改变

AnimatedPhysicalModel({
  Key? key,
  required Widget child,
  required BoxShape shape,
  Clip clipBehavior,
  BorderRadius borderRadius,
  required double elevation,
  required Color color,
  bool animateColor,
  required Color shadowColor,
  bool animateShadowColor,
  Curve curve = Curves.linear,
  required Duration duration,
  VoidCallback? onEnd
})

示例

更改elevation 属性实现Z 轴阴影的变化

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('AnimatedPhysicalModel 动画'),
    ),
    body: Center(
      child: AnimatedPhysicalModel(
        child: Container(
          width: 300,
          height: 300,
        ),
        duration: Duration(seconds: 1),
        color: _elevation == 0.0 ? Colors.blue : Colors.green,
        animateColor: true,
        animateShadowColor: true,
        elevation: _elevation,
        shape: BoxShape.circle,
        shadowColor: Colors.blue[900]!,
        curve: Curves.easeInOutCubic,
      ),
    ),
    floatingActionButton: FloatingActionButton(
      child: Text(
        'Play',
        style: TextStyle(
          color: Colors.white,
        ),
        textAlign: TextAlign.center,
      ),
      onPressed: () {
        setState(() {
          _elevation = _elevation == 0 ? 10.0 : 0.0;
        });
      },
    ),
  );
}

AnimatedSize

Widget build(BuildContext context) {
  return GestureDetector(
    onTap: () => _updateSize(),
    child: Container(
      color: Colors.amberAccent,
      child: AnimatedSize(
        curve: Curves.easeIn,
        duration: const Duration(seconds: 1),
        child: FlutterLogo(size: _size),
      ),
    ),
  );
}

三方库

  1. animations 三方库
1. Container Transform
  转场时将两个页面的元素联系使得转场更为平滑。
  类似Hero动画。
2. Shared Axis
  共享轴,适用于UI元素之间有空间或引导联系的场景,通过在x,y或z轴的转换,实现界面之间的联系来进行动画过渡。
3. Fade Through
  通过快速渐现和消失来实现没有关联UI界面的切换,避免突兀。
4. Fade
  弹窗动效。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容