使用CAShapeLayer & UIBezierPath画一些形状

本篇主要从以下几个方面来写的一点东西:

  • 线段
  • 曲线
  • 动画
  • 简单的柱状图
  • 简单的折线图

线段

线段
  • 单线段
    两点确定一条直线,给贝塞尔曲线一个起始点moveToPoint再添加一条线的终点addLineToPoint,这样就确定了一条直线。
- (void)drawLine {
    UIView *view = [self.view viewWithTag:1024];
    UILabel *label = [view viewWithTag:524];
    label.text = @"直线";
    
    CAShapeLayer *line = [CAShapeLayer layer];
    line.lineWidth = 2;
    line.strokeColor = [UIColor orangeColor].CGColor;
    line.fillColor = nil;
    [view.layer addSublayer:line];
    
    UIBezierPath *bezierPath = [UIBezierPath bezierPath];
    [bezierPath moveToPoint:CGPointMake(100, 50)];
    [bezierPath addLineToPoint:CGPointMake(200, 150)];
    
    line.path = bezierPath.CGPath;
}
  • 多线段
    前面线段的终点是后面线段的起点。给一个起点moveToPoint,然后想添加几条线就给几个线的终点addLineToPoint
- (void)drawDoubleLine {
    UIView *view = [self.view viewWithTag:1025];
    UILabel *label = [view viewWithTag:525];
    label.text = @"折线";
    
    CAShapeLayer *line = [CAShapeLayer layer];
    line.lineWidth = 2;
    line.strokeColor = [UIColor orangeColor].CGColor;
    line.fillColor = nil;
    [view.layer addSublayer:line];
    
    UIBezierPath *bezierPath = [UIBezierPath bezierPath];
    [bezierPath moveToPoint:CGPointMake(100, 50)];
    [bezierPath addLineToPoint:CGPointMake(200, 150)];
    [bezierPath addLineToPoint:CGPointMake(200, 100)];
    [bezierPath addLineToPoint:CGPointMake(250, 150)];
    
    line.path = bezierPath.CGPath;
}
  • 闭合多边形
    也是多线段连起来的,只不过最后一条线的终点为第一条线段的起点。
- (void)drawTriangle {
    UIView *view = [self.view viewWithTag:1026];
    UILabel *label = [view viewWithTag:526];
    label.text = @"闭合多边形";
    
    CAShapeLayer *triangle = [CAShapeLayer layer];
    triangle.lineWidth = 2;
    triangle.strokeColor = [UIColor redColor].CGColor;
    triangle.fillColor = [UIColor clearColor].CGColor;
    [view.layer addSublayer:triangle];
    
    UIBezierPath *bezierPath = [UIBezierPath bezierPath];
    [bezierPath moveToPoint:CGPointMake(kDeviceWidth/2.0, 50)];
    [bezierPath addLineToPoint:CGPointMake(kDeviceWidth/2.0-100, 150)];
    [bezierPath addLineToPoint:CGPointMake(kDeviceWidth/2.0+100, 150)];
    [bezierPath addLineToPoint:CGPointMake(kDeviceWidth/2.0, 50)];
    
    triangle.path = bezierPath.CGPath;
}
  • 线端点样式
    CAShapeLayer的lineCap属性决定线端点样式,可选样式kCALineCapButt(默认)kCALineCapRound(圆角)kCALineCapSquare(平角)。默认为kCALineCapButt也是平角。

    线端点样式示例

  • 线段拐点处样式
    CAShapeLayer的lineJoin属性决定线端点样式,可选样式kCALineJoinMiter(尖角)kCALineJoinRound(圆角)kCALineJoinBevel(平角)。默认为kCALineJoinMiter

    拐角样式示例

  • 虚线

@property(nullable, copy) NSArray<NSNumber *> *lineDashPattern;

