IOS在视频上添加字幕效果的基本思路是:
-
使用自定义的
CATextLayer文字图层或者CAShapeLayer文字图层,添加到视频的Layer上创建用户自定义的字幕效果。这两者的区别是:CATextLayer支持设置简单的文字效果,包括文字的内容、字体、字号大小、对其方式、文字颜色、背景颜色等基本的属性;CAShapeLayer功能更强大,提供了CATextLayer没有的边框大小、边框颜色等设置,如果需要更高级的文字内容展示,需要使用CATextLayer配合UIBezierPath来定制自定义的文字内容。 -
通过设置Layer图层的动画来控制字幕的时间点和时间长度,这里有一个坑如果单独设置
CATextLayer或者CAShapeLayer的动画不能控制开始的时间,需要额外添加一个CALayer图层,把文字图层CATextLayer或者CAShapeLayer添加到父CALayer图层中,在文字图层CATextLayer或者CAShapeLayer上设置开始的动画1NSTimeInterval animatedInStartTime = startTime + initAnimationDuration; 2CABasicAnimation *fadeInAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 3fadeInAnimation.fromValue = @0.0f; 4fadeInAnimation.toValue = @1.0f; 5fadeInAnimation.additive = NO; 6fadeInAnimation.removedOnCompletion = NO; 7fadeInAnimation.beginTime = animatedInStartTime; 8fadeInAnimation.duration = animationDuration; 9fadeInAnimation.autoreverses = NO; 10fadeInAnimation.fillMode = kCAFillModeBoth; 11[textLayer addAnimation:fadeInAnimation forKey:@"opacity"];
在父CALayer图层上设置结束的动画,这样设置才能实现用户自定义的时间点和时间长度
1NSTimeInterval animatedOutStartTime = startTime + duration - animationDuration; 2 CABasicAnimation *fadeOutAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 3 fadeOutAnimation.fromValue = @1.0f; 4 fadeOutAnimation.toValue = @0.0f; 5 fadeOutAnimation.additive = NO; 6 fadeOutAnimation.removedOnCompletion = NO; 7 fadeOutAnimation.beginTime = animatedOutStartTime; 8 fadeOutAnimation.duration = animationDuration; 9 fadeOutAnimation.autoreverses = NO; 10 fadeOutAnimation.fillMode = kCAFillModeBoth; 11 [animatedTitleLayer addAnimation:fadeOutAnimation forKey:@"opacity"];
完整的代码
1- (CALayer *)buildLayerbuildTxt:(NSString*)text 2 textSize:(CGFloat)textSize 3 textColor:(UIColor*)textColor 4 strokeColor:(UIColor*)strokeColor 5 opacity:(CGFloat)opacity 6 textRect:(CGRect)textRect 7 fontPath:(NSString*)fontPath 8 viewBounds:(CGSize)viewBounds 9 startTime:(NSTimeInterval)startTime 10 duration:(NSTimeInterval)duration 11{ 12 if (!text || [text isEqualToString:@""]) 13 { 14 return nil; 15 } 16 17 // Create a layer for the overall title animation. 18 CALayer *animatedTitleLayer = [CALayer layer]; 19 20 // 1. Create a layer for the text of the title. 21 CATextLayer *titleLayer = [CATextLayer layer]; 22 titleLayer.string = text; 23 titleLayer.font = (__bridge CFTypeRef)(@"Helvetica"); 24 titleLayer.fontSize = textSize; 25 titleLayer.alignmentMode = kCAAlignmentCenter; 26 titleLayer.bounds = CGRectMake(0, 0, textRect.size.width, textRect.size.height); 27 titleLayer.foregroundColor = textColor.CGColor; 28 titleLayer.backgroundColor = [UIColor clearColor].CGColor; 29 // [animatedTitleLayer addSublayer:titleLayer]; 30 31 // 添加文字以及边框效果 32 UIFont *font = nil; 33 if ((fontPath != nil) && (fontPath.length > 0)) { 34 font = [[FLVideoEditFontManager sharedFLVideoEditFontManager] fontWithPath:fontPath size:textSize]; 35 titleLayer.font = CGFontCreateWithFontName((__bridge CFStringRef)font.fontName); 36 } 37 if (font == nil) { 38 titleLayer.font = (__bridge CFTypeRef)(@"Helvetica"); 39 } 40 41 UIBezierPath *path = nil; 42 if (font) { 43 path = [FLLayerBuilderTool createPathForText:text fontHeight:textSize fontName:(__bridge CFStringRef)(font.fontName)]; 44 } 45 else 46 { 47 path = [FLLayerBuilderTool createPathForText:text fontHeight:textSize fontName:CFSTR("Helvetica")]; 48 } 49 CGRect rectPath = CGPathGetBoundingBox(path.CGPath); 50 CAShapeLayer *textLayer = [CAShapeLayer layer]; 51 textLayer.path = path.CGPath; 52 textLayer.lineWidth = 1; 53 if (strokeColor != nil) { 54 textLayer.strokeColor = strokeColor.CGColor; 55 } 56 if (textColor != nil) { 57 textLayer.fillColor = textColor.CGColor; 58 } 59 textLayer.lineJoin = kCALineJoinRound; 60 textLayer.lineCap = kCALineCapRound; 61 textLayer.geometryFlipped = NO; 62 textLayer.opacity = opacity; 63 textLayer.bounds = CGRectMake(0, 0, rectPath.size.width, textSize+10); 64 [animatedTitleLayer addSublayer:textLayer]; 65 66 // 动画图层位置 67 animatedTitleLayer.position = CGPointMake(textRect.origin.x+textRect.size.width/2, viewBounds.height - textRect.size.height/2 - textRect.origin.y); 68 69 NSTimeInterval initAnimationDuration = 0.1f; 70 NSTimeInterval animationDuration = 0.1f; 71 72 // 3.显示动画 73 NSTimeInterval animatedInStartTime = startTime + initAnimationDuration; 74 CABasicAnimation *fadeInAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 75 fadeInAnimation.fromValue = @0.0f; 76 fadeInAnimation.toValue = @1.0f; 77 fadeInAnimation.additive = NO; 78 fadeInAnimation.removedOnCompletion = NO; 79 fadeInAnimation.beginTime = animatedInStartTime; 80 fadeInAnimation.duration = animationDuration; 81 fadeInAnimation.autoreverses = NO; 82 fadeInAnimation.fillMode = kCAFillModeBoth; 83 [textLayer addAnimation:fadeInAnimation forKey:@"opacity"]; 84 85 NSTimeInterval animatedOutStartTime = startTime + duration - animationDuration; 86 CABasicAnimation *fadeOutAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 87 fadeOutAnimation.fromValue = @1.0f; 88 fadeOutAnimation.toValue = @0.0f; 89 fadeOutAnimation.additive = NO; 90 fadeOutAnimation.removedOnCompletion = NO; 91 fadeOutAnimation.beginTime = animatedOutStartTime; 92 fadeOutAnimation.duration = animationDuration; 93 fadeOutAnimation.autoreverses = NO; 94 fadeOutAnimation.fillMode = kCAFillModeBoth; 95 96 [animatedTitleLayer addAnimation:fadeOutAnimation forKey:@"opacity"]; 97 98 return animatedTitleLayer; 99}
依赖的工具类FLLayerBuilderTool.m文件:
1#import "FLLayerBuilderTool.h" 2#import <CoreText/CoreText.h> 3 4@implementation FLLayerBuilderTool 5 6 7+ (UIBezierPath*) createPathForText:(NSString*)string fontHeight:(CGFloat)height fontName:(CFStringRef)fontName 8{ 9 if ([string length] < 1) 10 return nil; 11 12 UIBezierPath *combinedGlyphsPath = nil; 13 CGMutablePathRef letters = CGPathCreateMutable(); 14 15 CTFontRef font = CTFontCreateWithName(fontName, height, NULL); 16 if (font == nil) { 17 font = (__bridge CFTypeRef)(@"Helvetica"); 18 } 19 NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys: 20 (__bridge id)font, kCTFontAttributeName, 21 nil]; 22 NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:string 23 attributes:attrs]; 24 CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attrString); 25 CFArrayRef runArray = CTLineGetGlyphRuns(line); 26 27 // for each RUN 28 for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runArray); runIndex++) 29 { 30 // Get FONT for this run 31 CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex); 32 CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName); 33 34 // for each GLYPH in run 35 for (CFIndex runGlyphIndex = 0; runGlyphIndex < CTRunGetGlyphCount(run); runGlyphIndex++) 36 { 37 // get Glyph & Glyph-data 38 CFRange thisGlyphRange = CFRangeMake(runGlyphIndex, 1); 39 CGGlyph glyph; 40 CGPoint position; 41 CTRunGetGlyphs(run, thisGlyphRange, &glyph); 42 CTRunGetPositions(run, thisGlyphRange, &position); 43 44 // Get PATH of outline 45 { 46 CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL); 47 CGAffineTransform t = CGAffineTransformMakeTranslation(position.x, position.y); 48 CGPathAddPath(letters, &t, letter); 49 CGPathRelease(letter); 50 } 51 } 52 } 53 CFRelease(line); 54 55 combinedGlyphsPath = [UIBezierPath bezierPath]; 56 [combinedGlyphsPath moveToPoint:CGPointZero]; 57 [combinedGlyphsPath appendPath:[UIBezierPath bezierPathWithCGPath:letters]]; 58 59 CGPathRelease(letters); 60 CFRelease(font); 61 62 if (attrString) 63 { 64 attrString = nil; 65 } 66 67 return combinedGlyphsPath; 68} 69 70@end
参考资料:
视频特效制作:如何给视频添加边框、水印、动画以及3D效果
视频特效制作2
AVFoundation Tutorial: Adding Overlays and Animations to Videos