從CoreAnimation到Pop

pop是Facebook在開源的一款動(dòng)畫引擎据某,看下其官方的介紹:

Pop是一款在iOS墩朦、tvOS和OS X平臺(tái)通用的可擴(kuò)展動(dòng)畫引擎勺阐。它在基本靜態(tài)動(dòng)畫的基礎(chǔ)上,增加了彈性以及衰減動(dòng)畫盾似,這在創(chuàng)建真實(shí)有物里性的交互很有用敬辣。其API能夠快速的整合進(jìn)已有的Objective-C工程,可以對(duì)任意對(duì)象的任意屬性做動(dòng)畫。這是一個(gè)成熟且經(jīng)過測(cè)試的框架溉跃,在Paper這款優(yōu)秀的app中有廣泛的應(yīng)用村刨。(iOS7之后蘋果也提供了Spring動(dòng)畫(不過CASpringAnimation iOS9才提供)以及UIDynamic物理引擎(比如碰撞以及重力等物理效果不錯(cuò),有興趣可以玩玩))

那Pop動(dòng)畫引擎跟CoreAnimation有啥區(qū)別撰茎?我們先來簡單了解一下蘋果的CoreAnimation:

CoreAnimation

先看下CoreAnimation在框架中所處的位置:

CoreAnimation.png

可以看出視圖的渲染以及動(dòng)畫都是基于CoreAnimation框架(看名字容易以為只是動(dòng)畫相關(guān))嵌牺,其地位還是相當(dāng)重要。我們來看下iOS在視圖的渲染以及動(dòng)畫的各個(gè)階段都發(fā)生了蝦米龄糊,這其中涉及到應(yīng)用內(nèi)部以及應(yīng)用外部:

應(yīng)用內(nèi)部4個(gè)階段:

  • 布局
    這個(gè)階段是用戶在程序內(nèi)部設(shè)置組織視圖或圖層的關(guān)系,比如設(shè)置view的backgroundColor枯饿、frame等屬性诡必;

  • 顯示
    這是圖層的寄宿圖片被繪制的階段,比如實(shí)現(xiàn)了-drawRect:或-drawLayer:inContext:方法搔扁,這些方法會(huì)這這個(gè)階段執(zhí)行,這些繪制方法是由CPU在應(yīng)用內(nèi)部同步地完成稿蹲,屬于離屏渲染。

  • 準(zhǔn)備
    這個(gè)階段苛聘,CoreAnimation框架會(huì)將渲染視圖的各種屬性以及動(dòng)畫的參數(shù)等數(shù)據(jù)準(zhǔn)備好涂炎;同時(shí)這個(gè)階段還會(huì)解壓需要渲染的image。

  • 提交
    這是在應(yīng)用內(nèi)部發(fā)生的最后階段唱捣,CoreAnimation打包準(zhǔn)備好的所有視圖/圖層以及動(dòng)畫的屬性网梢,然后通過IPC(進(jìn)程間通信)發(fā)送到render server進(jìn)行顯示,可以看到其實(shí)視圖的渲染以及動(dòng)畫是在另外一個(gè)進(jìn)程處理的拣宰。在iOS5和之前的版本是SpringBoard進(jìn)程(同時(shí)管理著iOS的主屏)烦感,在iOS6之后的版本中叫做BackBoard。

應(yīng)用外部2個(gè)階段:
一旦這些打包好的數(shù)據(jù)到達(dá)render server手趣,這些數(shù)據(jù)會(huì)被反序列化成另一個(gè)叫做渲染樹的圖層樹,根據(jù)這個(gè)樹狀結(jié)構(gòu)气笙,render server做如下工作:

  • 根據(jù)layer的屬性值,如果圖層包含動(dòng)畫缸棵,則計(jì)算其屬性的中間插值谭期,然后設(shè)置OpenGL幾何形狀(紋理化的三角形)來執(zhí)行渲染
  • 在屏幕上渲染可見的三角形

