CoreAnimation之常用圖層

CoreAnimation之CALayer基礎
CoreAnimation之變換

1. CAShapeLayer

CAShapeLayer是一個通過矢量圖形而不是bitmap來繪制的圖層子類,使用CAShapeLayer有以下一些優點:

  • 渲染快速——CAShapeLayer使用了硬件加速,繪制同一圖形會比用Core Graphics快很多

  • 高效使用內存——一個CAShapeLayer不需要像普通CALayer一樣創建一個寄宿圖形,所以無論有多大,都不會占用太多的內存

  • 不會被圖層邊界剪裁掉——一個CAShapeLayer可以在邊界之外繪制。你的圖層路徑不會像在使用Core Graphics的普通CALayer一樣被剪裁掉

  • 不會出現像素化——當你給CAShapeLayer做3D變換時,它不像一個有寄宿圖的普通圖層一樣變得像素化

CAShapeLayer可以用來繪制所有能夠通過CGPath來表示的形狀。這個形狀不一定要閉合,圖層路徑也不一定要不可破,事實上你可以在一個圖層上繪制好幾個不同的形狀。你可以控制一些屬性比如lineWith,lineCap,和lineJoin。但是在圖層層面你只有一次機會設置這些屬性,如果你想用不同顏色或風格來繪制多個形狀,就不得不為每個形狀準備一個圖層了。

說了這么多,CAShapeLayer到底能用來做什么呢?

  • CAShapeLayer實現視圖的部分圓角:
-(void)drawCorner{
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
    view.center = self.view.center;
    view.backgroundColor = [UIColor blackColor];
    [self.view addSubview:view];
    
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:view.frame byRoundingCorners:UIRectCornerTopLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(30.0f, 30.0f)];
    
    CAShapeLayer *layer = [[CAShapeLayer alloc] init];
    layer.bounds = view.frame;
    layer.position = CGPointMake(50.0f, 50.0f);
    layer.path = path.CGPath;
    
    view.layer.mask = layer;
}

運行效果:


Paste_Image.png
  • CAShapeLayer實現一個呆萌的火柴人:
-(void)drawMatchman{
    CGFloat radius = 25.0f;             //半徑
    
    UIBezierPath *path = [[UIBezierPath alloc] init];
    
    CGPoint point1 = CGPointMake(self.view.center.x + radius, self.view.center.y);
    [path moveToPoint:point1];          //將畫筆移動到point1
    [path addArcWithCenter:self.view.center radius:radius startAngle:0.0f endAngle:2.0f*M_PI clockwise:YES];        //畫一個圓代表火柴人的頭
    
    CGPoint point2 = CGPointMake(point1.x - radius, point1.y + radius);
    [path moveToPoint:point2];          //將畫筆移動到point2,準備畫身體
    
    CGPoint point3 = CGPointMake(point2.x, point2.y+50.0f);
    [path addLineToPoint:point3];       //畫一根長為50的豎線代表火柴人的身體,起點是point2,終點是point3
    
    CGPoint point4 = CGPointMake(point3.x - radius, point3.y+25.0f);
    [path addLineToPoint:point4];       //畫一根長為25的左斜線代表火柴人的左腳,起點是point3,終點是point4
    
    [path moveToPoint:point3];          //將畫筆移動到point3,準備畫右腳
    CGPoint point5 = CGPointMake(point3.x + radius, point3.y+25.0f);
    [path addLineToPoint:point5];       //畫一根長為25的右斜線代表火柴人的右腳,起點是point3,終點是point5
    
    //最后畫一根橫線,代表火柴人的手
    CGPoint point6 = CGPointMake(point2.x - radius, point2.y + 25.0f);
    [path moveToPoint:point6];
    [path addLineToPoint:CGPointMake(point6.x + 50.0f, point6.y)];
    
    shapeLayer = [CAShapeLayer layer];
    shapeLayer.strokeColor = [UIColor redColor].CGColor;    //畫筆顏色
    shapeLayer.fillColor = [UIColor clearColor].CGColor;    //填充色
    shapeLayer.lineWidth = 6.0f;    //線條寬度
    shapeLayer.lineJoin = kCALineJoinRound; //線條連接處的樣式
    shapeLayer.lineCap = kCALineCapRound;   //線條末端處的樣式
    shapeLayer.path = path.CGPath;
    [self.view.layer addSublayer:shapeLayer];
    
    [self addFlagPoint:point1];
    [self addFlagPoint:point2];
    [self addFlagPoint:point3];
    [self addFlagPoint:point4];
    [self addFlagPoint:point5];
    [self addFlagPoint:point6];
}
-(void)addFlagPoint:(CGPoint)aPoint{
    UIView *flag = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 6.0f, 6.0f)];
    flag.center = aPoint;
    flag.layer.cornerRadius = 3.0f;
    flag.backgroundColor = [UIColor blackColor];
    [self.view addSubview:flag];
}

