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

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

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

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

    /*              對(duì)應(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為例,在這里,我們填充的是一個(gè)顏色,顏色的透明度為1,也就是源顏色透明度為1,所以Sa = 1

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

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

    kCGBlendModeCopy,        R = S

  • 實(shí)現(xiàn)歌詞變色我們需要使用到的是kCGBlendModeSourceIn:
    kCGBlendModeSourceIn,    R = S*Da -> 結(jié)果 = 源顏色*目標(biāo)透明度
我們這個(gè)案例中的源顏色和目標(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)混合填充時(shí)

文字部分:  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)度的時(shí)候執(zhí)行重繪
- (void)setProgress:(CGFloat)progress{

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

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

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

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

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

接下來就是導(dǎo)入自定義Label頭文件,身份檢測器下綁定,修改Label類型,傳遞數(shù)據(jù),這樣就可以實(shí)現(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);
    
    // 渲染
    // 在某個(gè)區(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);
    
    /*              對(duì)應(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)度的時(shí)候執(zhí)行重繪
- (void)setProgress:(CGFloat)progress{
    
    _progress = progress;
    
    // 執(zhí)行重繪
    [self setNeedsDisplay];
}

@end

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

為了將功能模塊獨(dú)立出來,所以每個(gè)小的功能都封裝了一個(gè)方法
updateLyric(更新歌詞方法)會(huì)在updateData(更新數(shù)據(jù)的方法)中調(diào)用
updateData是一個(gè)定時(shí)器計(jì)時(shí)調(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)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市跷叉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌寓娩,老刑警劉巖荔泳,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異持钉,居然都是意外死亡投储,警方通過查閱死者的電腦和手機(jī)第练,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玛荞,“玉大人娇掏,你說我怎么就攤上這事⊙校” “怎么了婴梧?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長客蹋。 經(jīng)常有香客問我塞蹭,道長,這世上最難降的妖魔是什么讶坯? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任番电,我火速辦了婚禮,結(jié)果婚禮上辆琅,老公的妹妹穿的比我還像新娘漱办。我一直安慰自己,他們只是感情好婉烟,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布娩井。 她就那樣靜靜地躺著,像睡著了一般似袁。 火紅的嫁衣襯著肌膚如雪洞辣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天叔营,我揣著相機(jī)與錄音屋彪,去河邊找鬼。 笑死绒尊,一個(gè)胖子當(dāng)著我的面吹牛畜挥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播婴谱,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蟹但,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了谭羔?” 一聲冷哼從身側(cè)響起华糖,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瘟裸,沒想到半個(gè)月后客叉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年兼搏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了卵慰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡佛呻,死狀恐怖裳朋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吓著,我是刑警寧澤鲤嫡,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站绑莺,受9級(jí)特大地震影響暖眼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜紊撕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一罢荡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧对扶,春花似錦区赵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至络凿,卻和暖如春骡送,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背絮记。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來泰國打工摔踱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人怨愤。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓派敷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親撰洗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子篮愉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件差导、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,094評(píng)論 4 62
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,078評(píng)論 25 707
  • 大學(xué)生對(duì)于錢犀被,職業(yè)项玛,人生和愛情的態(tài)度與迷茫,就像你問一個(gè)哲學(xué)家:“你是誰弱判,你從哪里來,你到哪里去锥惋?”是一樣的昌腰,似乎...
    小啤酒肚閱讀 198評(píng)論 0 0
  • 其實(shí)我知道我的問題,因?yàn)橐恢痹谔颖苋ソ鉀Q問題導(dǎo)致我的焦慮越來越重膀跌,我不想在這樣痛苦下去了遭商,so,改變從現(xiàn)在開始吧!...
    努力成長的小樹閱讀 262評(píng)論 0 1
  • 這一天下午最后一節(jié)課是英語課捅伤,也是這個(gè)學(xué)期最后一節(jié)課劫流,英語老師分析期末卷子試題,這個(gè)學(xué)期就結(jié)束了丛忆。離高考也...
    有機(jī)蝸牛閱讀 513評(píng)論 0 3