所以整個(gè)階段包含六個(gè)階段,如果有動(dòng)畫踏志,最后兩個(gè)階段會(huì)重復(fù)的執(zhí)行胀瞪。前五個(gè)階段都是通過CPU處理的,只有最后一個(gè)階段使用GPU凄诞。而且你能控制的只有前面兩個(gè)階段:布局和顯示,剩下都是CoreAnimation框架在內(nèi)部進(jìn)行處理伪朽。

簡單了解完CoreAnimaton的工作方式之后汛蝙,我們?cè)趤砜纯磒op實(shí)現(xiàn)動(dòng)畫的方式。

pop

CADisplayLink是一個(gè)和屏幕刷新率(每秒60幀)相同的定時(shí)器坚洽,pop實(shí)現(xiàn)的動(dòng)畫就是基于該定時(shí)器西土,它在每一幀計(jì)根據(jù)指定的time function計(jì)算出動(dòng)畫的中間值,然后將計(jì)算好的值賦給視圖或圖層(可以是任意對(duì)象)的屬性(比如透明度绘雁、frame等)援所,當(dāng)屬性發(fā)生變化之后,我們知道Core Animation會(huì)通過IPC把這些變化通知render server進(jìn)行渲染挪略,因此整個(gè)動(dòng)畫過程變成是你的應(yīng)用內(nèi)部驅(qū)動(dòng)的,render server則被動(dòng)接受數(shù)據(jù)進(jìn)行渲染挽牢,跟上面提到的Core Animation動(dòng)畫方式有所不同摊求;另一個(gè)不同是pop在動(dòng)畫過程中改變的是model layer的狀態(tài),不像Core Animation作用的是渲染樹的圖層樹室叉,Core Animation動(dòng)畫會(huì)在動(dòng)畫結(jié)束后回到起始位置茧痕, model layer, presentation layer 和 render layer的區(qū)別有興趣可以去了解。

core_animation_basics_sublayer_hierarchies.png
Animate View

pop提供了幾種動(dòng)畫曼氛,包括basic令野、Spring(彈簧)舀患、Deacy(衰減)以及自定義的動(dòng)畫


pop animation

其API跟Core Animation提供的API類似彩掐,我們來看看如何使用pop灰追,包括以下幾個(gè)步驟:

// 1 選擇動(dòng)畫類型 (POPBasicAnimation  POPSpringAnimation POPDecayAnimation)
POPSpringAnimation *springAnimation = [POPSpringAnimation animation];
springAnimation.springBounciness=16;
springAnimation.springSpeed=6;

// 2 選擇要對(duì)視圖或者圖層的屬性做動(dòng)畫,比如我們想要縮放動(dòng)畫朴下,我們可以選擇:kPOPViewScaleXY苦蒿。
//pop提供了一些屬性佩迟,包括視圖屬性:kPOPViewAlpha kPOPViewBackgroundColor kPOPViewBounds kPOPViewCenter kPOPViewFrame等,
//圖層屬性:kPOPLayerBackgroundColor kPOPLayerBounds kPOPLayerScaleXY kPOPLayerSize kPOPLayerOpacity kPOPLayerPosition等灸姊,具體可以查看POPAnimatableProperty.m文件
springAnimation.property = [POPAnimatableProperty propertyWithName:kPOPViewScaleXY];

// 3 設(shè)置動(dòng)畫的終點(diǎn)值
springAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(1.3, 1.3)];

// 4 為動(dòng)畫指定代理POPAnimatorDelegate(可選)秉溉,
springAnimation.delegate = self;

// 5 將動(dòng)畫添加到視圖或圖層中碗誉,開始做動(dòng)畫
[_testView pop_addAnimation:springAnimation forKey:@"springAnimation"];

可以看到API與Core Animation的基本類似哮缺,熟悉的同學(xué)應(yīng)該能很快使用上甲喝,具體的使用方式可以嘗試,比如Spring動(dòng)畫的幾個(gè)參數(shù)的效果茎匠,實(shí)踐出真知~