運行效果:


Paste_Image.png
  • CAShapeLayer實現一個扇形動畫:
-(void)drawCircular{
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
    view.center = self.view.center;
    UIImage *image = [UIImage imageNamed:@"3"];
    view.layer.contents = (__bridge id _Nullable)(image.CGImage);
    view.layer.contentsGravity = kCAGravityCenter;
    view.layer.contentsScale = [UIScreen mainScreen].scale;
    [self.view addSubview:view];
    
    shapeLayer = [CAShapeLayer layer];
    shapeLayer.frame = view.bounds;
    shapeLayer.strokeEnd = 0.0f;
    shapeLayer.strokeStart = 0.0f;
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:view.bounds];
    shapeLayer.path = path.CGPath;
    shapeLayer.fillColor = [UIColor clearColor].CGColor;
    shapeLayer.lineWidth = 100.0f;
    shapeLayer.strokeColor = [UIColor redColor].CGColor;
    view.layer.mask = shapeLayer;
    [NSTimer scheduledTimerWithTimeInterval:0.005f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
}
-(void)timerAction{
    static BOOL flag = NO;
    if (shapeLayer.strokeEnd >= 1.5f) {
        flag = YES;
    }
    if (shapeLayer.strokeEnd <= -0.5f) {
        flag = NO;
    }
    if (flag) {
        shapeLayer.strokeEnd -= 0.005f;
    }else{
        shapeLayer.strokeEnd += 0.005f;
    }
}

運行效果:

running.gif

更多關于CAShapeLayer的動畫:動畫黃金搭檔:CADisplayLink & CAShapeLayer


2. CATransformLayer

之前我們在CoreAnimation之變換中構造了一個殘缺的正方體,最后在旋轉正方體的時候遇到了問題,原因在于CALayer是扁平的,所以直接將superLayer繞y軸旋轉的時候看不出正方體的3D效果

CoreAnimation有一個專用圖層叫CATransformLayer,它是CALayer的子類,但是不同于普通的CALayer,因為它不能顯示它自己的內容,只有當存在了一個能作用域子圖層的變換它才真正存在,而且CATransformLayer并不平面化它的子圖層,所以它能夠用于構造一個層級的3D結構

總之一句話,CATransformLayer相當于一個容器,一個3D的容器

這次我們依然以構建一個正方體為例,開始寫代碼前,我們不妨在腦袋里先構造一下這個正方體:

  • 首先,有六塊木板,都是平放在一個3D空間里

  • 第一步,構建上下兩面,把上面這塊木板往上移動50個點(即沿z軸移動50個點);再把下面這塊木板往下移動50個點(即沿z軸移動-50個點),這樣就構建出了上下兩面

  • 第二步,構建左右兩面,將左面這塊木板往左邊直立起來(即沿y軸旋轉-90度),再將左面這塊木板往上移動50個點(即沿z軸移動50個點);同理,右面這塊木板就是先往右邊直立起來再往上移動

  • 第三步,構建前后兩面,將前面這塊木板往前邊直立起來(即沿x軸旋轉-90度),再將前面這塊木板往上移動50個點(即沿z軸移動50個點);同理,后面這塊木板就是先往后邊直立起來再往上移動

