iOS - Core Animation 死亡笔记:初窥

你若盛开,蝴蝶自来(图片来自网络)

简介:
Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,使用它能做出非常炫丽的动画效果,而且往往是事半功倍。也就是说,使用少量的代码就可以实现非常强大的功能。
关于Core Animation完整的详细资料,这里有一份详细的介绍:http://blog.csdn.net/mad2man/article/details/16928891(由海水的味道翻译)

图层是Core Animation的核心,那么什么是CALayer

  • OC中,基本上所有的控件都是UIView,而UIView之所以能够在屏幕上显示,因为每一个UIView内部都默认关联一个CALayer.
  • UIView将要显示到屏幕上的时候,会调用drawRect:进行绘图,将所有的内容绘制在自己的图层上,绘制完毕后,系统会将图层copy到屏幕上.其实也就是说UIView本身不具备显示的功能,是因为有了Layer才有显示的功能,所以UIView和CALayer相互依赖.

一、什么是隐式动画

  • Core Animation基于一个假设,说屏幕上的任何东西都可以(或者可能)做动画。动画并不需要你在Core Animation中手动打开,相反需要明确地关闭,否则他会一直存在。
  • 所有手动创建的CALayer对象,都存在隐式动画,关于layer的属性,例如:
    1.bounds:用于设置CALayer的宽高.修改属性会产生缩放动画
    2.backgroundColor:设置背景色,会产生背景色的渐变动画
    3.position:位置的改变,默认会产生平移动画

tips:以上都是隐式动画

#import "ViewController.h"

@interface ViewController ()
@property (nonatomic,strong) CALayer *layer; 
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 手动创建layer
    self.layer = [CALayer layer];
    self.layer.frame = CGRectMake(0, 0, 200, 200);
    self.layer.backgroundColor = [UIColor blackColor].CGColor;
    [self.view.layer addSublayer:self.layer];
}
// 点击的时候更改layer的位置.(用于测试是否会产生隐式动画)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    self.layer.frame = CGRectMake(100, 100, 200, 200);
}
@end

那么如何关闭隐式动画呢?
CALayer隐式动画实际上是自动执行了CATransaction,CATransaction默认动画时间0.25s

// 点击的时候更改layer的位置.(用于测试是否会产生隐式动画)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [CATransaction begin];
    [CATransaction setDisableActions:YES];
        self.layer.frame = CGRectMake(100, 100, 200, 200);
    [CATransaction commit];
}

效果图:

左:未关闭隐式动画<br>右:关闭隐式动画

将隐式动画时间改成2s,并改变layer的背景颜色

  • 通过+ setAnimationDuration:方法设置当前动画时间,或者通过+ animationDuration方法来获取值(默认0.25秒)
    [CATransaction begin];
    // 改变动画时间:设置为2s
    [CATransaction setAnimationDuration:2.0f];
    [CATransaction setDisableActions:NO];
    self.layer.frame = CGRectMake(100, 100, 200, 200);
    self.layer.backgroundColor = [UIColor yellowColor].CGColor;
    [CATransaction commit];

基于UIView的block的动画允许你在动画结束的时候提供一个完成的动作。CATranscation接口提供的+ setCompletionBlock:方法也有同样的功能

    [CATransaction setCompletionBlock:^{
        NSLog(@"完成了");
    }];
    [CATransaction commit];
    NSLog(@"将要完成");


二、显式动画

  • 隐式动画是在iOS平台创建动态用户界面的一种直接方式,也是UIKit动画机制的基础,不过它并不能涵盖所有的动画类型。
  • 显式动画,它能够对一些属性做指定的自定义动画,或者创建非线性动画,比如沿着任意一条曲线移动。

* 属性动画 - CAPropertyAnimation

属性动画作用于图层的某个单一属性,并指定了它的一个目标值,或者一连串将要做动画的值。属性动画分为两种:基础和关键帧。

1. 基础动画 - CABasicAnimation
动画其实就是一段时间内发生的改变,最简单的形式就是从一个值改变到另一个值.
CABasicAnimation 继承 CAPropertyAnimation, 而CAPropertyAnimation是c所有动画类型的一个抽象基类
CAPropertyAnimation 通过指定动画的 KeyPath 作用于一个单一的属性.