Animate NSObject

pop除了可以對(duì)view或著layer做動(dòng)畫之外押袍,還可以對(duì)任意NSObject對(duì)象的屬性做動(dòng)畫,其實(shí)動(dòng)畫本質(zhì)上也是離散的汽馋,當(dāng)每秒內(nèi)離散的數(shù)據(jù)足夠多的時(shí)候?qū)τ谌搜蹃碚f就是連續(xù)的圈盔。因此對(duì)NSObject對(duì)象屬性做動(dòng)畫本質(zhì)上也是計(jì)算出一系列的離散值驱敲,比如對(duì)下面的對(duì)象做動(dòng)畫,然后我們可以根據(jù)這些離散值來觀察pop的動(dòng)畫曲線:

@interface AnimatableObject : NSObject
@property (nonatomic,assign) CGFloat propertyValue;
@end

@implementation AnimatableObject

- (void)setPropertyValue:(CGFloat)newValue{
    _propertyValue = newValue;
}

@end

上面的對(duì)象包含一個(gè)float類型的屬性握牧,由于這個(gè)對(duì)象的屬性并不是pop提供的內(nèi)建屬性(POPAnimatableProperty.mm中定義的)娩梨,因此我們需要?jiǎng)?chuàng)建一個(gè)新的動(dòng)畫屬性POPAnimatableProperty:

POPAnimatableProperty *valueProperty = [POPAnimatableProperty propertyWithName:@"value" initializer:^(POPMutableAnimatableProperty *prop) {
    prop.writeBlock=^(id obj, const CGFloat values[]) {
        [obj setPropertyValue:values[0]];
        [_values addObject:@(values[0])]; //收集值用于后面繪制觀察曲線
    };
    prop.readBlock = ^(id obj, CGFloat values[]) {
        values[0] = [obj propertyValue];
    };
}];

我們需要為這個(gè)動(dòng)畫屬性提供名稱以及writeBlock跟readBlock狈定,block里面定義如何將數(shù)值與對(duì)象屬性關(guān)聯(lián),現(xiàn)在我們對(duì)這個(gè)對(duì)象做動(dòng)畫并繪制相關(guān)的動(dòng)畫曲線措嵌。
我們對(duì)object做basic動(dòng)畫芦缰,采用easeInOut的時(shí)間函數(shù):

POPBasicAnimation *animation = [POPBasicAnimation animation];
animation.property = valueProperty;
animation.fromValue = [NSNumber numberWithFloat:0];
animation.toValue = [NSNumber numberWithFloat:100];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.duration = 1.5;
animation.completionBlock = ^(POPAnimation *anim, BOOL finished){
    [self drawCurl:_values];
};
_animateObject = [[AnimatableObject alloc] init];
[_animateObject pop_addAnimation:animation forKey:@"easeInEaseOut"];

//根據(jù)獲取到的值來繪制曲線
-(void)drawCurl:(NSArray*)values
{
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(100, 350)];
    
    for (int i=0; i<[values count]; i++) {
        NSNumber *value = values[i];
        CGPoint point = CGPointZero;
        point.x = 100+i*(100/values.count);
        point.y = 350 - [value floatValue];
        [path addLineToPoint:point];
    }
    
    _layer.path = path.CGPath;
    [self.view.layer addSublayer:_layer];
}

可以看到繪制出如下的曲線:

easeInEaseOut

假如使用PopSpringAnimation做動(dòng)畫:

POPSpringAnimation *springAni = [POPSpringAnimation animation];
springAni.property = valueProperty;
springAni.fromValue = [NSNumber numberWithFloat:0];
springAni.toValue = [NSNumber numberWithFloat:100];
springAni.dynamicsMass = 5;
springAni.completionBlock = ^(POPAnimation *anim, BOOL finished){
    [self drawCurl:_values];
};
_animateObject = [[AnimatableObject alloc] init];
[_animateObject pop_addAnimation:springAni forKey:@"springAnimation"];

