CoreAnimation之变换

CoreAnimation之CALayer基础

1. CGAffineTransform

  • CGAffineTransform基础

UIView有一个CGAffineTransform类型的属性,叫transform,用于在二维空间做旋转,缩放和平移。CALayer也有一个与之对应的属性叫affineTransform。CGAffineTransform所涉及的矩阵知识,这里有一篇文章说得很清楚: IOS矩阵之后的数学知识。CGPoint与CGAffineTransform的关系如图:

Paste_Image.png

用CGPoint的每一列和CGAffineTransform矩阵的每一行对应元素相乘再求和,就形成了一个新的CGPoint类型的结果。要解释一下图中显示的灰色元素,为了能让矩阵做乘法,左边矩阵的列数一定要和右边矩阵的行数个数相同,所以要给矩阵填充一些标志值,使得既可以让矩阵做乘法,又不改变运算结果,并且没必要存储这些添加的值,因为它们的值不会发生变化,但是要用来做运算。具体运算过程是这样的:
Paste_Image.png

当对图层应用变换矩阵,图层矩形内的每一个点都被相应地做变换,从而形成一个新的四边形的形状。CGAffineTransform中的“仿射”的意思是无论变换矩阵用什么值,图层中平行的两条线在变换之后任然保持平行,CGAffineTransform可以做出任意符合上述标注的变换。下图显示了一些仿射的和非仿射的变换:
Paste_Image.png

  • CGAffineTransform应用

CGAffineTransform的前缀CG即Core Graphics,Core Graphics提供了一系列函数,对完全没有数学基础的开发者也能够简单地做一些变换。如下几个函数都创建了一个CGAffineTransform实例:

/* Return a transform which translates by `(tx, ty)':
     t' = [ 1 0 0 1 tx ty ] */
CG_EXTERN CGAffineTransform CGAffineTransformMakeTranslation(CGFloat tx,
  CGFloat ty) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
/* Return a transform which scales by `(sx, sy)':
     t' = [ sx 0 0 sy 0 0 ] */
CG_EXTERN CGAffineTransform CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
  CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
/* Return a transform which rotates by `angle' radians:
     t' = [ cos(angle) sin(angle) -sin(angle) cos(angle) 0 0 ] */
CG_EXTERN CGAffineTransform CGAffineTransformMakeRotation(CGFloat angle)
  CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

举个栗子,点击屏幕,将子视图顺时针旋转45度:

#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController{
    UIView *layerView;
}
-(void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    layerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
    layerView.backgroundColor = [UIColor purpleColor];
    layerView.center = self.view.center;
    [self.view addSubview:layerView];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [UIView animateWithDuration:1.25f animations:^{
        CGAffineTransform transform = CGAffineTransformMakeRotation(45.0f/180.0f*M_PI);
        layerView.layer.affineTransform = transform;
    }];
}
@end

运行效果:


running.gif

π是弧度单位,其对应的角度为180度,Core Animation使用弧度最为参数
弧度转角度:

#define RADIANS_TO_DEGREES(x) ((x)/M_PI*180.0)

角度转弧度:

#define DEGREES_TO_RADIANS(x) ((x)/180.0*M_PI)
  • 复合变换

CGAffineTransformConcat方法可以混合两个已经存在的变换矩阵,在两个变换的基础上创建一个新的变换,举个栗子,修改上面的代码:

#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController{
    UIView *layerView;
}
-(void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    layerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
    layerView.backgroundColor = [UIColor purpleColor];
    layerView.center = self.view.center;
    [self.view addSubview:layerView];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [UIView animateWithDuration:1.25f animations:^{
        //CGAffineTransform transform = CGAffineTransformMakeRotation(45.0f/180.0f*M_PI);
        //layerView.layer.affineTransform = transform;
        
        CGAffineTransform rotation = CGAffineTransformMakeRotation(45.0f/180.0f*M_PI);
        CGAffineTransform scale = CGAffineTransformMakeScale(1.5f, 1.5f);
        
        CGAffineTransform transform = CGAffineTransformConcat(rotation, scale);
        layerView.layer.affineTransform = transform;
    }];
}
@end

运行效果:


running.gif

