歌詞處理-歌詞變色 - (Obj-C)

這里自定義了一個Label,通過DrawRect方法獲取Label的圖形上下文,使用混合填充的方式實現(xiàn)Label繪制顏色

  • 先介紹一下混合填充的參數(shù):
void UIRectFillUsingBlendMode(CGRect rect, CGBlendMode blendMode);

CGBlendMode參數(shù)為一個枚舉類型:

    /*              對應(yīng)公式(其余是固定的):
        result, source, and destination colors with alpha; 
        Ra, Sa, and Da are the alpha components of these colors.
            R --> result
            S --> source
            D --> destination
     
         kCGBlendModeNormal,                 R = S + D*(1 - Sa)
         kCGBlendModeMultiply,
         kCGBlendModeScreen,
         kCGBlendModeOverlay,
         kCGBlendModeDarken,
         kCGBlendModeLighten,
         kCGBlendModeColorDodge,
         kCGBlendModeColorBurn,
         kCGBlendModeSoftLight,
         kCGBlendModeHardLight,
         kCGBlendModeDifference,
         kCGBlendModeExclusion,
         kCGBlendModeHue,
         kCGBlendModeSaturation,
         kCGBlendModeColor,
         kCGBlendModeLuminosity,
     
     
         kCGBlendModeClear,                   R = 0
         kCGBlendModeCopy,                    R = S
         kCGBlendModeSourceIn,                R = S*Da
         kCGBlendModeSourceOut,               R = S*(1 - Da)
         kCGBlendModeSourceAtop,              R = S*Da + D*(1 - Sa)
         kCGBlendModeDestinationOver,         R = S*(1 - Da) + D
         kCGBlendModeDestinationIn,           R = D*Sa
         kCGBlendModeDestinationOut,          R = D*(1 - Sa)
         kCGBlendModeDestinationAtop,         R = S*(1 - Da) + D*Sa
         kCGBlendModeXOR,                     R = S*(1 - Da) + D*(1 - Sa)
         kCGBlendModePlusDarker,              R = MAX(0, (1 - D) + (1 - S))
         kCGBlendModePlusLighter              R = MIN(1, S + D)
     */

kCGBlendModeNormal樣式公式為:

    R = S + D * ( 1 - Sa )
    結(jié)果 = 源顏色 + 目標(biāo)顏色 * (1-源顏色各透明組件的透明度)


以kCGBlendModeNormal為例,在這里,我們填充的是一個顏色,顏色的透明度為1,也就是源顏色透明度為1,所以Sa = 1

  R = S + D*(1 - Sa) --> R = S + D*(1 - 1) --> R = S

這種情況下, kCGBlendModeNormal 和kCGBlendModeCopy類型是一樣的效果(使用的就是源顏色填充)

    kCGBlendModeCopy,        R = S

  • 實現(xiàn)歌詞變色我們需要使用到的是kCGBlendModeSourceIn:
    kCGBlendModeSourceIn,    R = S*Da -> 結(jié)果 = 源顏色*目標(biāo)透明度
我們這個案例中的源顏色和目標(biāo)顏色:

    源顏色   -->  就是要繪制上去的顏色/填充色  ([[UIColor greenColor] setFill];)
    目標(biāo)顏色 -->  Label當(dāng)前的顏色(文字顏色和透明),上下文中已經(jīng)有的顏色
D: Label
        默認(rèn)的文字部分有有顏色     透明度是1  
        其余部分使用的是透明色     透明度是0

S: 填充色(源顏色)
        當(dāng)前圖形上下文中的內(nèi)容的不透明度

      結(jié)合公式: R = S*Da ,當(dāng)混合填充時

文字部分:  R = S * Da (Da=1) -> R = S -> 顯示的就是源顏色(填充色)
其余部分:  R = S * Da (Da=0) -> R = 0 -> 不進(jìn)行填充/顯示目標(biāo)顏色原有的顏色(透明色)

  • 聲明屬性,存放當(dāng)前變色歌詞進(jìn)度,在setter方法中執(zhí)行重繪
// 更新進(jìn)度的時候執(zhí)行重繪
- (void)setProgress:(CGFloat)progress{

    _progress = progress;
    // 執(zhí)行重繪
    [self setNeedsDisplay];
}
  • 設(shè)置歌詞變色的進(jìn)度