CAShapeLayer的lineDashPattern属性决定你画出一条什么样的虚线,这个属性返回一组NSNumber类型的数组,其实就是实虚相交来表示你的虚线,数组的长度由你决定(当然最好不要第一轮实虚相加超过线段长度)。比如line.lineDashPattern = @[@10,@5,@2,@8];就是表示每轮都为长度为10的实线,长度为5的虚线,长度为2的实线,长度为8的虚线,循环直到线段结束。

曲线

曲线
  • 二次贝塞尔曲线


    二次贝塞尔曲线

    二次贝塞尔曲线有一个控制点,控制点的位置决定了显示一条怎样的曲线。下面的例子,我把起点pA、终点pB、控制点pC 都画出来方便观察。

//篇幅限制 只贴主要代码
//曲线
CAShapeLayer *layerOne = [CAShapeLayer layer];
layerOne.fillColor = [UIColor clearColor].CGColor;
layerOne.strokeColor = [UIColor blackColor].CGColor;
layerOne.strokeStart = 0;
layerOne.strokeEnd = 1;
[view.layer addSublayer:layerOne];
    
//路径
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:pA];
[path addQuadCurveToPoint:pB controlPoint:pC];
    
//关联路径
layerOne.path = path.CGPath;
  • 三次贝塞尔曲线


    三次贝塞尔曲线

    三次贝塞尔曲线有两个控制点,两个控制点的位置决定了显示一条怎样的曲线。下面的例子,我把起点pA、终点pB、控制点pC、pD 都画出来方便观察。

//篇幅限制 只贴主要代码
//曲线
CAShapeLayer *layerTwo = [CAShapeLayer layer];
layerTwo.fillColor = [UIColor clearColor].CGColor;
layerTwo.strokeColor = [UIColor blackColor].CGColor;
layerTwo.strokeStart = 0;
layerTwo.strokeEnd = 1;
[view.layer addSublayer:layerTwo];
    
//路径
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:pA];
[path addCurveToPoint:pB controlPoint1:pC controlPoint2:pD];
    
//关联路径
layerTwo.path = path.CGPath;
  • 圆角矩形
- (void)drawRectRound {
    UIView *view = [self.view viewWithTag:1028];
    
    UIBezierPath *rectRound = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(kDeviceWidth/2.0-100, 50, 200, 100) byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(20, 20)];
    
    CAShapeLayer *layer = [CAShapeLayer layer];
    layer.strokeColor = [UIColor clearColor].CGColor;
    layer.fillColor = [UIColor whiteColor].CGColor;
    layer.path = rectRound.CGPath;
    
    [view.layer addSublayer:layer];
}
圆角矩形
  • 虚线圆
    如果是静态的(无动画),那么需要两个贝塞尔圆环曲线表示内圆和外圆,内圆一周,外圆实时进度。
    如果是动态的(有动画),那么可以一个贝塞尔圆环曲线表示内圆和外圆,内、外圆都一周,外圆添加动画,动画的toValue标志实时进度。
    虚线圆
- (void)drawXuCircle {
    UIView *view = [self.view viewWithTag:1029];
    
    //底部虚圆
    CAShapeLayer *xuCircle = [CAShapeLayer layer];
    xuCircle.lineWidth = 10;
    xuCircle.strokeColor = ColorWithHex(0xbebebe, 1).CGColor;
    xuCircle.fillColor = nil;
    xuCircle.lineJoin = kCALineJoinMiter;
    xuCircle.lineDashPattern = @[@2,@3];
    [view.layer addSublayer:xuCircle];
    
    //外部虚圆
    CAShapeLayer *circle = [CAShapeLayer layer];
    circle.lineWidth = 10;
    circle.strokeColor = ColorWithHex(0xa2d100, 1).CGColor;
    circle.fillColor = nil;
    circle.lineJoin = kCALineJoinMiter;
    circle.lineDashPattern = @[@2,@3];
    [view.layer addSublayer:circle];

    
    UIBezierPath *xuBezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(kDeviceWidth/2.0, 100) radius:55 startAngle:-M_PI_2 endAngle:3*M_PI_2 clockwise:YES];
    
    UIBezierPath *bezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(kDeviceWidth/2.0, 100) radius:55 startAngle:-M_PI_2 endAngle:M_PI_2 clockwise:YES];
    
    xuCircle.path = xuBezierPath.CGPath;
    circle.path = bezierPath.CGPath;
}

