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