代碼如下(樓主也是初學,為了邏輯清晰,就沒有封裝方法,直接一個面一個方法,所以代碼有點多哈):

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController{
    CATransformLayer *transformLayer;
    NSTimer *timer;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    CATransform3D sublayerTransform = CATransform3DIdentity;
    sublayerTransform.m34 = -1.0f/500.0f;
    self.view.layer.sublayerTransform = sublayerTransform;
    
    transformLayer = [CATransformLayer layer];
    transformLayer.position = self.view.layer.position;
    
    CATransform3D transform = CATransform3DIdentity;
    transform = CATransform3DRotate(transform, -M_PI_4, 1.0f, 0.0f, 0.0f);
    transform = CATransform3DRotate(transform, -M_PI_4, 0.0f, 1.0f, 0.0f);
    transformLayer.transform = transform;
    
    [self.view.layer addSublayer:transformLayer];
    
    [self addFace1];
    [self addFace2];
    [self addFace3];
    [self addFace4];
    [self addFace5];
    [self addFace6];
}

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

-(void)timerAction{
    CATransform3D transform = transformLayer.transform;
    transform = CATransform3DRotate(transform, 1.0f/180.0f*M_PI, 1.0f, 1.0f, 0.0f);
    transformLayer.transform = transform;
}

//上面 ———— 把上面這塊木板往上移動50個點(即沿z軸移動50個點)
-(void)addFace1{
    CALayer *layer = [[CALayer alloc] init];
    layer.bounds = CGRectMake(0.0f, 0.0f, 100.0f, 100.0f);
    layer.backgroundColor = [UIColor redColor].CGColor;
    CATransform3D transform = CATransform3DIdentity;
    transform = CATransform3DTranslate(transform, 0.0f, 0.0f, 50.0f);
    layer.transform = transform;
    [transformLayer addSublayer:layer];
}

//下面 ———— 把下面這塊木板往下移動50個點(即沿z軸移動-50個點)
-(void)addFace2{
    CALayer *layer = [[CALayer alloc] init];
    layer.bounds = CGRectMake(0.0f, 0.0f, 100.0f, 100.0f);
    layer.backgroundColor = [UIColor orangeColor].CGColor;
    CATransform3D transform = CATransform3DIdentity;
    transform = CATransform3DTranslate(transform, 0.0f, 0.0f, -50.0f);
    layer.transform = transform;
    [transformLayer addSublayer:layer];
}

//左面
-(void)addFace3{
    CALayer *layer = [[CALayer alloc] init];
    layer.bounds = CGRectMake(0.0f, 0.0f, 100.0f, 100.0f);
    layer.backgroundColor = [UIColor yellowColor].CGColor;
    CATransform3D transform = CATransform3DIdentity;
    //將左面這塊木板往左邊直立起來(即沿y軸旋轉-90度)
    transform = CATransform3DRotate(transform, -M_PI_2, 0.0f, 1.0f, 0.0f);
    //再將左面這塊木板往上移動50個點(即沿z軸移動50個點)
    transform = CATransform3DTranslate(transform, 0.0f, 0.0f, 50.0f);
    layer.transform = transform;
    [transformLayer addSublayer:layer];
}

//右面
-(void)addFace4{
    CALayer *layer = [[CALayer alloc] init];
    layer.bounds = CGRectMake(0.0f, 0.0f, 100.0f, 100.0f);
    layer.backgroundColor = [UIColor greenColor].CGColor;
    CATransform3D transform = CATransform3DIdentity;
    //將右面這塊木板往右邊直立起來(即沿y軸旋轉90度)
    transform = CATransform3DRotate(transform, M_PI_2, 0.0f, 1.0f, 0.0f);
    //再將右面這塊木板往上移動50個點(即沿z軸移動50個點)
    transform = CATransform3DTranslate(transform, 0.0f, 0.0f, 50.0f);
    layer.transform = transform;
    [transformLayer addSublayer:layer];
}