动画

现在我们来给一些图形加上动画,使运行起来更美观。


动画
  • 主要写了三类动画
    1.最常用的普通动画
    2.进度条动画
    3.其他属性的动画(比如这里有重复次数和逆执行)
//普通动画,strokeEnd
- (CABasicAnimation *)animComm {
    if (_animComm == nil) {
        _animComm = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        _animComm.fromValue = @0.0;
        _animComm.toValue = @1.0;
        _animComm.duration = 2.0;
    }
    return _animComm;
}

//进度条动画
- (CABasicAnimation *)animProgress {
    if (_animProgress == nil) {
        _animProgress = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        _animProgress.fromValue = @0.0;
        _animProgress.toValue = @0.7;
        _animProgress.fillMode = kCAFillModeForwards;
        _animProgress.removedOnCompletion = NO;
        _animProgress.duration = 2.0;
    }
    return _animProgress;
}


//重复次数,逆执行试用
- (CABasicAnimation *)animRepeat {
    if (_animRepeat == nil) {
        _animRepeat = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        _animRepeat.fromValue = @0.0;
        _animRepeat.toValue = @1.0;
        _animRepeat.duration = 2.0;
        _animRepeat.autoreverses = YES;
        _animRepeat.repeatCount = 10;
    }
    return _animRepeat;
}

简单的柱状图

简单柱状图

这是个非常简单的柱状图,需要注意的是柱子的三个重要部分,起点、终点、柱宽。柱子由起点根据柱宽向左右两边扩张,如下图柱子的起点是位置2而不是位置1。


柱状图
#import "SJBarChart.h"

static CGFloat const lineWidth  = 1.0;      //坐标轴线宽
static CGFloat const distance   = 20.0;     //距屏幕边距
static CGFloat const cornerW    = 10.0f;    //拐角长度
static CGFloat const barWidth   = 50.0f;    //柱状宽度
static CGFloat const space      = 30.0f;    //柱状之间的间隔
static CGFloat const scale      = 3.0f;     //柱状显示高度计算比例 *scale

@interface SJBarChart ()
{
    CGFloat selfW, selfH;
    NSArray *source;
}
@property (nonatomic, strong) CAShapeLayer *xAxis;
@property (nonatomic, strong) CAShapeLayer *yAxis;
@property (nonatomic, strong) UIScrollView *barScrollView;
@property (nonatomic, strong) CABasicAnimation *animation;
@end

@implementation SJBarChart

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        //
        selfW = frame.size.width;
        selfH = frame.size.height;
        self.backgroundColor = [UIColor lightGrayColor];
    }
    return self;
}

- (void)showBarChart:(NSArray *)sourceArray {
    source = sourceArray;
    [self addxyAxis];
    [self addSubview:self.barScrollView];
    _barScrollView.contentSize = CGSizeMake(sourceArray.count*(space+barWidth) + space, 0);
    
    [sourceArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        CAShapeLayer *bar = [self drawBar:idx];
        [_barScrollView.layer addSublayer:bar];
    }];

}

//柱状图
- (CAShapeLayer *)drawBar:(NSInteger)index {
    CAShapeLayer *layer = [CAShapeLayer layer];
    layer.fillColor = [UIColor clearColor].CGColor;
    layer.strokeColor = [UIColor redColor].CGColor;
    layer.lineWidth = barWidth;
    
    //终点y
    CGFloat y = _barScrollView.frame.size.height-60 - lineWidth/2.0 - ([[source objectAtIndex:index] floatValue] * scale);
    
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake((space + barWidth)*index + (space+barWidth/2.0), _barScrollView.frame.size.height-60)];
    [path addLineToPoint:CGPointMake((space + barWidth)*index + (space+barWidth/2.0), y)];
    layer.path = path.CGPath;
    
    [layer addAnimation:self.animation forKey:nil];
    return layer;
}

