Layer學習筆記

筆記主要來源iOS核心動畫高級技巧,感謝作者與翻譯的各位同學.

一、圖層樹

  • UIView、NSView都有一個關聯的CALayer,不用CALayer處理所有事情的原因是為了職責分離,在iOS和Mac OS兩個平臺上,事件和用戶交互有很多地方的不同.

二、寄宿圖

  • UIView有個contentMode屬性,經常在它的子類UIImageView上使用,而實際上這個屬性是對應CALayer的contentGravity屬性的,并且CALayer的contentGravity屬性是NSString類型,可選的常量值如下:

    * KCAGravityCenter
    * KCAGravityTop
    * KCAGravityBottom
    * KCAGravityLeft
    * KCAGravityRight
    * KCAGravityTopRight
    * KCAGravityBottomLeft
    * KCAGravityBottomRight
    * KCAGravityResize
    * KCAGravityResizeAspect
    * KCAGravityResizeAepectFill
    
  • contentScale屬性定義了寄宿圖的像素尺寸和視圖大小的比例,默認值為1.0,contentScale屬性屬于支持高分辨率屏幕機制的一部分(與圖片的2x,3x使用相對應,iPhone4、5、6系列均為2.0,iPhone6p系列均為3.0).為了使圖片顯示爭取可設置layer.contentScale = [UISCreen mainScreen].scale;

  • maskToBounds屬性與UIView的clipsToBounds屬性對應,決定是否顯示超出邊界的內容.

  • contentsRect屬性CALayer的contentsRect屬性允許我們在圖層邊框里顯示寄宿圖的一個子域.和boundsframe不同,contentsRect不是按點來計算的,它使用了單位坐標,單位坐標指定在0和1之間,是一個相對值.iOS的坐標系統:

    • 點 —— 在iOS和Mac OS中最常見的坐標體系。點就是虛擬的像素。也被稱作邏輯像素。在標準設備上,一個點就是一個像素點,但是在retina設備上,一個點等于2*2個像素。

    • 像素——物理像素坐標并不會用來屏幕布局,但是仍然與圖片有相對關系。UIImage是一個屏幕分辨率解決方案,所以指定點來度量大小。但是一些底層的圖片表示如CGImage就會使用像素,所以你要清楚在Retina設備和普通設備上,他們表現出來了不同的大小。

    • 單位——對于與圖片或者圖層邊界相關的顯示,單位坐標是一個方便的度量方式,當大小改變的時候,也不需要再次調整。單位坐標在OpenGL這種紋理坐標系統中用得很多,Core Animation中也用到了單位坐標。

      ?

    默認的contentsRect是{0,0,1,1},這意味著整個寄宿圖默認都是可見的.詳細的使用參考Layer的寄宿圖contents屬性.

  • contentsCenter屬性.可以用Interface Builder探測窗口控制contentsCenter屬性(View里).

  • Custom Drawing

    -drawRect:方法沒有默認的實現,如果不需要自定義的繪制,就不要創建這個方法,這會造成CPU資源和內存浪費。雖然-drawRect:方法是一個UIView方法,事實上都是底層的CALayer安排了重繪工作和保存了因此產生的圖片。CALayer有一個可選的delegate屬性,實現了CALayerDelegate,當CALayer需要一個內容特定的信息時,就會從協議中請求。當需要被重繪時,CALayer會請求它的代理來給他一個寄宿圖來顯示。它通過調用這個方法做到:

    -(void)displayLayer:(CALayer *)layer
    

    如果代理不實現-displayLayer:方法,CALayer就會轉而嘗試調用下面這個方法:

    -(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
    

    提供了以上方法,可以使用CALayer的display去強制layer去繪制。

    UIView創建了它的宿主圖層時,它就會自動地把圖層的delegate設置為它自己,并提供了一個-displayLayer:的實現。所以一般繪制調用UIView的-drawRect:方法就可以。

三、圖層幾何學

  • 布局

    UIView的三個屬性frame,bounds,center對應CALayer的三個屬性frame,bounds,postioncenterposition都代表了相對于父圖層anchorPoint所在的位置.

    視圖的frame,boundscenter屬性僅僅是存取方法,當操縱視圖的frame,實際上是在改變視圖下方CALayerframe,不能夠獨立于圖層之外改變視圖的frame

    對于視圖或者圖層來說,frame并不是一個非常清晰的屬性,它其實是一個虛擬屬性,是根據bounds,boundstransform計算而來,所以當其中任何一個值發生改變,frame都會變化。相反,改變frame的值同樣會影響到他們當中的值。

  • 錨點(anchorPoint)可以參考這篇文章

  • 坐標系.

    一個圖層的position依賴于它父圖層的bounds,如果父圖層發生了變化,它的所有子圖層也會跟著移動.CALayer給不同坐標系之間的圖層轉換提供了一些工具類方法:

    - (CGPoint)convertPoint:(CGPoint)point fromLayer:(CALayer *)layer;
    - (CGPoint)convertPoint:(CGPoint)point toLayer:(CALayer *)layer;
    - (CGRect)convertRect:(CGRect)rect fromLayer:(CALayer *)layer;
    - (CGRect)convertRect:(CGRect)rect toLayer:(CALayer *)layer;
    
  • Hit Testing.

    CALayer并不關心任何響應事件,所以不能直接處理觸摸事件或者手勢。但是它有一系列的方法幫你處理事件:-containsPoint:-hitTest:.

    hitTest:方法同樣接受一個CGPoint類型參數,而不是BOOL類型,它返回圖層本身,或者包含這個坐標點的葉子點圖層。

需要注意的是,當調用圖層的-hitTest:方法時,測算的順序嚴格依賴于圖層樹當中的圖層順序(和UIView處理事件類似).zPosition屬性可以明顯改變屏幕上圖層的順序,但不能改變事件傳遞的順序。

四、視覺效果

  • 圓角.

  • 圖層邊框.

    邊框是繪制在圖層邊界里面的,而且在所有子內容之前,也在子圖層之前。邊框并會把寄宿圖或者子圖層的形狀計算進來,如果圖層的子圖層超過了邊界,或者是寄宿圖在透明區域有一個透明的蒙版,邊框仍然會沿著圖層的邊界繪制出來。

  • 陰影.

    • shadowOpacity
    • shadowColor
    • shadowOffest,該屬性控制著陰影的方向和距離。它是一個CGSize的值,寬度控制著陰影橫向的位移,高度控制著縱向的位移。shadowOffese的默認值是{0,-3},即陰影相對于Y軸有3個點的向上位移。
    • shadowRadius,該屬性控制著陰影的模糊的,當它的值是0的時候,陰影就和視圖一樣有一個非常確定的邊界線。當值越來越大的時候,邊界線看上去就會越來越模糊和自然.(所以不要設置為0)。
    • shadowPath.
  • 圖層蒙版.

    mask圖層的color屬性是無關緊要的,真正重要的是圖層的輪廓。mask屬性就像是一個餅干切割機,mask圖層實心的部分被保留下來,其他的則會被拋棄。

  • 拉伸過濾.

  • 組透明.

    UIViewalpha屬性與CALayeropacity屬性相對應。

五、變換

  • 仿射變換.

    UIView的transform與CALayer的affineTransform相對應,都是CGAffineTransform類型,用于在二維空間做旋轉,縮放和平移。CGAffineTransform是一個可以和二維空間向量做乘法的3*2矩陣。

  • 3D變換

    CALayer的屬性transformCATransform3D類型 ,CATransform3D也是一個矩陣,是一個可以在3維空間內做變換的4*4矩陣。

六、專用圖層

  • CAShapeLayer(十分重要)
  • CATextLayer
  • CAGradientLayer. CAGradientLayer是用來生成兩種或更多顏色平滑漸變的。
  • CAScrollLayer
  • AVPlayerLayer(十分重要).

七、隱式動畫

  • 事務

    Core Animation基于一個假設,屏幕上的任何東西都可以(或者可能)做動畫。動畫并不需要你在Core Animation中手動打開,相反需要明確的關閉,否則它會一直存在.

    事務事件上是Core Animation用來包含一系列屬性動畫集合的機制,任何用指定事務去改變可以做動畫的圖層屬性都不會立刻發送變化。而是當事務一旦提交的時候開始用一個動畫過渡到新值。

    事務是通過CATransaction類來做管理, CATransaction沒有屬性或者實例方法,并且也不能用+alloc-init方法創建它。但是可以用+begin+commint分別來入棧和出棧.

    Core Animation在每個run loop周期中自動開始一次新的事務(run loop是iOS負責收集用戶輸入,處理定時器或者網絡事件并且重新繪制屏幕的東西),即使你不顯式的用[CATransaction begin]開始一次事務,任何在一次run loop循環中屬性的改變都會被機種起來,然后做一次0.25秒的動畫。

    UIView有兩個方法,+beginAnimations:context:+commitAnimations,和CATransactionbegincommit方法類似。實際上在+beginAnimations:context+commitAnimations之間所有視圖或者圖層屬性的改變而做的動畫都是由于設置了CATransaction的原因。在iOS4中,蘋果對UIView添加了一種基于block的動畫:+animateWithDuration:animations:.實質上它們都是在做同樣的事情。

  • 完成塊

  • 圖層行為

    Core Animation通常對CALayer的所有屬性(可動畫的屬性)做隱式動畫,但UIView把它關聯的圖層的這個屬性關閉了。

  • 呈現于模型

    CALayer的屬性行為其實很不正常,因為改變一個圖層的屬性并沒有立即生效,而是通過一段時間漸變更新。

    當你改變一個圖層的屬性,屬性值的確立刻更新的,但是在屏幕上并沒有馬上發生改變。這是因為你設置的屬性并沒有直接調整圖層的外觀,相反,它只是定義了圖層動畫結束之后將要變化的外觀。

    在iOS中,屏幕每秒鐘重繪60次。如果動畫時長比60分之一要長,Core Animation就需要在設置一次新值和新值生效之間,對屏幕上的圖層進行重新組織。這意味著CALayer除了“真實”值(就是你設置的值)之外,必須要知道當前顯示在屏幕上的屬性值的記錄。

    通過presentationLayer來獲取屏幕上真正顯示出來的值;而在presentationLayer上調用-modelLayer將會返回它正在呈現所依賴的layer.

    如果想讓做動畫的圖層響應用戶輸入,可以使用-hitTest:方法,來判斷呈現圖層(presentationLayer)是否被觸摸來響應。(具體代碼請參考原書籍)

八、顯式動畫

  • 屬性動畫

    • CABasicAnimation

      當更新屬性的時候,我們需要設置一個新的事務,并且禁用圖層行為。否則動畫會發生兩次,一個是因為顯式的CABasicAnimation,另一次是因為隱式動畫。(在設置屬性的時候)

      通過keyPathfromValuetoValue設置動畫,fromValue可以不設置.byValue是一個相對值,會從當前的值上動畫到byValue的值。

    • CAKeyframeAnimation(關鍵幀動畫)

      屬性動畫通過設置keyPathvalues屬性得以實現關鍵幀動畫;還可以通過設置CGPath來實現一個路徑相關的動畫,另外,設置rotationMode屬性為KCAAnimationRotateAuto,圖層將會根據曲線的切線自動旋轉。

    • 虛擬屬性

      • transform.rotation
      • transform.postion
      • transform.scale

      keyPath可以設置為以上三個虛擬屬性,然后再通過byValuetoValue來設置需要動畫的值。

  • CAAnimationGroup(動畫組)

  • 過渡

    最常見到的過渡就是在childViewController之間切換view.

    為了創建一個過渡動畫,將使用CATransiton,同樣是另一個CAAnimation的子類,和別的子類不同,CATransiton有一個Typesubtype來標識變換效果.type屬性是一個NSString類型,可以被設置成以下值:

    KCATransitionFade
    KCATransitionMoveIn
    KCATransitionPush
    KCATransitionReveal
    

    隱式過渡.當設置CALayercontent屬性的時候,CATransition的確是默認的行為。但是對于視圖關聯的圖層,或者其他隱式動畫的行為,這個特性依然是被禁用的。

  • 在動畫過程中取消動畫

    為了終止一個指定的動畫,你可以用如下方法把它從圖層移除掉:

    -(void)removeAnimationForKey:(NSString *)key;
    

    或者移除所有的動畫:

    -(void)removeAllAnimations;
    

    動畫一旦被移除,圖層的外觀就立刻更新到當先的模型圖層的值。一般來說,動畫在結束之后被自動移除,除非設置removeAllAnimationNO,如果你設置動畫在結束之后不被自動移除,那么當它不需要的時候你要手動移除它;否則它會一直存在于內存中,直到圖層被銷毀。

九、圖層時間

  • CAMediaTiming協議

    CAMediaTiming協議定義了在一段動畫內用來控制逝去時間的屬性的集合,CALayerCAAnimation都實現了這個協議,所以時間可以被任意基于一個圖層或者一段動畫的類控制。

    • 持續和重復

      duration是一個CFTimeInterval類型,對將要進行的動畫的一次迭代指定了時間;repeatCount代表動畫重復的迭代次數。例如,duration是2,repeatCount是3.5,那么完整的動畫時長將是7秒。

    • 相對時間

      beginTime指定動畫開始之前的延遲時間。這里的延遲是從動畫添加到可見圖層的那一刻開始測量,默認是0(就是說動畫會立刻執行)。

      speed是一個時間的倍數,默認1.0,減少它會減慢圖層/動畫的時間,增加它會加快速度。如果speed為2.0,那么對于一個duration為1的動畫,實際上在0.5秒的時候就已經完成了。

      timeOffset讓動畫快進到某一點,例如,對于一個持續1秒的動畫來說,設置timeOffset為0.5意味著動畫將從一般的地方開始。需要注意的是,和beginTime不同的是,timeOffset并不受speed的影響。所以如果你把speed設置為2.0,把timeOffset設置0.5,那么你的動畫將從動畫最后結束的地方開始,因為1秒的動畫實際上被縮短到了0.5秒。然而即使使用了timeOffset讓動畫從結束的地方開始,它仍然播放了一個完整的時長,這個動畫僅僅是循環了一圈,然后從頭開始播放。

    • fillMode

      fillModel是一個NSString類型,可以接受如下四種常量:

      KCAFillModeForwards
      KCAFillModeBackwards
      KCAFillModeBoth
      KCAFillModeRemoved
      

      默認是KCAFillModeRemoved,當動畫不再播放的時候就顯示圖層模型指定的值,剩下的三種類型,向前、向后或者即向前又向后去填充動畫狀態,使得動畫在開始前或者結束后仍然保持開始和結束那一刻的值。

      這就對避免對動畫結束的時候急速返回提供了另一種方案。但是,當它來解決這個問題的時候,需要把removeOnCompletion設置為NO,另外需要給動畫添加一個非空的鍵,于是可以在不需要動畫的時候把它從圖層上移除。

  • 層級關系時間

    每個動畫和圖層在時間上都有它自己的層級概念,相對于它的父親來測量,對圖層調整時間將會影響到它本身和子圖層的動畫,但不會影響到父圖層。

    CALayer或者CAGroupAnimation調整durationrepeatCountrepeatDuration屬性并不會影響到子動畫。但是beginTimetimeOffsetspeed屬相將會影響到子動畫。

    CoreAnimation有一個全局時間的概念—馬赫時間。馬赫時間在設備上所有進程都是全局的,但是在不同的設備上并不是全局的(手機休眠時,馬赫時間會暫停),但是比較兩次馬赫時間差很有價值。訪問馬赫時間:

    CFTimeInterval time = CACurrentMediaTime();
    
  • 手動動畫

    timeOffset一個很有用的功能在于它可以讓你手動控制動畫進程,通過設置speed為0,可以禁用動畫的自動播放,然后用timeOffset來來回顯示動畫序列。這可以使得運用手勢來控制動畫變得很簡單。

  • 總結

十、緩沖

  • 動畫速度

    使用緩沖方程式需要設置CAAnimationtimingFunction屬性,timingFunctionCAMediaTimingFunction類的一個對象;如果想改變隱式動畫的計時函數,同樣也可以使用CATransaction+setAnimationTimingFunctions:方法。

    創建CAMediaTimingFunction的最簡單的方式是調用+timingFuctionWithName:的構造方法。傳入的常量如下:

    KCAMediaTimingFunctionLinear
    KCAMediaTimingFunctionEaseIn
    KCAMediaTimingFunctionEaseOut
    KCAMediaTimingFunctionEaseInEaseOut
    KCAMediaTimingFunctionDefault
    
  • 自定義緩沖函數

十一、基于定時器的動畫

  • 定時幀

    iOS按照每秒60次刷新屏幕,用定時器1/60秒去更新view的屬性便可以實現定時幀動畫.

    • NSTimer

      iOS上的每個線程都管理了一個NSRunloop,字面上看就是通過一個循環來完成一些任務列表。但是對主線程,這些任務包含如下幾項:

      • 處理觸摸事件
      • 發送和接受網絡數據包
      • 執行使用GCD的代碼
      • 處理計時器的行為
      • 屏幕重繪

      當設置一個NSTimer,他會被插入到當前任務列表中,然后知道指定的時間過去之后才會被執行。但是何時啟動定時器并沒有一個時間上限,而且它只會在列表中上一個任務完成之后開始執行。這通常會有幾毫秒的延遲,但是如果上一個任務過了很久才完成就會導致延遲很長一段時間。

      如何精確動畫:

      • 可以用CADisplayLink讓更新頻率嚴格控制在屏幕刷新之后.
      • 基于真實幀的持續時間而不是假設的更新頻率來做動畫(通過兩次馬赫時間差來取動畫幀).
      • 調整動畫計時器的runloop模式,這樣就不會被別的事件干擾.
    • CADisplayLink

      CADisplayLink是CoreAnimation提供的類似NSTimer的類,它總是在屏幕完成一次更新之前啟動。CADisplayLink有一個整型的frameInterval屬性,指定了間隔多少幀之后才執行。默認值是1,意味著每次屏幕更新之前都會執行一次。但是如果動畫的代碼執行起來超過了六十分之一秒,你可以指定frameInterval為2,就是說動畫每隔一幀執行一次。

    • Run Loop模式

      常見的run loop模式:

      • NSDefaultRunLoopModel - 標準優先級
      • NSRunLoopCommonModes - 高優先級
      • UITrackingRunLoopMode - 用于UISCrollView和別的控件的動畫

      可以對CADisplayLink指定多個run loop模式,于是我們可以同時加入NSDefaultRunLoopModeUITrackingRunLoopMode來保證它不會被滑動打斷,也不會被其他UIKit控件動畫影響性能,像這樣:

      self.timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(step:)];
      [self.timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
      [self.timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:UITrackingRunLoopMode];
      

      NSTimer同樣:

      self.timer = [NSTimer timerWithTimeInterval:1/60.0
                                       target:self
                                     selector:@selector(step:)
                                     userInfo:nil
                                      repeats:YES];
      [[NSRunLoop mainRunLoop] addTimer:self.timer
                                forMode:NSRunLoopCommonModes];
      

十二、性能調優

  • CPU VS GPU

  • 測量,而不是猜測

    可以在程序中用CADisplayLink來測量幀率(用兩次馬赫時間差的倒數即為幀率)

  • Instruments

    • Time Profiler
    • Leaks
    • Core Animation
    • Allocations
    • GPU Driver

十三、高效繪圖

  • 軟件繪圖

    在iOS中,軟件繪圖通常是由Core Graphics框架完成。但是在一些必要的情況下,相比Core Animation和OpenGL, Core Graphics要慢不少。

    一旦實現了CALayerDelegate協議中的-drawLayer:inContext:方法或者UIView中的-drawRect:方法(其實就是前者的包裝方法),圖層就創建了一個繪制上下文,這個上下文需要的大小的內存可從這個算式得出:圖層寬圖層高4字節,寬高的單位均為像素。對于一個在Retina iPad上的全屏圖層來說,這個內存量就是 2048*1526*4字節,相當于12MB內存,圖層每次重繪的時候都需要重新抹掉內存然后重新分配(所以一般還是不要重寫view的-drawRect:方法)。

  • 矢量圖形

    在某些情況下,需要使用Core Graphics來繪圖:

    • 任意多邊形(不僅僅是一個矩形)
    • 斜線或曲線
    • 文本
    • 漸變

    但Core Animation實際上為這樣的繪制提供了專門的類,如CAShapeLayer繪制多邊形、直線和曲線,CATextLayer繪制文本,CAGradientLayer用來繪制漸變。

  • 臟矩形

  • 異步繪制

十四、圖像IO

  • 加載和潛伏
    • +imageNamed:方法會解壓圖片,并緩存圖片,但是+imageNamed:只對應用資源束中的圖片有效,所以對用戶生成的圖片或者下載的圖片就沒法使用了。
    • +imageWithContentsOfFile:加載大圖片比較耗時,并且不會解壓圖片。如果要實現主線程快速加載圖片,需要在后臺線程加載圖片數據并強制解壓。
    • 第三方庫SDWebImage實現了圖片的下載、解壓、緩存,可以多學習。
  • 緩存
    • NSCache
      • -setCountLimit:,設置緩存數量。
      • -setObject:forKey:cost:,對每個存儲的對象指定消耗的值來提供一些暗示。
      • -setTotalCostLimit:,設置全體緩存的尺寸。
      • NSCache在系統低內存的時候自動丟棄存儲的對象。
  • 文件格式

十五、圖層性能

  • 隱式繪制

  • 離屏渲染

    當圖層屬性的混合體被指定為在未預合成之前不能直接在屏幕中繪制時,屏幕外渲染就被喚起了。屏幕外渲染并不意味著軟件繪制,但是它意味著圖層必須在被顯示之前在一個屏幕外上下文中被渲染(不論CPU還是GPU)。圖層的以下屬性將會觸發屏幕外繪制:

    • 圓角(當和maskToBounds一起使用時)
    • 圖層蒙板
    • 陰影

    對于那些需要動畫而且要在屏幕外渲染的圖層來說,你可以用CAShapeLayercontentsCenter或者shadowPath來獲得同樣的表現而且較少地影響到性能。

  • 混合和過渡繪制

    透明、半透明顏色的View,會增加GPU的計算,降低性能。shouldRasterize屬性的使用。

  • 減少圖層數量

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,024評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,285評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,548評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,328評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,878評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,098評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,616評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,554評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,725評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,971評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,361評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,613評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,339評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,695評論 2 370

推薦閱讀更多精彩內容