乾貨之運用CALayer建立星級評等組件(五角星),乾貨calayer

來源:互聯網
上載者:User

乾貨之運用CALayer建立星級評等組件(五角星),乾貨calayer

本篇記錄星級評等組件的建立過程以及CALayer的運用。

為了實現一個星級評等的組件,使用了CALayer,涉及到mask、CGPathRef、UIBezierPath、動畫和一個計算多角星關鍵節點的演算法。

CALayer管理基於映像的內容,並讓我們可以在內容上添加動畫。UIView及其子類擁有一個屬性layer,我們可以運用該屬性做出非常多的效果。例如圓角、多邊形、甚至自訂形狀的view,局部遮擋,擦除模糊效果,局部內容依次閃亮效果,弧形進度條等等。

首先查看CALayer的一個屬性mask,這個屬性也是CALayer類型,跟他的名字意義一樣,就是用來遮擋內容的,預設為nil,所以我們可以看到一個UIView上的內容。如果給一個可見內容的view的layer屬性賦值CALayer執行個體,那這個view立刻就“消失”了。如下代碼:

CALayer *maskLayer = [CALayer layer];

maskLayer.frame = testView.bounds;

self.layer.mask = maskLayer;

再看另一個類CAShapeLayer,繼承與CALayer,並具有眾多額外屬性,例如path、fillColor、strokeColor。其中的path屬性,可以決定“不遮擋”路徑範圍內的內容。如果我們將之前的mask屬性賦值為一個CAShapeLayer執行個體,或者在之前的CALayer執行個體上addSublayer該執行個體,並指定一個中心圓的路徑,那就實現了常見的圓形頭像顯示效果。與設定UIView的layer的corner作用一樣。

需要注意一點,如果添加的CAShapeLayer的fillColor為[UIColor clearColor].CGColor,即使在path範圍內,也將失去“不遮擋”作用,而該值預設為不透明黑色。簡單來說,如果透明就將遮擋內容。可以發現,mask及其sublayer上的填充和路徑顏色,都是不會顯示的,只用於決定是否遮擋自身所在layer的內容。

利用這一特點,可以實現“開門”和“關門”的類似效果,在一個CALayer上添加兩個sublayer作為“門”。還可以實現潑墨等更多的效果。

值得一提的是,每個sublayer也可以利用frame和mask屬性做出相對於view的局部的遮擋效果。

回到CAShapeLayer的fillColor屬性,該屬性將path範圍內填充上某種顏色;strokeColor是指定path軌跡上的顏色,strokeStart、strokeEnd指定軌跡的開始和結束的繪製點;還有line相關的屬性指定線的屬性。運用這些屬性,可以做出任意形狀的進度條、載入動畫等。

從以上記錄可以看出path屬性很重要,該屬性是CGPathRef類型。常用建立方法為 CGPathCreateMutable(void)系列方法:

    CGMutablePathRef path = CGPathCreateMutable();    CGPoint firstPoint = [[keyPointsArray firstObject] CGPointValue];    CGPathMoveToPoint(path, nil, firstPoint.x, firstPoint.y);        for (int i = 1; i < keyPointsArray.count; i++) {        CGPoint currentPoint = [keyPointsArray[i] CGPointValue];        CGPathAddLineToPoint(path, nil, currentPoint.x, currentPoint.y);    }        CGPathAddLineToPoint(path, nil, firstPoint.x, firstPoint.y);        _maskLayer.path = path;    _starShapeLayer.path = path;    CGPathRelease(path);

也可以使用UIBezierPath的系列類方法,添加好路徑後,由方法- (CGPathRef)CGPath得到path。例如:

    UIBezierPath *bezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(_bgShapeLayer.frame.size.width / 2.0, _bgShapeLayer.frame.size.height / 2.0) radius:_radius startAngle:0 endAngle: 2 * M_PI clockwise:YES];    _bgShapeLayer.path = bezierPath.CGPath;

 

下面開始記錄星級評等組件的建立。

為最終效果:

點擊星星的左右半邊會決定填充一半還是全部內容。還可以設定百分比填充內容。支援>5角星。

單個星星的內部設計為:view設定了灰色背景色;view.layer.mask的path為五角星;view.layer添加一個bgShapeLayer,背景為黃色,控制其width實現填充百分比;再添加一個starShapeLayer,path為同樣的五角星,設定路徑顏色作為邊框。

這裡的痛點在於計算多角星的交點,如果為五角星,則需要10個關鍵點的座標。

下面記錄求交點的演算法:

1.先確定一個初始頂點

2.根據等分弧度和半徑,由三角函數的正餘弦相關公式求得全部外頂點

3.求出內交點的兩條相交直線

4.由二元一次方程求出交點

 

具體實現代碼如下:

- (NSArray *)getStarKeyPoints{    CGPoint center = CGPointMake(self.frame.size.width / 2.0, _radius);    CGFloat sectionAngle = 2 * M_PI / _topPointCount;        NSMutableArray *keyPointsArray = [NSMutableArray array];    CGPoint firstPoint = CGPointMake(center.x, 0);    [keyPointsArray addObject:[NSValue valueWithCGPoint:firstPoint]];        //外圍頂點    for (int i = 1; i < _topPointCount; i++) {        CGFloat x = cosf(i * sectionAngle - M_PI_2) * _radius;        CGFloat y = sinf(i * sectionAngle - M_PI_2) * _radius;                CGPoint point = CGPointMake(x + center.x, y + center.y);                [keyPointsArray addObject:[NSValue valueWithCGPoint:point]];    }        //內交點    NSMutableArray *crossPointsArray = [NSMutableArray array];    //採用二元一次方程求解    //AC點確定直線方程y = kx + b    //過B點直線y = B.y    for (int i = 0; i < _topPointCount; i++) {        CGPoint A = [keyPointsArray[i] CGPointValue];                NSInteger index = i + 1;        if (index > _topPointCount - 1) {            index -= _topPointCount;        }        CGPoint B = [keyPointsArray[index] CGPointValue];                index = i + 2;        if (index > _topPointCount - 1) {            index -= _topPointCount;        }        CGPoint C = [keyPointsArray[index] CGPointValue];                index = i - 1;        if (index < 0) {            index += _topPointCount;        }        CGPoint E = [keyPointsArray[index] CGPointValue];                CGFloat F_x = 0.0, F_y = 0.0, k1 = 0.0, k2 = 0.0, b1 = 0.0, b2 = 0.0;                if (A.x == C.x) {            F_x = A.x;        } else {            k1 = (A.y - C.y) / (A.x - C.x);            b1 = A.y - k1 * A.x;        }                if (B.x == E.x) {            F_x = B.x;        } else {            k2 = (B.y - E.y) / (B.x - E.x);            b2 = B.y - k2 * B.x;        }                if (A.x == C.x) {            F_y = k2 * F_x + b2;        }else if (B.x == E.x) {            F_y = k1 * F_x + b1;        }else{            if (k1 == 0) {                F_y = A.y;                F_x = (F_y - b2) / k2;            } else {                F_y = (b1 * k2 - b2 * k1) / (k2 - k1);                F_x = (F_y - b1) / k1;            }        }        CGPoint pointF = CGPointMake(F_x, F_y);        [crossPointsArray addObject:[NSValue valueWithCGPoint:pointF]];    }        //合并資料    for (int i = 0; i < crossPointsArray.count; i++) {        [keyPointsArray insertObject:crossPointsArray[i] atIndex:(i * 2 + 1)];    }        return keyPointsArray;}

 

將關鍵點線段添加到path中,產生多角形路徑,用於mask和邊框layer。

最後添加點選手勢,根據觸摸點的範圍確定操作。

 

實現單個五角星以後,常見的將五個五角星並排放置,統一管理每個五角星的填充百分比。

 

ALWStarComment已在Base項目中更新:https://github.com/ALongWay/base.git

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.