CGAffineTransformConcat方法只能接受两个参数,要做两种以上的复合变换的话,需要用到下面这些方法:

/* Translate `t' by `(tx, ty)' and return the result:
     t' = [ 1 0 0 1 tx ty ] * t */
CG_EXTERN CGAffineTransform CGAffineTransformTranslate(CGAffineTransform t,
  CGFloat tx, CGFloat ty) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
/* Scale `t' by `(sx, sy)' and return the result:
     t' = [ sx 0 0 sy 0 0 ] * t */
CG_EXTERN CGAffineTransform CGAffineTransformScale(CGAffineTransform t,
  CGFloat sx, CGFloat sy) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
/* Rotate `t' by `angle' radians and return the result:
     t' =  [ cos(angle) sin(angle) -sin(angle) cos(angle) 0 0 ] * t */
CG_EXTERN CGAffineTransform CGAffineTransformRotate(CGAffineTransform t,
  CGFloat angle) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

举个栗子,我们来用这些函数组合一个更加复杂的变换,先缩小50%,再顺时针旋转30度,最后向右移动200个像,继续修改上面的代码:

#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController{
    UIView *layerView;
}
-(void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    layerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
    layerView.backgroundColor = [UIColor purpleColor];
    layerView.center = self.view.center;
    [self.view addSubview:layerView];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [UIView animateWithDuration:1.25f animations:^{
        //CGAffineTransform transform = CGAffineTransformMakeRotation(45.0f/180.0f*M_PI);
        //layerView.layer.affineTransform = transform;
        
        //CGAffineTransform rotation = CGAffineTransformMakeRotation(45.0f/180.0f*M_PI);
        //CGAffineTransform scale = CGAffineTransformMakeScale(1.5f, 1.5f);
        
        //CGAffineTransform transform = CGAffineTransformConcat(rotation, scale);
        //layerView.layer.affineTransform = transform;
        
        CGAffineTransform transform = CGAffineTransformIdentity;
        transform = CGAffineTransformScale(transform, 0.5f, 0.5f);
        transform = CGAffineTransformRotate(transform, 30.0f/180.0f*M_PI);
        transform = CGAffineTransformTranslate(transform, 200.0f, 0.0f);
        layerView.layer.affineTransform = transform;
    }];
}
@end

运行效果:


running.gif

有些需要注意的地方:图片向右边发生了平移,但并没有指定距离那么远(200像素),另外它还有点向下发生了平移。原因在于当你按顺序做了变换,上一个变换的结果将会影响之后的变换,所以200像素的向右平移同样也被旋转了30度,缩小了50%,所以它实际上是斜向移动了100像素。这意味着变换的顺序会影响最终的结果,也就是说旋转之后的平移和平移之后的旋转结果可能不同。


2. CATransform3D

  • CATransform3D基础

affineTransform属性只能用来做2D变换,CALayer还有一个用来做3D变换的属性叫transform,其类型是CATransform3D类型。和CGAffineTransform类似,CATransform3D也是一个矩阵,但是和2x3的矩阵不同,CATransform3D是一个可以在3维空间内做变换的4x4的矩阵:


Paste_Image.png

我们对X轴和Y轴比较熟悉了,分别以右和下为正方向,Z轴和这两个轴分别垂直,指向视角外为正方向:


Paste_Image.png

由图所见,绕Z轴的旋转等同于之前二维空间的仿射旋转,但是绕X轴和Y轴的旋转就突破了屏幕的二维空间,并且在用户视角看来发生了倾斜。
  • CATransform3D应用

CATransform3D类型的矩阵,和Core Graphics的函数类似,但是3D的平移和旋转多处了一个z参数,并且旋转函数除了angle之外多出了x,y,z三个参数,分别决定了每个坐标轴方向上的旋转:

/* Returns a transform that translates by '(tx, ty, tz)':
 * t' =  [1 0 0 0; 0 1 0 0; 0 0 1 0; tx ty tz 1]. */
CA_EXTERN CATransform3D CATransform3DMakeTranslation (CGFloat tx,
    CGFloat ty, CGFloat tz)
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
/* Returns a transform that scales by `(sx, sy, sz)':
 * t' = [sx 0 0 0; 0 sy 0 0; 0 0 sz 0; 0 0 0 1]. */