类型的转换
id obj = @(float) // CGFloat 类型转换
id obj = [NSValue valueWithCGPoint:point] // CGPoint
id obj = [NSValue valueWithCGsize:point] // CGsize
id obj = [NSValue valueWithCGPoint:point] // CGPoint
id obj = [NSValue valueWithCGRect:point] // CGRect

CABasicAnimation 继承 CAPropertyAnimation,添加了如下属性:
@property(nullable, strong) id fromValue;             // 动画开始前属性值
@property(nullable, strong) id toValue;              // 动画结束后属性值
@property(nullable, strong) id byValue;             // 动画过程中改变的值

/**
* tips: 这里的 id 类型 可以作用于不同的属性,比如:'数字','矢量','颜色','图片'
* 根据官方的头文件说明:这三个组合属性 可以有很多方式去组合.但是不能一次性同时指定'三个值',只需要指定'fromValue 和 toValue 或 byValue'
**/

  • 根据官方的头文件说明:这三个组合属性 可以有很多方式去组合.但是不能一次性同时指定'三个值',只需要指定'fromValue 和 toValue 或 byValue'
  • 用于CAPropertyAnimation的一些类型转换


基础动画的属性详解
  *****基础动画的keyPath******
  transform.rotation.x 围绕x轴翻转 参数:角度 angle2Radian(4) 
  transform.rotation.y 围绕y轴翻转 参数:同上 
  transform.rotation.z 围绕z轴翻转 参数:同上 
  transform.rotation 默认围绕z轴 
  transform.scale.x x方向缩放 参数:缩放比例 1.5 
  transform.scale.y y方向缩放 参数:同上 
  transform.scale.z z方向缩放 参数:同上 
  transform.scale 所有方向缩放 参数:同上 
  transform.translation.x x方向移动 参数:x轴上的坐标 100 
  transform.translation.y x方向移动 参数:y轴上的坐标 
  transform.translation.z x方向移动 参数:z轴上的坐标 
  transform.translation 移动 参数:移动到的点 (100,100) 
  opacity 透明度 参数:透明度 0.5 
  backgroundColor 背景颜色 参数:颜色 (id)[[UIColor redColor] CGColor] 
  cornerRadius 圆角 参数:圆角半径 5 
  borderWidth 边框宽度 参数:边框宽度 5 
  bounds 大小 参数:CGRect 
  contents 内容 参数:CGImage 
  contentsRect 可视内容 参数:CGRect 值是0~1之间的小数 
  hidden 是否隐藏 
  position 
  shadowColor 
  shadowOffset 
  shadowOpacity 
  shadowRadius
                            '通过CABasicAnimation来设置图层背景色'
#import "ViewController.h"

@interface ViewController ()
@property (nonatomic,strong) CALayer *layer;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];

    self.layer = [CALayer layer];
    self.layer.frame = CGRectMake(50, 50, 200, 200);
    self.layer.backgroundColor = [UIColor blackColor].CGColor;
    [self.view.layer addSublayer:self.layer];
 }

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    CABasicAnimation *animation = [CABasicAnimation animation];
    animation.duration = 3.f;
'   /* 效果发现:一旦动画结束并从图层上移除之后,layer就立刻恢复到之前定义的外观状态。我们从没改变过backgroundColor属性,所以图层就返回到原始的颜色。
     * 如果需要动画完成后,layer的颜色不恢复到之前的外观,则需要加上下面这两行代码
     */

    // animation.fromValue = (__bridge id _Nullable)(self.layer.backgroundColor);
    // self.layer.backgroundColor = [UIColor redColor].CGColor;
'   
    animation.keyPath = @"backgroundColor";
    animation.toValue = (__bridge id _Nullable)([UIColor redColor].CGColor);
    [self.layer addAnimation:animation forKey:nil];
}
@end

有小伙伴提出怎么在动画结束后保持效果,在这里感谢:"霸气侧漏"的这位蜀黍提供的代码;

    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;
  • CAAnimationDelegate

