IOS在視訊上新增字幕效果的基本思路是:
-
使用自定義的
CATextLayer
文字圖層或者CAShapeLayer
文字圖層,新增到視訊的Layer
上建立使用者自定義的字幕效果。這兩者的區別是:CATextLayer
支援設定簡單的文字效果,包括文字的內容、字型、字號大小、對其方式、文字顏色、背景顏色等基本的屬性;CAShapeLayer
功能更強大,提供了CATextLayer
沒有的邊框大小、邊框顏色等設定,如果需要更高階的文字內容展示,需要使用CATextLayer
配合UIBezierPath
來定製自定義的文字內容。 -
通過設定Layer圖層的動畫來控制字幕的時間點和時間長度,這裡有一個坑如果單獨設定
CATextLayer
或者CAShapeLayer
的動畫不能控制開始的時間,需要額外新增一個CALayer
圖層,把文字圖層CATextLayer
或者CAShapeLayer
新增到父CALayer
圖層中,在文字圖層CATextLayer
或者CAShapeLayer
上設定開始的動畫
NSTimeInterval animatedInStartTime = startTime + initAnimationDuration;
CABasicAnimation *fadeInAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeInAnimation.fromValue = @0.0f;
fadeInAnimation.toValue = @1.0f;
fadeInAnimation.additive = NO;
fadeInAnimation.removedOnCompletion = NO;
fadeInAnimation.beginTime = animatedInStartTime;
fadeInAnimation.duration = animationDuration;
fadeInAnimation.autoreverses = NO;
fadeInAnimation.fillMode = kCAFillModeBoth;
[textLayer addAnimation:fadeInAnimation forKey:@"opacity"];
複製程式碼
在父CALayer
圖層上設定結束的動畫,這樣設定才能實現使用者自定義的時間點和時間長度
NSTimeInterval animatedOutStartTime = startTime + duration - animationDuration;
CABasicAnimation *fadeOutAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeOutAnimation.fromValue = @1.0f;
fadeOutAnimation.toValue = @0.0f;
fadeOutAnimation.additive = NO;
fadeOutAnimation.removedOnCompletion = NO;
fadeOutAnimation.beginTime = animatedOutStartTime;
fadeOutAnimation.duration = animationDuration;
fadeOutAnimation.autoreverses = NO;
fadeOutAnimation.fillMode = kCAFillModeBoth;
[animatedTitleLayer addAnimation:fadeOutAnimation forKey:@"opacity"];
複製程式碼
完整的程式碼
- (CALayer *)buildLayerbuildTxt:(NSString*)text
textSize:(CGFloat)textSize
textColor:(UIColor*)textColor
strokeColor:(UIColor*)strokeColor
opacity:(CGFloat)opacity
textRect:(CGRect)textRect
fontPath:(NSString*)fontPath
viewBounds:(CGSize)viewBounds
startTime:(NSTimeInterval)startTime
duration:(NSTimeInterval)duration
{
if (!text || [text isEqualToString:@""])
{
return nil;
}
// Create a layer for the overall title animation.
CALayer *animatedTitleLayer = [CALayer layer];
// 1. Create a layer for the text of the title.
CATextLayer *titleLayer = [CATextLayer layer];
titleLayer.string = text;
titleLayer.font = (__bridge CFTypeRef)(@"Helvetica");
titleLayer.fontSize = textSize;
titleLayer.alignmentMode = kCAAlignmentCenter;
titleLayer.bounds = CGRectMake(0, 0, textRect.size.width, textRect.size.height);
titleLayer.foregroundColor = textColor.CGColor;
titleLayer.backgroundColor = [UIColor clearColor].CGColor;
// [animatedTitleLayer addSublayer:titleLayer];
// 新增文字以及邊框效果
UIFont *font = nil;
if ((fontPath != nil) && (fontPath.length > 0)) {
font = [[FLVideoEditFontManager sharedFLVideoEditFontManager] fontWithPath:fontPath size:textSize];
titleLayer.font = CGFontCreateWithFontName((__bridge CFStringRef)font.fontName);
}
if (font == nil) {
titleLayer.font = (__bridge CFTypeRef)(@"Helvetica");
}
UIBezierPath *path = nil;
if (font) {
path = [FLLayerBuilderTool createPathForText:text fontHeight:textSize fontName:(__bridge CFStringRef)(font.fontName)];
}
else
{
path = [FLLayerBuilderTool createPathForText:text fontHeight:textSize fontName:CFSTR("Helvetica")];
}
CGRect rectPath = CGPathGetBoundingBox(path.CGPath);
CAShapeLayer *textLayer = [CAShapeLayer layer];
textLayer.path = path.CGPath;
textLayer.lineWidth = 1;
if (strokeColor != nil) {
textLayer.strokeColor = strokeColor.CGColor;
}
if (textColor != nil) {
textLayer.fillColor = textColor.CGColor;
}
textLayer.lineJoin = kCALineJoinRound;
textLayer.lineCap = kCALineCapRound;
textLayer.geometryFlipped = NO;
textLayer.opacity = opacity;
textLayer.bounds = CGRectMake(0, 0, rectPath.size.width, textSize+10);
[animatedTitleLayer addSublayer:textLayer];
// 動畫圖層位置
animatedTitleLayer.position = CGPointMake(textRect.origin.x+textRect.size.width/2, viewBounds.height - textRect.size.height/2 - textRect.origin.y);
NSTimeInterval initAnimationDuration = 0.1f;
NSTimeInterval animationDuration = 0.1f;
// 3.顯示動畫
NSTimeInterval animatedInStartTime = startTime + initAnimationDuration;
CABasicAnimation *fadeInAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeInAnimation.fromValue = @0.0f;
fadeInAnimation.toValue = @1.0f;
fadeInAnimation.additive = NO;
fadeInAnimation.removedOnCompletion = NO;
fadeInAnimation.beginTime = animatedInStartTime;
fadeInAnimation.duration = animationDuration;
fadeInAnimation.autoreverses = NO;
fadeInAnimation.fillMode = kCAFillModeBoth;
[textLayer addAnimation:fadeInAnimation forKey:@"opacity"];
NSTimeInterval animatedOutStartTime = startTime + duration - animationDuration;
CABasicAnimation *fadeOutAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeOutAnimation.fromValue = @1.0f;
fadeOutAnimation.toValue = @0.0f;
fadeOutAnimation.additive = NO;
fadeOutAnimation.removedOnCompletion = NO;
fadeOutAnimation.beginTime = animatedOutStartTime;
fadeOutAnimation.duration = animationDuration;
fadeOutAnimation.autoreverses = NO;
fadeOutAnimation.fillMode = kCAFillModeBoth;
[animatedTitleLayer addAnimation:fadeOutAnimation forKey:@"opacity"];
return animatedTitleLayer;
}
複製程式碼
依賴的工具類FLLayerBuilderTool.m檔案:
#import "FLLayerBuilderTool.h"
#import <CoreText/CoreText.h>
@implementation FLLayerBuilderTool
+ (UIBezierPath*) createPathForText:(NSString*)string fontHeight:(CGFloat)height fontName:(CFStringRef)fontName
{
if ([string length] < 1)
return nil;
UIBezierPath *combinedGlyphsPath = nil;
CGMutablePathRef letters = CGPathCreateMutable();
CTFontRef font = CTFontCreateWithName(fontName, height, NULL);
if (font == nil) {
font = (__bridge CFTypeRef)(@"Helvetica");
}
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)font, kCTFontAttributeName,
nil];
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:string
attributes:attrs];
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attrString);
CFArrayRef runArray = CTLineGetGlyphRuns(line);
// for each RUN
for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runArray); runIndex++)
{
// Get FONT for this run
CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex);
CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName);
// for each GLYPH in run
for (CFIndex runGlyphIndex = 0; runGlyphIndex < CTRunGetGlyphCount(run); runGlyphIndex++)
{
// get Glyph & Glyph-data
CFRange thisGlyphRange = CFRangeMake(runGlyphIndex, 1);
CGGlyph glyph;
CGPoint position;
CTRunGetGlyphs(run, thisGlyphRange, &glyph);
CTRunGetPositions(run, thisGlyphRange, &position);
// Get PATH of outline
{
CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL);
CGAffineTransform t = CGAffineTransformMakeTranslation(position.x, position.y);
CGPathAddPath(letters, &t, letter);
CGPathRelease(letter);
}
}
}
CFRelease(line);
combinedGlyphsPath = [UIBezierPath bezierPath];
[combinedGlyphsPath moveToPoint:CGPointZero];
[combinedGlyphsPath appendPath:[UIBezierPath bezierPathWithCGPath:letters]];
CGPathRelease(letters);
CFRelease(font);
if (attrString)
{
attrString = nil;
}
return combinedGlyphsPath;
}
@end
複製程式碼
參考資料:
視訊特效製作:如何給視訊新增邊框、水印、動畫以及3D效果
視訊特效製作2
AVFoundation Tutorial: Adding Overlays and Animations to Videos