//前面
-(void)addFace5{
    CALayer *layer = [[CALayer alloc] init];
    layer.bounds = CGRectMake(0.0f, 0.0f, 100.0f, 100.0f);
    layer.backgroundColor = [UIColor blueColor].CGColor;
    CATransform3D transform = CATransform3DIdentity;
    //將前面這塊木板往前邊直立起來(即沿x軸旋轉-90度)
    transform = CATransform3DRotate(transform, -M_PI_2, 1.0f, 0.0f, 0.0f);
    //再將前面這塊木板往上移動50個點(即沿z軸移動50個點)
    transform = CATransform3DTranslate(transform, 0.0f, 0.0f, 50.0f);
    layer.transform = transform;
    [transformLayer addSublayer:layer];
}

//后面
-(void)addFace6{
    CALayer *layer = [[CALayer alloc] init];
    layer.bounds = CGRectMake(0.0f, 0.0f, 100.0f, 100.0f);
    layer.backgroundColor = [UIColor purpleColor].CGColor;
    CATransform3D transform = CATransform3DIdentity;
    //將后面這塊木板往后邊直立起來(即沿x軸旋轉90度)
    transform = CATransform3DRotate(transform, M_PI_2, 1.0f, 0.0f, 0.0f);
    //再將后面這塊木板往上移動50個點(即沿z軸移動50個點)
    transform = CATransform3DTranslate(transform, 0.0f, 0.0f, 50.0f);
    layer.transform = transform;
    [transformLayer addSublayer:layer];
}

@end

以前在CALayer上旋轉正方體的時候我們要這樣寫(即把所有的子圖層挨個兒做一次變換):

-(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;
}

而現在在CATransformLayer上旋轉正方體,只需要將CATransformLayer繞x軸或者繞y軸旋轉就行了:

-(void)timerAction{
    CATransform3D transform = transformLayer.transform;
    transform = CATransform3DRotate(transform, 1.0f/180.0f*M_PI, 1.0f, 1.0f, 0.0f);
    transformLayer.transform = transform;
}

運行效果:

running.gif

3. CAGradientLayer

CAGradientLayer可以用來實現漸變效果:

    CAGradientLayer *layer = [CAGradientLayer layer];
    layer.bounds = CGRectMake(0.0f, 0.0f, 150.0f, 150.0f);
    layer.position = self.view.layer.position;
    layer.colors = @[(__bridge id)[UIColor redColor].CGColor,(__bridge id)[UIColor greenColor].CGColor,(__bridge id)[UIColor blueColor].CGColor];
    layer.locations = @[@.25,@0.5,@0.75];
    layer.startPoint = CGPointMake(0.0f, 0.0f);
    layer.endPoint = CGPointMake(1.0f, 0.0f);
    [self.view.layer addSublayer:layer];

運行效果:


Paste_Image.png

需要特別說明一下locations這個屬性,locations數組里面裝的是相對位置,這個相對位置必須是單調遞增的,但是這個位置并不是代表顏色的位置,而是說從這個位置開始,將要開始漸變成下一個顏色了

拿示例代碼來說,從0.25開始,將要由紅變綠了,從0.5開始將要由綠變藍了,從0.75開始又要開始下一個漸變了,但是由于沒有下一個顏色了,所以后面全是藍色,你可以在示例代碼的colors里面再添加一個顏色試試


4. CAReplicatorLayer