我们可以看到有两个方法.那么,优化上面的代码



- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
    [CATransaction begin];
    [CATransaction setDisableActions:YES];
    self.layer.backgroundColor =(__bridge CGColorRef)((CABasicAnimation *)anim).toValue;
    [CATransaction commit];
}
  • 关键帧动画 - CAKeyframeAnimation

CAKeyframeAnimation是另一种UIKit没有暴露出来但功能强大的类。和CABasicAnimation类似,CAKeyframeAnimation同样是CAPropertyAnimation的一个子类,它依然作用于单一的一个属性,但是和CABasicAnimation不一样的是,它不限制于设置一个起始和结束的值,而是可以根据一连串随意的值来做动画。

使用CAKeyframeAnimation 做颜色变化

@interface ViewController ()
@property (nonatomic,strong) CALayer *layer;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.layer = [CALayer layer];
    self.layer.frame = CGRectMake(50, 50, 200, 200);
    self.layer.backgroundColor = [UIColor blackColor].CGColor;
    [self.view.layer addSublayer:self.layer];    
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"backgroundColor";
    animation.duration = 2.0f;
    animation.values = @[(__bridge id)[UIColor redColor].CGColor,
                         (__bridge id)[UIColor orangeColor].CGColor,
                         (__bridge id)[UIColor yellowColor].CGColor,
                         (__bridge id)[UIColor greenColor].CGColor,
                         (__bridge id)[UIColor cyanColor].CGColor,
                         (__bridge id)[UIColor blueColor].CGColor,
                         (__bridge id)[UIColor purpleColor].CGColor];
    [self.layer addAnimation:animation forKey:nil];
}

CAKeyframeAnimation不能自动把当前值作为第一帧(类似1于1CABasicAnimation把fromValue设为nil),所以为了动画的平滑特性,我们需要开始和结束的关键帧来匹配当前属性的值

CAKeyframeAnimation

CAKeyframeAnimation有另一种方式去指定动画,就是使用CGPath.可以结合UIBezierpath创建路径,进行做动画
Tips:关于UIBezierPath,可以参考我上一篇:iOS-UIBezierPath和各种layer把我玩坏了

  • 根据UIBezierPath进行Animation
@interface ViewController ()
@property (nonatomic,strong) UIView *bezierPathView;
@property (nonatomic,strong) UIBezierPath *path;
@end

@implementation ViewController


- (void)viewDidLoad {
    [super viewDidLoad];

    self.bezierPathView = [[UIView alloc] initWithFrame:(CGRectMake(10, 40, 80, 80))];
    self.bezierPathView.layer.cornerRadius = 50;
    self.bezierPathView.layer.masksToBounds = YES;
    self.bezierPathView.backgroundColor = [UIColor yellowColor];
    [self.view addSubview:self.bezierPathView];
    
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:(CGPointMake(40, 80))];
    [path addLineToPoint:(CGPointMake(100, 300))];
    [path addCurveToPoint:CGPointMake(300, 300) controlPoint1:(CGPointMake(200, 0)) controlPoint2:(CGPointMake(325, 500))];
    self.path = path;
    
    CAShapeLayer *shaperLayer = [CAShapeLayer layer];
    shaperLayer.path = path.CGPath;
    shaperLayer.fillColor = [UIColor clearColor].CGColor;
    shaperLayer.strokeColor = [UIColor purpleColor].CGColor;
    path.lineWidth = 2.0f;
    [self.view.layer addSublayer:shaperLayer];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"position";
    animation.duration = 5.0f;
    animation.path = _path.CGPath;
    // 通过rotation自动对齐图层到曲线
#warning 如果不适用自动对齐,效果不会太逼真.可以自己尝试效果
    animation.rotationMode = kCAAnimationRotateAuto;
    [self.bezierPathView.layer addAnimation:animation forKey:nil];
}

这样一个路径图就构造出来了,_