CA_EXTERN CATransform3D CATransform3DMakeScale (CGFloat sx, CGFloat sy,
    CGFloat sz)
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
/* Returns a transform that rotates by 'angle' radians about the vector
 * '(x, y, z)'. If the vector has length zero the identity transform is
 * returned. */
CA_EXTERN CATransform3D CATransform3DMakeRotation (CGFloat angle, CGFloat x,
    CGFloat y, CGFloat z)
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);

举个栗子,点击屏幕将子视图旋转-45度:

#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController{
    UIView *layerView;
}
-(void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    layerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 150.0f, 150.0f)];
    layerView.center = self.view.center;
    layerView.layer.backgroundColor = [UIColor blueColor].CGColor;
    [self.view addSubview:layerView];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [UIView animateWithDuration:1.25f animations:^{
        CATransform3D transform = CATransform3DIdentity;
        transform = CATransform3DRotate(transform, -(45.0f/180.0f*M_PI), 0.0f, 1.0f, 0.0f);
        layerView.layer.transform = transform;
    }];
}
@end

运行效果:


running.gif

看起来好像只是宽度被压扁了而已,这是因为我们在用一个斜向的视角看它,而不是透视。

  • 透视效果

CATransform3D的透视效果通过一个矩阵中一个很简单的元素来控制:m34。m34用于按比例缩放X和Y的值来计算到底要离视角多远,其默认值是0,我们可以通过设置m34为-1.0 / d来应用透视效果,d代表了想象中视角相机和屏幕之间的距离,以像素为单位,那应该如何计算这个距离呢?实际上并不需要如何计算,大概估算一个就好了,一般是500-1000。修改上面的代码:

#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController{
    UIView *layerView;
}
-(void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    layerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 150.0f, 150.0f)];
    layerView.center = self.view.center;
    layerView.layer.backgroundColor = [UIColor blueColor].CGColor;
    [self.view addSubview:layerView];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [UIView animateWithDuration:1.25f animations:^{
        CATransform3D transform = CATransform3DIdentity;
        transform.m34 = -1.0f / 500.0f;
        transform = CATransform3DRotate(transform, -(45.0f/180.0f*M_PI), 0.0f, 1.0f, 0.0f);
        layerView.layer.transform = transform;
    }];
}
@end

运行效果:


running.gif
  • 灭点

当在透视角度绘图的时候,远离相机视角的物体将会变小变远,当远离到一个极限距离,它们可能就缩成了一个点,于是所有的物体最后都汇聚消失在同一个点,这个点叫灭点。

Core Animation定义了灭点位于变换图层的anchorPoint,这就是说,当图层发生变换时,这个点永远位于图层变换之前anchorPoint的位置。当改变一个图层的position,你也改变了它的灭点,做3D变换的时候要时刻记住这一点,当你视图通过调整m34来让它更加有3D效果,应该首先把它放置于屏幕中央,然后通过平移来把它移动到指定位置(而不是直接改变它的position),这样所有的3D图层都共享一个灭点。

  • sublayerTransform

如果有多个子视图或者子图层,每个都做3D变换,那就需要分别设置相同的m34值,并且确保在变换之前都在屏幕中央共享同一个position,这样操作起来很麻烦,CALayer有一个叫做sublayerTransform的属性可以解决这个问题。

sublayerTransform也是CATransform3D类型,但和对一个图层的变换不同,它影响到所有的子图层,这意味着你可以一次性对包含这些图层的容器做变换。

使用sublayerTransform,灭点被设置在容器图层的中点,从而不需要再对子图层分别设置了。这意味着你可以随意使用position和frame来放置子图层,而不需要把它们放置在屏幕中点,然后为了保证统一的灭点用变换来做平移。

  • doubleSided

CALayer有一个叫做doubleSided的属性来控制图层的背面是否要被绘制,这是一个BOOL类型,默认为YES,如果设置为NO,那么当图层正面从相机视角消失的时候,它将不会被绘制。举个栗子:

#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController{
    UIView *topView;
    UIView *bottomView;
}
-(void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    UIImage *image = [UIImage imageNamed:@"3"];
    
    topView = [[UIView alloc] initWithFrame:CGRectMake(85.0f, 100.0f, 150.0f, 150.0f)];
    bottomView = [[UIView alloc] initWithFrame:CGRectMake(85.0f, 300.0f, 150.0f, 150.0f)];
    
    bottomView.layer.doubleSided = NO;
    
    [self configView:topView image:image];
    [self configView:bottomView image:image];
}
-(void)configView:(UIView *)aView image:(UIImage *)aImage{
    aView.layer.backgroundColor = [UIColor greenColor].CGColor;
    aView.layer.contents = (__bridge id _Nullable)(aImage.CGImage);
    aView.layer.contentsGravity = kCAGravityResizeAspect;
    aView.layer.contentsScale = [UIScreen mainScreen].scale;
    [self.view addSubview:aView];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [UIView animateWithDuration:5.0f animations:^{
        CATransform3D transform = CATransform3DIdentity;
        transform = CATransform3DRotate(transform, M_PI, 0.0f, 1.0f, 0.0f);
        topView.layer.transform = transform;
        bottomView.layer.transform = transform;
    }];
}
@end

运行效果:


running.gif

3. 固体对象

现在我们懂得了在3D空间的一些图层布局的基础,接下来试着创建一个固态的3D对象(实际上是一个技术上所谓的空洞对象,但它以固态呈现),我们用三个独立的视图来构建一个残缺的正方体

Interface Build布局如图:

Paste_Image.png

代码:

#import "TestViewController.h"
@interface TestViewController ()
@property (nonatomic, weak) IBOutlet UIView *containerView;
@property (nonatomic, strong) IBOutletCollection(UIView) NSArray *faces;
@end

@implementation TestViewController

- (void)addFace:(NSInteger)index withTransform:(CATransform3D)transform{
    //get the face view and add it to the container
    UIView *face = self.faces[index];
    [self.containerView addSubview:face];
    //center the face view within the container
    CGSize containerSize = self.containerView.bounds.size;
    face.center = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0);
    // apply the transform
    face.layer.transform = transform;
}

- (void)viewDidLoad{
    [super viewDidLoad];
    //set up the container sublayer transform
    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = -1.0 / 500.0;
    self.containerView.layer.sublayerTransform = perspective;
    //add cube face 1
    CATransform3D transform = CATransform3DMakeTranslation(0, 0, 100);
    [self addFace:0 withTransform:transform];
    //add cube face 2
    transform = CATransform3DMakeTranslation(100, 0, 0);
    transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
    [self addFace:1 withTransform:transform];
    //add cube face 3
    transform = CATransform3DMakeTranslation(0, -100, 0);
    transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);
    [self addFace:2 withTransform:transform];
}
@end

运行效果:

Paste_Image.png

从这个角度看正方体并不是很明显,看起来只是一个方块,为了更好地欣赏它,我们将更换一个不同的视角

但是旋转这个正方体将会显得很笨重,因为我们要单独对每个面做旋转,这时候我们之前说的sublayerTransform就派上了用场

添加两行代码去旋转containerView图层的perspective变换矩阵:

    perspective = CATransform3DRotate(perspective, -M_PI_4, 1, 0, 0);
    perspective = CATransform3DRotate(perspective, -M_PI_4, 0, 1, 0);

把相机(或者相对相机的整个场景)绕Y轴旋转45度,并且绕X轴旋转45度,现在从另一个角度去观察正方体,就能看出它的真实面貌:

Paste_Image.png

效果出来了,但是我还想旋转一下这个正方体,看看其他的面,怎么办呢

so easy!绕y轴旋转咯

说干就干,修改代码:

#import "TestViewController.h"
@interface TestViewController ()
@property (nonatomic, weak) IBOutlet UIView *containerView;
@property (nonatomic, strong) IBOutletCollection(UIView) NSArray *faces;
@end

@implementation TestViewController{
    CATransform3D rotateTransform;
}

- (void)addFace:(NSInteger)index withTransform:(CATransform3D)transform{
    //get the face view and add it to the container
    UIView *face = self.faces[index];
    [self.containerView addSubview:face];
    //center the face view within the container
    CGSize containerSize = self.containerView.bounds.size;
    face.center = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0);
    // apply the transform
    face.layer.transform = transform;
}