//添加坐标轴
- (void)addxyAxis {
    self.xAxis = [self lineWithStartPoint:CGPointMake(distance, selfH-30) breakPoint:CGPointMake(kDeviceWidth-distance, selfH-30) endPoint:CGPointMake(kDeviceWidth-distance-cornerW, selfH-30-cornerW)];
    self.yAxis = [self lineWithStartPoint:CGPointMake(distance+lineWidth/2.0, selfH-30) breakPoint:CGPointMake(distance, 30) endPoint:CGPointMake(distance+cornerW, 30+cornerW)];
    
    [self.layer addSublayer:self.xAxis];
    [self.layer addSublayer:self.yAxis];
}

//画坐标轴
- (CAShapeLayer *)lineWithStartPoint:(CGPoint)startPoint breakPoint:(CGPoint)breakPoint endPoint:(CGPoint)endPoint {
    CAShapeLayer *line = [CAShapeLayer layer];
    line.fillColor = [UIColor clearColor].CGColor;
    line.strokeColor = [UIColor blackColor].CGColor;
    line.lineWidth = 1.0;
    
    UIBezierPath *linePath = [UIBezierPath bezierPath];
    [linePath moveToPoint:startPoint];
    [linePath addLineToPoint:breakPoint];
    [linePath addLineToPoint:endPoint];
    line.path = linePath.CGPath;
    
    [line addAnimation:self.animation forKey:@"xyLineStrokeEndAnimation"];
    return line;
}

- (UIScrollView *)barScrollView {
    if (_barScrollView == nil) {
        _barScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(distance+lineWidth, 30, kDeviceWidth-distance*2-lineWidth-cornerW, selfH-60-lineWidth/2.0)];
        _barScrollView.bounces = NO;
        _barScrollView.showsHorizontalScrollIndicator = NO;
    }
    return _barScrollView;
}

- (CABasicAnimation *)animation {
    if (_animation == nil) {
        _animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        _animation.fromValue = @0.0;
        _animation.toValue = @1.0;
        _animation.duration = 2.0;
        _animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    }
    return _animation;
}

@end

折线图

折线图

柱状图是一条条单独的线段,折线图就是一条连起来的完整折线。

#import "SJLineChart.h"

static CGFloat const lineWidth  = 1.0;      //坐标轴线宽
static CGFloat const distance   = 20.0;     //距屏幕边距
static CGFloat const cornerW    = 10.0f;    //拐角长度
static CGFloat const space      = 50.0f;    //柱状之间的间隔
static CGFloat const scale      = 3.0f;     //直线显示高度计算比例 *scale
static CGFloat const radius     = 3.0f;     //标记每个点的小圆半径


@interface SJLineChart ()
{
    CGFloat selfW, selfH;
    NSArray *source;
}
@property (nonatomic, strong) CAShapeLayer *xAxis;
@property (nonatomic, strong) CAShapeLayer *yAxis;
@property (nonatomic, strong) UIScrollView *lineScrollView;
@property (nonatomic, strong) CABasicAnimation *animation;

@end
@implementation SJLineChart

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        //
        selfW = frame.size.width;
        selfH = frame.size.height;
        self.backgroundColor = [UIColor lightGrayColor];
    }
    return self;
}

- (void)showLineChart:(NSArray *)sourceArray {
    source = sourceArray;
    [self addxyAxis];
    [self addSubview:self.lineScrollView];
    _lineScrollView.contentSize = CGSizeMake(sourceArray.count*(space+1), 0);
    [self drawLineChart:sourceArray];
    [self drawPoint:sourceArray];
}