可以看到是如下曲線饺藤,有興趣可以自己是試試其它曲線。

spring

實(shí)現(xiàn)原理

簡單了解完pop的使用方式罗丰,我們來繼續(xù)聊一聊pop的實(shí)現(xiàn)方式萌抵,為了方便說明簡單分析下面的pop動(dòng)畫,移動(dòng)view的x位置:

POPBasicAnimation *basicAnimation = [POPBasicAnimation animation];
basicAnimation.property = [POPAnimatableProperty propertyWithName:kPOPLayerPositionX];
basicAnimation.toValue = @(200);
[_testView pop_addAnimation:basicAnimation forKey:nil];
  • pop內(nèi)建屬性

kPOPLayerPositionX是pop內(nèi)建的屬性霎桅,pop內(nèi)置了常見的屬性動(dòng)畫讨永,保存在全局的靜態(tài)數(shù)組_staticStates[]中,對(duì)每個(gè)屬性定義好了讀取屬性值readBlock以及寫入屬性值的writeBlock(如果是自定義的屬性揭糕,則需要自己實(shí)現(xiàn)readBlock和writeBlock锻霎,如之前所示),

static POPStaticAnimatablePropertyState _staticStates[] =
{
    ...
    {kPOPLayerPositionX,
        ^(CALayer *obj, CGFloat values[]) {
          values[0] = [(CALayer *)obj position].x;
        },
        ^(CALayer *obj, const CGFloat values[]) {
          CGPoint p = [(CALayer *)obj position];
          p.x = values[0];
          [obj setPosition:p];
        },
        kPOPThresholdPoint
    },
    ...
 }
  • POPAnimator
    pop的動(dòng)畫都是交給POPAnimator執(zhí)行的吏口,POPAnimator是一個(gè)負(fù)責(zé)執(zhí)行動(dòng)畫單例對(duì)象产徊,這個(gè)對(duì)象會(huì)開啟一個(gè)CADisplayLink定時(shí)器冬殃,該定時(shí)器會(huì)在每幀執(zhí)行動(dòng)畫:
//POPAnimator.mm
- (id)init
{
    ...
    _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render)];
    _displayLink.paused = YES;
    [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
    ...
}

可以發(fā)現(xiàn)定時(shí)器是加到runloop的NSRunLoopCommonModes模式中的叁怪,這樣即便是UI滑動(dòng)的時(shí)候也不會(huì)影響動(dòng)畫的執(zhí)行奕谭。

當(dāng)我們使用pop_addAnimation把定義好的動(dòng)畫加到POPAnimator對(duì)象時(shí):

- (void)addAnimation:(POPAnimation *)anim forObject:(id)obj key:(NSString *)key
{
  ...
  //POPAnimator會(huì)先判斷該動(dòng)畫對(duì)象是否存在(所有動(dòng)畫會(huì)保存在內(nèi)部的一個(gè)字典對(duì)象中)了,如果存在就不重復(fù)添加執(zhí)行動(dòng)畫
  NSMutableDictionary *keyAnimationDict = (__bridge id)CFDictionaryGetValue(_dict, (__bridge void *)obj);
  if (nil == keyAnimationDict) {
    keyAnimationDict = [NSMutableDictionary dictionary];
    CFDictionarySetValue(_dict, (__bridge void *)obj, (__bridge void *)keyAnimationDict);
  } else {
    POPAnimation *existingAnim = keyAnimationDict[key];
    if (existingAnim) {
      if (existingAnim == anim) {
        return;
      }
      [self removeAnimationForObject:obj key:key cleanupDict:NO];
    }
  }
  keyAnimationDict[key] = anim

  // 將動(dòng)畫保存在_pendingList數(shù)組中
  _pendingList.push_back(item);

  // 開啟CADisplayLink定時(shí)器
  updateDisplayLink(self);

  //執(zhí)行_pendingList數(shù)組中的動(dòng)畫
  [self _scheduleProcessPendingList];
}

  • 基于NSRunLoop的動(dòng)畫更新機(jī)制
    當(dāng)我們有動(dòng)畫需要被執(zhí)行時(shí)官册,pop會(huì)在主線層的runloop中添加觀察者难捌,監(jiān)聽kCFAllocatorDefault、kCFRunLoopBeforeWaiting和kCFRunLoopExit事件员淫,并在回調(diào)的時(shí)候處理執(zhí)行_pendingList里的動(dòng)畫