- (void)viewDidLoad{
    [super viewDidLoad];
    //set up the container sublayer transform
    
    rotateTransform = CATransform3DIdentity;
    rotateTransform.m34 = -1.0f / 500.0f;
    
    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = -1.0 / 500.0;
    perspective = CATransform3DRotate(perspective, -M_PI_4, 1, 0, 0);
    perspective = CATransform3DRotate(perspective, -M_PI_4, 0, 1, 0);
    self.containerView.layer.sublayerTransform = perspective;

    //add cube face 1
    CATransform3D transform = CATransform3DMakeTranslation(0, 0, 100);
    [self addFace:0 withTransform:transform];
    //add cube face 2
    transform = CATransform3DMakeTranslation(100, 0, 0);
    transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
    [self addFace:1 withTransform:transform];
    //add cube face 3
    transform = CATransform3DMakeTranslation(0, -100, 0);
    transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);
    [self addFace:2 withTransform:transform];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [NSTimer scheduledTimerWithTimeInterval:0.01f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
}

-(void)timerAction{
    static CGFloat angle = 1.0f;
    rotateTransform = CATransform3DRotate(rotateTransform, angle/180.0f*M_PI, 0.0f, 1.0f, 0.0f);
    self.containerView.layer.transform = rotateTransform;
}

@end

跑起来:

running.gif

纳尼,这是什么鬼👻

这是由于尽管Core Animation图层存在于3D空间之内,但它们并不都存在同一个3D空间。每个图层的3D场景其实是扁平化的,当你从正面观察一个图层,看到的实际上由子图层创建的想象出来的3D场景,但当你倾斜这个图层,你会发现实际上这个3D场景仅仅是被绘制在图层的表面。

总之一句话,图层是扁平的,直接把superLayer绕y轴旋转的方法行不通

要想实现旋转正方体的效果,就得将所有的子图层全部挨个儿做变换

到最后还得用sublayerTransform

继续修改代码:

#import "TestViewController.h"
@interface TestViewController ()
@property (nonatomic, weak) IBOutlet UIView *containerView;
@property (nonatomic, strong) IBOutletCollection(UIView) NSArray *faces;
@end

@implementation TestViewController{
//    CATransform3D rotateTransform;
}

- (void)addFace:(NSInteger)index withTransform:(CATransform3D)transform{
    //get the face view and add it to the container
    UIView *face = self.faces[index];
    [self.containerView addSubview:face];
    //center the face view within the container
    CGSize containerSize = self.containerView.bounds.size;
    face.center = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0);
    // apply the transform
    face.layer.transform = transform;
}

- (void)viewDidLoad{
    [super viewDidLoad];
    //set up the container sublayer transform
    
//    rotateTransform = CATransform3DIdentity;
//    rotateTransform.m34 = -1.0f / 500.0f;
    
    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = -1.0 / 500.0;
    perspective = CATransform3DRotate(perspective, -M_PI_4, 1, 0, 0);
    perspective = CATransform3DRotate(perspective, -M_PI_4, 0, 1, 0);
    self.containerView.layer.sublayerTransform = perspective;
    
    //add cube face 1
    CATransform3D transform = CATransform3DMakeTranslation(0, 0, 100);
    [self addFace:0 withTransform:transform];
    //add cube face 2
    transform = CATransform3DMakeTranslation(100, 0, 0);
    transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
    [self addFace:1 withTransform:transform];
    //add cube face 3
    transform = CATransform3DMakeTranslation(0, -100, 0);
    transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);
    [self addFace:2 withTransform:transform];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [NSTimer scheduledTimerWithTimeInterval:0.01f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
}

-(void)timerAction{
    static CGFloat angle = 1.0f;
    CATransform3D transform3d = self.containerView.layer.sublayerTransform;
    transform3d = CATransform3DRotate(transform3d, angle/180.0f*M_PI, 0.0f, 1.0f, 0.0f);
    self.containerView.layer.sublayerTransform = transform3d;
    
//    rotateTransform = CATransform3DRotate(rotateTransform, angle/180.0f*M_PI, 0.0f, 1.0f, 0.0f);
//    self.containerView.layer.transform = rotateTransform;
}

@end

最终运行效果:

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

推荐阅读更多精彩内容