lrc格式的歌詞文件無法實現(xiàn)根據(jù)節(jié)奏設(shè)置變色進(jìn)度,這里取平均值:
每一句歌詞在每句歌詞顯示的總時間內(nèi),勻速的變色

    
     平均速度進(jìn)行計算 : (當(dāng)前播放時間 - 當(dāng)前句起始時間) / 當(dāng)前句總時間
     當(dāng)前句總時間: (下一句的起始時間 - 當(dāng)前句的起始時間)

因為歌詞變色進(jìn)度也是需要實時更新的,所以也是需要在控制器下的定時器方法內(nèi)執(zhí)行的,這里就用到了當(dāng)當(dāng)前歌詞索引為最后一條時,自定義的一條虛擬歌詞對象

    CGFloat averageProgress = ([JSMusciManager sharedMusicManager].currentTime - currentLyric.initialTime) / (nextLyric.initialTime - currentLyric.initialTime);

接下來就是導(dǎo)入自定義Label頭文件,身份檢測器下綁定,修改Label類型,傳遞數(shù)據(jù),這樣就可以實現(xiàn)歌詞變色了

歌詞變色.png

自定義Label代碼:

#import "JSColorLabel.h"

@implementation JSColorLabel


- (void)drawRect:(CGRect)rect {
    // 調(diào)用父類方法: 將Label上的文字繪制上
    [super drawRect:rect];
    
    // 設(shè)置填充色
    // [[UIColor greenColor] setStroke]; // 描邊
    [[UIColor greenColor] setFill]; // 填充
    
    // 設(shè)置填充色的區(qū)域 (默認(rèn)文字為白色,填充后為綠色,只需要根據(jù)當(dāng)前歌詞顯示進(jìn)度來改變填充的寬度,其他不變)
    rect = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width *self.progress, rect.size.height);
    
    // 渲染
    // 在某個區(qū)域中使用混合模式進(jìn)行填充
    /*
        kCGBlendModeNormal公式: R = S + D*(1 - Sa) --> 結(jié)果 = 源顏色 + 目標(biāo)顏色 * (1-源顏色各透明組件的透明度)
     在這里;
            源顏色  -->  就是要繪制上去的顏色/填充色  ([[UIColor greenColor] setFill];)
            目標(biāo)顏色 --> Label當(dāng)前的顏色(白色和透明),上下文中已經(jīng)有的顏色
     
     */
    UIRectFillUsingBlendMode(rect, kCGBlendModeSourceIn);
    
    /*              對應(yīng)公式(其余是固定的):
     
        result, source, and destination colors with alpha; 
        Ra, Sa, and Da are the alpha components of these colors.
            R --> result
            S --> source
            D --> destination
     
         kCGBlendModeNormal,                 R = S + D*(1 - Sa)
         kCGBlendModeMultiply,
         kCGBlendModeScreen,
         kCGBlendModeOverlay,
         kCGBlendModeDarken,
         kCGBlendModeLighten,
         kCGBlendModeColorDodge,
         kCGBlendModeColorBurn,
         kCGBlendModeSoftLight,
         kCGBlendModeHardLight,
         kCGBlendModeDifference,
         kCGBlendModeExclusion,
         kCGBlendModeHue,
         kCGBlendModeSaturation,
         kCGBlendModeColor,
         kCGBlendModeLuminosity,
     
     
         kCGBlendModeClear,                   R = 0
         kCGBlendModeCopy,                    R = S
         kCGBlendModeSourceIn,                R = S*Da
         kCGBlendModeSourceOut,               R = S*(1 - Da)
         kCGBlendModeSourceAtop,              R = S*Da + D*(1 - Sa)
         kCGBlendModeDestinationOver,         R = S*(1 - Da) + D
         kCGBlendModeDestinationIn,           R = D*Sa
         kCGBlendModeDestinationOut,          R = D*(1 - Sa)
         kCGBlendModeDestinationAtop,         R = S*(1 - Da) + D*Sa
         kCGBlendModeXOR,                     R = S*(1 - Da) + D*(1 - Sa)
         kCGBlendModePlusDarker,              R = MAX(0, (1 - D) + (1 - S))
         kCGBlendModePlusLighter              R = MIN(1, S + D)
     */
}

// 更新進(jìn)度的時候執(zhí)行重繪
- (void)setProgress:(CGFloat)progress{
    
    _progress = progress;
    
    // 執(zhí)行重繪
    [self setNeedsDisplay];
}

@end

控制器下更新歌詞方法中計算平均進(jìn)度,并給Label的progress屬性賦值