CAKeyframeAnimation构造路径图
  • 虚拟属性
  • 属性动画实际上是针对关键路径而不是一个键,这就意味着可以对子属性甚至是虚拟属性进行做动画.
  • 什么是虚拟属性?
    如果想要对一个物体做旋转动画,应该用到transform属性,因为CALayer没有显示提供角度或者方向之类的属性

利用transform对layer做动画

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic,strong) UIView *animationView;
@property (nonatomic,strong) CALayer *layer;

@end

@implementation ViewController


- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.animationView = [[UIView alloc] initWithFrame:(CGRectMake(10, 40, 300, 300))];
    self.animationView.backgroundColor = [UIColor cyanColor];
    [self.view addSubview:self.animationView];
    
    CALayer *layer= [CALayer layer];
    layer.frame = CGRectMake(0, 0, 200, 200);
    layer.position = CGPointMake(150, 150);
    layer.contents = (__bridge id)[UIImage imageNamed:@"1.jpg"].CGImage;
    [self.animationView.layer addSublayer:layer];
    self.layer = layer;
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    CABasicAnimation *animtion = [CABasicAnimation animation];
    animtion.keyPath = @"transform";
    animtion.duration = 3.0f;
    animtion.toValue = [NSValue valueWithCATransform3D:(CATransform3DMakeRotation(M_PI, 0, 0, 1))];
    [self.layer addAnimation:animtion forKey:nil];
    
}
rotation

这里如果我们把M_PI(180°)调整到M_PI2(360°),就会发现怎么都不会动了.因为:toValue的结果是360°,又回到了最初的状态,所以值根本没有变,没有动画效果,那么如何改变呢?*

  • *改变 : 对 transform.rotaion 进行动画 *
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    CABasicAnimation *animtion = [CABasicAnimation animation];
    animtion.keyPath = @"transform.rotation";
    animtion.duration = 3.0f;
//    animtion.toValue = [NSValue valueWithCATransform3D:(CATransform3DMakeRotation(M_PI * 2, 0, 0, 1))];
    animtion.byValue = @(M_PI * 2);
    [self.layer addAnimation:animtion forKey:nil];
}

为什么用transform.rotation?

  1. 我们可以不通过关键帧一次旋转多余180°的动画
  • 可以用相对值 (byValue)
  • 可以用不用CATransform3D
  • 不会和transform.positon以及transform.scale冲突,完全分开进行独立的动画
transform.rotation
  • 组动画 - CAAnimationGroup

CAAnimationGroup可以把上面所述的动画类型进行组合在一起.

  • CAAnimationGroup 也是继承于CAAnimtion的子类
  • CAAnimationGroup 添加了 animaitons 属性

组合 CAKeyframeAnimtionCABasicAnimation

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic,strong) UIView *animationView;
@property (nonatomic,strong) CALayer *layer;
@property (nonatomic,strong) CAAnimationGroup *group;
@end

@implementation ViewController