- (void)drawLineChart:(NSArray *)array {
    CAShapeLayer *lineLayer = [CAShapeLayer layer];
    lineLayer.fillColor = [UIColor clearColor].CGColor;
    lineLayer.strokeColor = [UIColor redColor].CGColor;
    lineLayer.lineWidth = 2.0;
    //轨迹
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(space, _lineScrollView.frame.size.height - 60 - lineWidth/2.0 - ([[array objectAtIndex:0] floatValue] * scale))];
    
    [array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        //
        if (idx > 0) {
            CGFloat y = _lineScrollView.frame.size.height-60 - lineWidth/2.0 - ([obj floatValue] * scale);
            [path addLineToPoint:CGPointMake(space*(idx+1), y)];
        }
    }];
    lineLayer.path = path.CGPath;
    [self.lineScrollView.layer addSublayer:lineLayer];
    [lineLayer addAnimation:self.animation forKey:@"lineStrokeEndAnimation"];
    
}

//把点标出来
- (void)drawPoint:(NSArray *)array {
    
    [array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        //
        CGFloat y = _lineScrollView.frame.size.height - 60 - lineWidth/2.0 - [obj floatValue]*scale;
        
        UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(space * (idx+1), y) radius:radius startAngle:0 endAngle:(M_PI)*2 clockwise:YES];
        
        CAShapeLayer *circleLayer = [CAShapeLayer layer];
        circleLayer.fillColor = [UIColor orangeColor].CGColor;
        circleLayer.strokeColor = [UIColor clearColor].CGColor;
        circleLayer.path = circlePath.CGPath;
        
        [_lineScrollView.layer addSublayer:circleLayer];
        
    }];
    
}

//添加坐标轴
- (void)addxyAxis {
    self.xAxis = [self lineWithStartPoint:CGPointMake(distance, selfH-30) breakPoint:CGPointMake(kDeviceWidth-distance, selfH-30) endPoint:CGPointMake(kDeviceWidth-distance-cornerW, selfH-30-cornerW)];
    self.yAxis = [self lineWithStartPoint:CGPointMake(distance+lineWidth/2.0, selfH-30) breakPoint:CGPointMake(distance, 30) endPoint:CGPointMake(distance+cornerW, 30+cornerW)];
    
    [self.layer addSublayer:self.xAxis];
    [self.layer addSublayer:self.yAxis];
}

//画坐标轴
- (CAShapeLayer *)lineWithStartPoint:(CGPoint)startPoint breakPoint:(CGPoint)breakPoint endPoint:(CGPoint)endPoint {
    CAShapeLayer *line = [CAShapeLayer layer];
    line.fillColor = [UIColor clearColor].CGColor;
    line.strokeColor = [UIColor blackColor].CGColor;
    line.lineWidth = 1.0;
    
    UIBezierPath *linePath = [UIBezierPath bezierPath];
    [linePath moveToPoint:startPoint];
    [linePath addLineToPoint:breakPoint];
    [linePath addLineToPoint:endPoint];
    line.path = linePath.CGPath;
    
    [line addAnimation:self.animation forKey:@"xyLineStrokeEndAnimation"];
    return line;
}

- (UIScrollView *)lineScrollView {
    if (_lineScrollView == nil) {
        _lineScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(distance+lineWidth, 30, kDeviceWidth-distance*2-lineWidth, selfH-60-lineWidth/2.0)];
        _lineScrollView.bounces = NO;
        _lineScrollView.showsHorizontalScrollIndicator = NO;
    }
    return _lineScrollView;
}

- (CABasicAnimation *)animation {
    if (_animation == nil) {
        _animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        _animation.fromValue = @0.0;
        _animation.toValue = @1.0;
        _animation.duration = 2.0;
        _animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    }
    return _animation;
}

@end

结语

感谢阅读全文的朋友。
☞demo地址 https://github.com/SPIREJ/SJCAShapeLayer

相关阅读
上一篇:CAShapeLayer & UIBezierPath & CABasicAnimation

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

推荐阅读更多精彩内容