學習CAReplicatorLayer之前,我們再來復習一下變換的順序:

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController{
    CALayer *layer;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    layer = [CALayer layer];
    layer.backgroundColor = [UIColor cyanColor].CGColor;
    UIImage *image = [UIImage imageNamed:@"3"];
    layer.frame = CGRectMake(110.0f, 100.0f, 100.0f, 100.0f);
    layer.contents = (__bridge id)image.CGImage;
    layer.contentsGravity = kCAGravityResizeAspect;
    layer.contentsScale = [UIScreen mainScreen].scale;
    [self.view.layer addSublayer:layer];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self rotate];
}

-(void)rotate{
    __weak __typeof__(self) weakSelf = self;
    [UIView animateWithDuration:3.0f animations:^{
        CATransform3D transform = layer.transform;
        transform = CATransform3DRotate(transform, M_PI, 0.0f, 0.0f, 1.0f);
        layer.transform = transform;
    } completion:^(BOOL finished) {
        [weakSelf translate];
    }];
}

-(void)translate{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [UIView animateWithDuration:3.0f animations:^{
            CATransform3D transform = layer.transform;
            transform = CATransform3DTranslate(transform, 0.0f, 100.0f, 0.0f);
            layer.transform = transform;
        }];
    });
}

@end

運行效果


running.gif

注意這句代碼:

transform = CATransform3DTranslate(transform, 0.0f, 100.0f, 0.0f);

明明是沿y軸移動200個點,為啥運行的時候會往上跑呢?

原因在于,在移動之前,我們已經繞z軸旋轉過了:

transform = CATransform3DRotate(transform, M_PI, 0.0f, 0.0f, 1.0f);

旋轉之后的layer,其相對于superLayer的坐標系已經發生了改變

拿示例代碼來說,繞z軸旋轉180度之后,x軸和y軸的方向都已經變成與原來相反的方向了

變換的順序,在CAReplicatorLayer中體現得尤為明顯,因為CAReplicatorLayer的instance的變換是逐步增加的,每個實例都是相對于前一實例布局

CAReplicatorLayer實現一個帶倒影的ImageView:

#import "ReplicatorImageView.h"

@implementation ReplicatorImageView

+ (Class)layerClass{
    return [CAReplicatorLayer class];
}

- (void)setUp{
    CAReplicatorLayer *layer = (CAReplicatorLayer *)self.layer;
    layer.instanceCount = 2;
    CATransform3D transform = CATransform3DIdentity;
    transform = CATransform3DScale(transform, 1, -1, 0);
    transform = CATransform3DTranslate(transform, 0.0f, -self.frame.size.height, 0.0f);
    layer.instanceTransform = transform;
    layer.instanceAlphaOffset = -0.6;
    
    CALayer *imageLayer = [CALayer layer];
    imageLayer.frame = layer.bounds;
    imageLayer.contents = (__bridge id _Nullable)(self.image.CGImage);
    imageLayer.contentsScale = [UIScreen mainScreen].scale;
    imageLayer.contentsGravity = kCAGravityResizeAspect;
    [layer addSublayer:imageLayer];
}

-(void)setReplicatorImage:(UIImage *)replicatorImage{
    self.image = replicatorImage;
    [self setUp];
}

@end
- (void)viewDidLoad {
    [super viewDidLoad];
    
    ReplicatorImageView *imageView = [[ReplicatorImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 150.0f, 150.0f)];
    imageView.center = self.view.center;
    [self.view addSubview:imageView];
    
    UIImage *image = [UIImage imageNamed:@"3"];
    imageView.replicatorImage = image;
}

運行效果:

Paste_Image.png

instanceCount指定了總共要復制多少個圖層(包含本身)

instanceAlphaOffset = -0.6f; 即當前圖層實例的alpha值 = 上一個圖層實例的alpha - 0.6f,與之類似的屬性還有這些:

/* The color components added to the color of instance k-1 to produce
 * the modulation color of instance k. Defaults to the clear color (no
 * change). Animatable. */

@property float instanceRedOffset;
@property float instanceGreenOffset;
@property float instanceBlueOffset;

更多CAReplicatorLayer動畫:基于CAReplicatorLayer的炫酷動畫

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

推薦閱讀更多精彩內容