- (void)viewDidLoad
{
    [super viewDidLoad];
    
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:(CGPointMake(50, 400))];
    [path addCurveToPoint:(CGPointMake(300, 100)) controlPoint1:(CGPointMake(200, 0)) controlPoint2:CGPointMake(245, 350)];
    
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.lineWidth = 2.0f;
    shapeLayer.strokeColor = [UIColor brownColor].CGColor;
    shapeLayer.fillColor = [UIColor clearColor].CGColor;
    shapeLayer.path = path.CGPath;
    [self.view.layer addSublayer:shapeLayer];
    

    self.layer = [CALayer layer];
    self.layer.frame = CGRectMake(0, 0, 80, 80);
    self.layer.position =CGPointMake(100, 150);
    self.layer.cornerRadius = 40;
    self.layer.masksToBounds = YES;
    self.layer.backgroundColor = (__bridge CGColorRef _Nullable)((__bridge id)[UIColor whiteColor].CGColor);
    ;
    [self.view.layer addSublayer:self.layer];
    
    CALayer *imgLayer = [CALayer layer];
    imgLayer.frame = CGRectMake(0, 0, 50, 50);
    imgLayer.position = CGPointMake(40, 40);
    imgLayer.contents = (__bridge id)[UIImage imageNamed:@"1.jpg"].CGImage;
    [self.layer addSublayer:imgLayer];
    
    // position
    CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animation];
    keyAnimation.keyPath = @"position";
    keyAnimation.path = path.CGPath;
    keyAnimation.rotationMode = kCAAnimationRotateAuto;
    
    // transform.rotation
    CABasicAnimation *basicAnimaiton = [CABasicAnimation animation];
    basicAnimaiton.keyPath = @"transform.rotation";
    basicAnimaiton.byValue = @(M_PI * 2);
    
    // opacity
    CAKeyframeAnimation *alphaAnimation = [CAKeyframeAnimation animation];
    alphaAnimation.keyPath = @"opacity";
    alphaAnimation.values = @[@0.3,@0.5,@0.8,@0.1,@.4,@.9,@0.1,@.5,@.2,@.7,@.3,@1.0];
    
    // backgroupColors
    CAKeyframeAnimation *colorAnimtion = [CAKeyframeAnimation alloc];
    colorAnimtion.keyPath = @"backgroundColor";
    colorAnimtion.values = @[(__bridge id)[UIColor whiteColor].CGColor,
                             (__bridge id)[UIColor redColor].CGColor,
                             (__bridge id)[UIColor orangeColor].CGColor,
                             (__bridge id)[UIColor yellowColor].CGColor,
                             (__bridge id)[UIColor greenColor].CGColor,
                             (__bridge id)[UIColor cyanColor].CGColor,
                             (__bridge id)[UIColor blueColor].CGColor,
                             (__bridge id)[UIColor purpleColor].CGColor,
                             (__bridge id)[UIColor whiteColor].CGColor];
    
    //groupAnimation
    CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
    groupAnimation.animations = @[keyAnimation,alphaAnimation,colorAnimtion,basicAnimaiton];
    groupAnimation.duration = 5.0;
    
    self.group = groupAnimation;
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self.layer addAnimation:self.group forKey:nil];
}
CAAnimtionGroup
  • 过渡动画 - CATransition
  • CATransition 同样继承于 CAAnimation
  • CATransition 在 API中已经有很清楚的说明了.他有一个type,subtype来标记变换效果

简单的imageView加载图片

@interface ViewController ()

@property (nonatomic,strong) UIImageView *imageView;
@property (nonatomic,strong) NSArray *imageArr;

@end

@implementation ViewController


- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.imageView = [[UIImageView alloc] initWithFrame:(CGRectMake(100, 100, 100, 100))];
    [self.view addSubview:self.imageView];
    self.imageView.image = [UIImage imageNamed:@"1.jpg"];
    
    self.imageArr = @[[UIImage imageNamed:@"1.jpg"],
                      [UIImage imageNamed:@"2.jpg"],
                      [UIImage imageNamed:@"3.jpg"],
                      [UIImage imageNamed:@"4.jpg"]];
    
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    CATransition *transition = [CATransition animation];
    transition.type = kCATransitionFade;
    [self.imageView.layer addAnimation:transition forKey:nil];
    
    UIImage *current = self.imageView.image;
    NSUInteger index = [self.imageArr indexOfObject:current];
    index = (index + 1) % self.imageArr.count;
    self.imageView.image = self.imageArr[index];
}

imageView加载图片
  • 隐式过渡
    当设置了CALayer的content属性的时候,CATransition是默认的行为。但对于UIView关联的图层,或者是其他隐式动画,这个特性依然是被禁用的,但是对于自己创建的Layerlayer.contents图片都会自动附上Fade的动画。
  • 使用UIKit提供的动画方法做过渡动画
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    UIImage *current = self.imageView.image;
    NSUInteger index = [self.imageArr indexOfObject:current];
    index = (index + 1) % self.imageArr.count;
    
    [UIView transitionWithView:self.imageView duration:1.0 options:(UIViewAnimationOptionTransitionFlipFromTop) animations:^{
        self.imageView.image = self.imageArr[index];
    } completion:NULL];
}
UIKit

Tips: 最初的梦想,紧握在手上,让我们在最美的时光里,做最好的自己!

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

推荐阅读更多精彩内容