- (void)_scheduleProcessPendingList
{
  ...

  if (!_pendingListObserver) {
    __weak POPAnimator *weakSelf = self;
    _pendingListObserver = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting | kCFRunLoopExit, false, POPAnimationApplyRunLoopOrder, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
      ...
      //在回調(diào)中執(zhí)行_pendingList中的動(dòng)畫
      CFTimeInterval time = [self _currentRenderTime];
      [self _renderTime:(0 != _beginTime) ? _beginTime : time items:_pendingList];
      ...
    });

    if (_pendingListObserver) {
      CFRunLoopAddObserver(CFRunLoopGetMain(), _pendingListObserver,  kCFRunLoopCommonModes);
    }
  }
  ...
}
  • 渲染 pending 動(dòng)畫
    當(dāng)runloop觀察者的回調(diào)被執(zhí)行時(shí)介返,POPAnimator會(huì)根據(jù)當(dāng)前時(shí)間(需要這個(gè)時(shí)間去做插值)一個(gè)一個(gè)執(zhí)行_pendingList里的動(dòng)畫:
- (void)_renderTime:(CFTimeInterval)time item:(POPAnimatorItemRef)item
{
    ...
    // 只執(zhí)行有效的動(dòng)畫
    if (state->active && !state->paused) {
      
      //根據(jù)當(dāng)前時(shí)間執(zhí)行動(dòng)畫
      applyAnimationTime(obj, state, time);

      //如果動(dòng)畫執(zhí)行完畢
      if (state->isDone()) {

        //將計(jì)算好的值設(shè)給視圖或圖層對(duì)象
        applyAnimationToValue(obj, state);
        }
    }
    ...
}

static void applyAnimationTime(id obj, POPAnimationState *state, CFTimeInterval time)
{
    //根據(jù)當(dāng)前時(shí)間計(jì)算推倒出新的值大小
    if (!state->advanceTime(time, obj)) {
        return;
    }
    POPPropertyAnimationState *ps = dynamic_cast<POPPropertyAnimationState*>(state);
    if (NULL != ps) {

        //將推倒出的新值作用到視圖或圖層對(duì)象
        updateAnimatable(obj, ps);
    }
}

pop會(huì)根據(jù)動(dòng)畫類型做不同的插值算法圣蝎,如下所示可以看到有四種不同的插值方式

bool advanceTime(CFTimeInterval time, id obj) {
    ...
    switch (type) {
      case kPOPAnimationSpring:
        advanced = advance(time, dt, obj);
        break;
      case kPOPAnimationDecay:
        advanced = advance(time, dt, obj);
        break;
      case kPOPAnimationBasic: {
        advanced = advance(time, dt, obj);
        computedProgress = true;
        break;
      }
      case kPOPAnimationCustom: {
        customFinished = [self _advance:obj currentTime:time elapsedTime:dt] ? false : true;
        advanced = true;
        break;
      }
     ...
}

我們以kPOPAnimationBasic方式為例,

bool advance(CFTimeInterval time, CFTimeInterval dt, id obj) {
    
    //默認(rèn)采用kCAMediaTimingFunctionDefault時(shí)間函數(shù)
    ((POPBasicAnimation *)self).timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];

    // 將時(shí)間歸一化到[0-1]
    CGFloat p = 1.0f;
    if (duration > 0.0f) {
        // cap local time to duration
        CFTimeInterval t = MIN(time - startTime, duration) / duration;
        p = POPTimingFunctionSolve(timingControlPoints, t, SOLVE_EPS(duration));
        timeProgress = t;
    } else {
        timeProgress = 1.;
    }

    //根據(jù)當(dāng)前的時(shí)間牲证,以及from和to的值計(jì)算出新的當(dāng)前值
    interpolate(valueType, valueCount, fromVec->data(), toVec->data(), currentVec->data(), p);
    progress = p;

 }