// 更新歌詞
- (void)updateLyric{
    
    // 當(dāng)前歌詞
    JSLyricModel *currentLyric = self.lyricModelArray[self.currentLyricIndex];
    
    // 下一句歌詞  ( 2.判斷越界問題)
    JSLyricModel *nextLyric = nil;
    if (self.currentLyricIndex == self.lyricModelArray.count - 1) {
        
        // 創(chuàng)建一個最大的下一句歌詞
        nextLyric = [[JSLyricModel alloc]init];
        // 給自定義出來的最后一條歌詞設(shè)置數(shù)據(jù)  (設(shè)置成最后一條歌詞的數(shù)據(jù))
        nextLyric.content = currentLyric.content;
        // 因為當(dāng)前索引已經(jīng)是最后一條歌詞,所以上面的歌詞賦值就相當(dāng)于nextLyric.content = [self.lyricModelArray lastObject].content;
        // 直接設(shè)置成歌曲的總時長
        nextLyric.initialTime = [JSMusciManager sharedMusicManager].duration;
        
    }else{
        
        nextLyric = self.lyricModelArray[self.currentLyricIndex + 1];
    }
    
    // 正向調(diào)整進(jìn)度(判斷越界問題): 判斷時間,改變當(dāng)前的歌詞的索引  : 當(dāng)前播放時間 > 下一句歌詞的起始時間 歌詞索引 +1
    if ([JSMusciManager sharedMusicManager].currentTime > nextLyric.initialTime && self.currentLyricIndex < self.lyricModelArray.count - 1) {
        
        self.currentLyricIndex++;
        
        //  拖拽進(jìn)度條時,只需要顯示最近當(dāng)前歌詞,防止拖動歌詞逐條跳動
        [self updateLyric];
        // 1. 當(dāng)累加到正確的當(dāng)前歌詞索引時,下面才給歌詞賦值,否則遞歸調(diào)用返回
        return;
        // 如果不進(jìn)行遞歸調(diào)用直接return: 這里更新數(shù)據(jù)的定時器間隔時間為0.1s,假如將進(jìn)度條拖拽到歌詞索引60的位置,那么等到定時器自動調(diào)用到到歌詞索引為60的歌詞數(shù)據(jù)時,需要6s的時間才可以
        
    }
    
    // 反向調(diào)整進(jìn)度(判斷越界問題): 當(dāng)前時間 < 當(dāng)前句歌詞的初始時間 歌詞索引-1
    if ([JSMusciManager sharedMusicManager].currentTime < currentLyric.initialTime && self.currentLyricIndex > 0) {
        
        self.currentLyricIndex--;
        [self updateLyric];
        return;
    }
    
    // 設(shè)置歌詞
    self.verticalLyricLabel.text = self.lyricModelArray[self.currentLyricIndex].content;
    self.horizonLyricLabel.text = self.lyricModelArray[self.currentLyricIndex].content;
    
#pragma mark -- 設(shè)置歌詞變色
    
    /*          設(shè)置歌詞變色進(jìn)度
     
         平均速度進(jìn)行計算 : (當(dāng)前播放時間 - 當(dāng)前句起始時間) / 當(dāng)前句總時間
            當(dāng)前句總時間 :   下一句的起始時間 - 當(dāng)前句的起始時間)
     
     */
    
    CGFloat averageProgress = ([JSMusciManager sharedMusicManager].currentTime - currentLyric.initialTime) / (nextLyric.initialTime - currentLyric.initialTime);
    
    self.horizonLyricLabel.progress = averageProgress;
    self.verticalLyricLabel.progress = averageProgress;
    
}

為了將功能模塊獨立出來,所以每個小的功能都封裝了一個方法
updateLyric(更新歌詞方法)會在updateData(更新數(shù)據(jù)的方法)中調(diào)用
updateData是一個定時器計時調(diào)用的方法

self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(updateData) userInfo:nil repeats:YES];

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,647評論 4 61
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,323評論 25 708
  • 大學(xué)生對于錢,職業(yè),人生和愛情的態(tài)度與迷茫,就像你問一個哲學(xué)家:“你是誰,你從哪里來,你到哪里去?”是一樣的,似乎...
    小啤酒肚閱讀 239評論 0 0
  • 其實我知道我的問題,因為一直在逃避去解決問題導(dǎo)致我的焦慮越來越重,我不想在這樣痛苦下去了,so,改變從現(xiàn)在開始吧!...
    努力成長的小樹閱讀 286評論 0 1
  • 這一天下午最后一節(jié)課是英語課,也是這個學(xué)期最后一節(jié)課,英語老師分析期末卷子試題,這個學(xué)期就結(jié)束了。離高考也...
    有機蝸牛閱讀 589評論 0 3

友情鏈接更多精彩內(nèi)容