計(jì)算出新的值后从隆,便可以通過內(nèi)建屬性定義好的writeBlock將新的值付給UI對(duì)象:

static void updateAnimatable(id obj, POPPropertyAnimationState *anim, bool shouldAvoidExtraneousWrite = false)
{
    pop_animatable_write_block write = anim->property.writeBlock;
     if (NULL == write)
        return;
    write(obj, currentVec->data());
}

pop動(dòng)畫的過程大體上如上所示缭裆,也就是在每一幀將通過不同的曲線函數(shù)計(jì)算出新的插值并賦給UI對(duì)象,以此來實(shí)現(xiàn)動(dòng)畫

custom animation

下面我們來看看如何通過pop來實(shí)現(xiàn)一個(gè)自定義的動(dòng)畫辛燥,pop對(duì)自定義動(dòng)畫的支持感覺比較單一缝其,可以認(rèn)為就是一個(gè)定時(shí)器的功能而已。榴都。漠其。
想要自定義動(dòng)畫我們就需要有一個(gè)自定義的函數(shù)曲線,比如我們要實(shí)現(xiàn)一個(gè)彈簧動(dòng)畫(跟spring動(dòng)畫類似)拴驮,我們使用如下的時(shí)間函數(shù)柴信,輸出為[0-1](更多的緩動(dòng)函數(shù)可以去這查看:http://easings.net/zh-cn):

float ElasticEaseOut(float p)
{
    return sin(-13 * M_PI_2 * (p + 1)) * pow(2, -6 * p) + 1;
}

當(dāng)有了定義好的緩動(dòng)曲線后,我們就可以通過POPCustomAnimation來實(shí)現(xiàn)自定義動(dòng)畫潜沦,POPCustomAnimation會(huì)在每次CADisplayLink定時(shí)器觸發(fā)時(shí)回調(diào)我們定義好的函數(shù),同時(shí)給我們傳遞相關(guān)的時(shí)間參數(shù):

POPCustomAnimation *customAni = [POPCustomAnimation animationWithBlock:^BOOL(id target, POPCustomAnimation *animation) {
        
        //動(dòng)畫開始的時(shí)間窃判,我們可以記錄下來作為基準(zhǔn)時(shí)間
        if(_baseTime == 0){
            _baseTime = animation.currentTime;
        }

        //根據(jù)當(dāng)前時(shí)間喇闸,計(jì)算出當(dāng)前的時(shí)間進(jìn)度,并根據(jù)動(dòng)畫周期歸一化到[0-1]
        double progress = (animation.currentTime - _baseTime)/_duration;

        //使用ElasticEaseOut自定義曲線根據(jù)當(dāng)前進(jìn)度計(jì)算出新的值唆樊,該值大小也為[0-1]
        double caculateValue = ElasticEaseOut(progress);

        //根據(jù)緩動(dòng)函數(shù)的輸出刻蟹,計(jì)算新的值舆瘪,并賦給UI對(duì)象
        CGPoint current = CGPointZero;
        current.x = _from.x + (_to.x - _from.x) * caculateValue;
        current.y = _from.y + (_to.y - _from.y) * caculateValue;
        _testView.frame = CGRectMake(current.x, current.y, 20, 20);
        
        //如果當(dāng)前進(jìn)度小于1,則繼續(xù)動(dòng)畫
        if(progress < 1.0){
            return YES;
        }
        return NO;
    }];
    [_testView pop_addAnimation:layCus forKey:@"custom"];

可以看到如下的彈簧效果淀衣,與spring效果類似:

ElasticEaseOut
總結(jié)

通過上面的介紹我們大概也了解了pop動(dòng)畫引擎了召调,pop相比iOS的coreanimation的優(yōu)勢(shì)在于提供了spring以及decay動(dòng)畫效果唠叛,iOS7的spring動(dòng)畫效果較弱,CASpringAnimation能夠提供的效果較好艺沼,不過需要iOS9或以上的版本,除此之外pop還允許你自定義動(dòng)畫调鲸,所以pop還是有一定的吸引力剩拢。不過我們也可以發(fā)現(xiàn)pop動(dòng)畫是在主線層執(zhí)行的徐伐,因此如果主線層做耗時(shí)操作的話募狂,動(dòng)畫就不那么流暢了角雷,有興趣可以試一試性穿。。吗坚。

參考:

ios核心動(dòng)畫高級(jí)技巧
pop
緩動(dòng)函數(shù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末商源,一起剝皮案震驚了整個(gè)濱河市谋减,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌庄吼,老刑警劉巖严就,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異废菱,居然都是意外死亡抖誉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門旁理,熙熙樓的掌柜王于貴愁眉苦臉地迎上來孽文,“玉大人夺艰,你說我怎么就攤上這事〖跷” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵肥隆,是天一觀的道長稚失。 經(jīng)常有香客問我,道長吸占,這世上最難降的妖魔是什么凿宾? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任菌湃,我火速辦了婚禮,結(jié)果婚禮上惧所,老公的妹妹穿的比我還像新娘。我一直安慰自己纽绍,他們只是感情好势似,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布履因。 她就那樣靜靜地躺著,像睡著了一般站故。 火紅的嫁衣襯著肌膚如雪毅舆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天岂津,我揣著相機(jī)與錄音悦即,去河邊找鬼橱乱。 笑死,一個(gè)胖子當(dāng)著我的面吹牛冗美,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播节预,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼安拟,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼宵喂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起锅棕,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤裸燎,失蹤者是張志新(化名)和其女友劉穎嚎京,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體移稳,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年袱蚓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了几蜻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡颖低,死狀恐怖弧烤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情莺戒,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布瘪校,位于F島的核電站名段,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏麻惶。R本人自食惡果不足惜信夫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望脐彩。 院中可真熱鬧姊扔,春花似錦、人聲如沸佛南。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至苛白,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間懂版,已是汗流浹背躏率。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蓬抄,地道東北人嚷缭。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像克滴,于是被迫代替她去往敵國和親优床。 傳聞我的和親對(duì)象是個(gè)殘疾皇子誓焦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 書寫的很好杂伟,翻譯的也棒!感謝譯者观话,感謝感謝越平! iOS-Core-Animation-Advanced-Techni...
    錢噓噓閱讀 2,293評(píng)論 0 6
  • CoreAnimation:Core Animation自身并不是一個(gè)繪圖系統(tǒng)。它只是一個(gè)負(fù)責(zé)在硬件上合成和操縱應(yīng)...
    YGoooooooooal閱讀 572評(píng)論 0 1
  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果晦溪,實(shí)現(xiàn)這些動(dòng)畫的過程并不復(fù)雜挣跋,今天將帶大家一窺iOS動(dòng)畫全貌。在這里你可以看...
    F麥子閱讀 5,101評(píng)論 5 13
  • ?新人剛?cè)肼殘?chǎng)院领,總有兩三個(gè)月的摸索期,這段時(shí)期公司一般只安排一些很基礎(chǔ)的事情給你做比然∏糠ǎ可若是碰到部門正在處理一件緊急...
    春春姐閱讀 1,810評(píng)論 0 1
  • 漠中絕境猶勝妝,人生何處無歌詠闰歪!萬苦終抵一捧沙蓖墅,千佛融融為我頌。
    靈魂刀手閱讀 208評(píng)論 0 0