반응형 자막에서 포인트 텍스트에만 반응형 박스 적용하기
After Effect Blender expression Premiere 모션 그래픽 템플릿 애프터이펙트 익스프레션 프리미어
반응형 자막에서 포인트 텍스트에만 반응형으로 박스나 기타 효과를 적용하는 모션 그래픽 템플릿을 만들어 보겠습니다.
레이어 생성하기
이번 시간에 만들 템플릿은 포인트 텍스트가 1개일 때, 포인트 텍스트에만 밑줄 박스로 강조효과를 주는 템플릿입니다. 먼저 레이어를 다음과 같이 세팅하겠습니다.

BG는 적당한 이미지로 깔아주시고, 가이드 레이어 처리 해줍니다. 이번에는 Text 레이어에 기본 텍스트를 쓰고, PointText 레이어의 텍스트를 기준으로 RenderText를 Left, Center, Right로 나누었습니다.
그리고 Box는 Center 텍스트를 기준으로 작업할 것이고, Center 텍스트가 포인트 텍스트가 되도록 작업하겠습니다.
Box_Control과 RenderText_Control은 지난 시간과 같습니다.
기준이 되는 렌더 텍스트 익스프레션
먼저 RenderText_Left가 가장 기준이 됩니다. 여기는 전체 텍스트 중에서 포인트 텍스트를 기준으로 split한 배열의 첫 번째 원소를 텍스트로 출력합니다.
//RenderText_Left 레이어의 소스텍스트 익스프레션
totalText = thisComp.layer("Text").text.sourceText;
pointText = thisComp.layer("PointText").text.sourceText;
// '||' 기호는 '또는'을 뜻합니다. (엔터키 바로 위에 있습니다.)
// 정규표현식에서 /\s+$/는 텍스트 뒤에 들어간 공백을 가리킵니다. 따라서 여기서는 만약 출력하려는 텍스트 끝에 공백이 있으면 그 공백을 제거하고 출력합니다.
//renderText 는 포인트 텍스트가 입력되지 않았거나(pointText.length === 0) 포인트 텍스트가 토탈 텍스트에서 찾을 수 없는 경우(totalText.indexOf(pointText) === -1)에는 토탈텍스트를 출력하고, 그 외의 경우에는 토탈텍스트를 포인트 텍스트 기준으로 나눈 배열(totalText.split(pointText))의 첫 번째 원소 텍스트([0])를 출력합니다.
renderText = (pointText.length === 0 || totalText.indexOf(pointText) === -1) ? [totalText] : totalText.split(pointText)[0].replace(/\s+$/, "");
//여기에서 출력하는 텍스트의 기본 스타일은 토탈텍스트 레이어의 디자인을 그대로 가져오도록 합니다. : totalText.style
//이러면 작업을 할 때, Text레이어의 디자인을 바꿔보면서 실시간으로 디자인 작업을 할 수 있습니다
totalText.style.setText(renderText);
이 코드를 작성하면 렌더 텍스트 중에서 포인트 텍스트를 기준으로 배열을 만들어서 그 중에 첫 번째 원소 텍스트를 출력합니다.

위 이미지에서 렌더텍스트는 [“프리미어와 “,” 이펙트의 반응형 자막의 차이점”]입니다. 여기서 첫 번째 원소 텍스트인 “프리미어와 “에서 .replace(/\s+$/, “”)를 통해 마지막 공백을 제거하고 “프리미어와”로 출력해줍니다.
포인트 텍스트가 출력되는 렌더 텍스트 익스프레션
//RenderText_Center 레이어의 소스텍스트 익스프레션
totalText = thisComp.layer("Text").text.sourceText;
pointText = thisComp.layer("PointText").text.sourceText;
//여기에서 출력할 렌더 텍스트는 포인트 텍스트가 없거나 오타가 있을 경우에는 그냥 빈 텍트스 레이어([""])를 출력하고 그 외에는 포인트 텍스트를 뒷 공백을 없애고 출력합니다.
renderText = (pointText.length === 0 || totalText.indexOf(pointText) === -1) ? [""] : pointText.replace(/\s+$/, "");
//이 레이어의 스타일은 포인트 텍스트 레이어의 스타일을 가져옵니다. : pointText.style
pointText.style.setText(renderText);

나머지 텍스트가 출력되는 렌더 텍스트 익스프레션
//RenderText_Right 레이어의 소스텍스트 익스프레션
totalText = thisComp.layer("Text").text.sourceText;
pointText = thisComp.layer("PointText").text.sourceText;
renderText = (pointText.length === 0 || totalText.indexOf(pointText) === -1) ? [""] : totalText.split(pointText)[1];
//이 레이어의 스타일은 Text 레이어의 스타일을 가져옵니다. : totalText.style
//Left 텍스트와 Right 텍스트의 디자인은 똑같이 적용합니다.
totalText.style.setText(renderText);
텍스트를 바꿔가며 테스트 해보겠습니다.



지금 캡쳐는 자막의 위치도 맞춰 놓은 상태로 캡쳐한 것이기 때문에 작업하실 때 보여지는 것과는 다를 것입니다. 텍스트가 원하는 대로 제대로 불러오고 있는지만 체크해보세요.
렌더 텍스트들의 위치 조절하기
우선 기준이 되는 텍스트는 Left 텍스트가 될 것이고, 나머지 텍스트들은 Left 텍스트를 기준으로 위치 값을 결정할 것입니다. 따라서 Center와 Rigth 텍스트 먼저 위치 값을 계산합니다.
앵커 포인트 결정하기
먼저 앵커 포인트를 먼저 잡아줍니다. 이번에는 텍스트의 정 가운데가 아니라 하단의 가운데로 앵커 포인트를 맞추겠습니다. 그 이유는 포인트 텍스트가 나머지 텍스트보다 크고, 폰트를 바꿨을 때, 텍스트가 아래 라인에 맞춰서 써지도록 하기 위해 앵커 포인트를 하단으로 정렬합니다.
//렌더 텍스트 레이어의 앵커포인트를 모두 다음과 같이 세팅합니다.
a = thisLayer.sourceRectAtTime();
x = a.left + a.width/2;
//a.height/2가 아닙니다.
y = a.top + a.height;
[x, y]

포지션 결정하기
먼저 center 텍스트의 포지션을 맞추겠습니다.
//RenderText_Center 레이어의 포지션 익스프레션
totalText = thisComp.layer("Text").text.sourceText;
leftTextLayer = thisComp.layer("RenderText_Left");
leftText = leftTextLayer.text.sourceText;
//만약 포인트 텍스트를 기준으로 앞 뒤에 공백이 있을 경우 이 슬라이더 값으로 여백을 조절하기 위해 슬라이더 콘트롤을 추가했습니다.
text_blank = thisComp.layer("RenderText_Control").effect("Text_blank")("Slider");
//만약 포인트 텍스트를 기준으로 앞 뒤에 공백이 없을 경우 박스를 넣었을 때 앞 또는 뒷 텍스트와 박스가 겹쳐져서 디자인이 안좋아질 수 있습니다. 이런 경우 이 슬라이더 값으로 여백을 조절하기 위해 슬라이더 콘트롤을 추가했습니다.
default_Text_blank = thisComp.layer("RenderText_Control").effect("Default_Text_blank")("Slider");
default_X_Position = leftTextLayer.transform.position[0] + leftTextLayer.sourceRectAtTime().width/2 + thisLayer.sourceRectAtTime().width/2;
//.replace(/^\s+/, "")는 텍스트의 앞에 있는 공백을 제거합니다. 예를 들어 포인트 텍스트를 " 애프터"라고 적었다면 "애프터"로 바꿔주는 역할을 합니다.
x = leftText.length !== totalText.indexOf(thisLayer.text.sourceText) || thisLayer.text.sourceText.length !== thisLayer.text.sourceText.replace(/^\s+/, "").length ? default_X_Position + text_blank : default_X_Position + default_Text_blank;
//y값은 Left 렌더 텍스트의 y값을 그대로 가져옵니다.
y = leftTextLayer.transform.position[1];
[x, y]
x가 조금 복잡해 보이는데 끊어서 해석해보겠습니다.
default_X_Position은 leftTextLayer.transform.position[0] + leftTextLayer.sourceRectAtTime().width/2 + thisLayer.sourceRectAtTime().width/2입니다.
Left 텍스트 레이어의 x값에서 Left 텍스트 레이어의 가로 길이의 절반을 더하고 이 레이어의 가로 길이의 절반을 더한 값입니다. 즉, Left 텍스트 옆에 딱 붙어서 출력합니다.

leftText.length !== totalText.indexOf(thisLayer.text.sourceText) : Left 텍스트레이어의 글자수랑 토탈 텍스트에서 포인트 텍스트를 찾은 인덱스 가 같지 않은 경우
우리는 Left 렌더 텍스트 레이어에서 렌더 텍스트를 뒷 공백을 없애고 출력했습니다. 따라서 만약 포인트 텍스트 앞에 공백이 하나 있다면(띄어쓰기 되었다면) Left 텍스트 레이어의 글자수보다 포인트 텍스트의 indexOf 값이 더 크게 됩니다. 같은 값이 나올 수 없죠.
|| : 또는
thisLayer.text.sourceText.length !== thisLayer.text.sourceText.replace(/^\s+/, “”).length : 이 레이어의 텍스트의 개수와 이 레이어의 텍스트에서 앞 공백을 제거한 텍스트의 개수가 다른 경우인데 이 역시 우리가 입력한 포인트 텍스트에 앞 공백이 있느냐 없느냐를 체크하는 것입니다.
즉, Left 렌더 텍스트가 뒷 공백이 있거나 우리가 입력한 포인트 텍스트에 실수로 앞 공백이 들어간 경우를 체크합니다.
? : 이 두 가지 경우 중에 하나라도 참이면
x 값으로 default_X_Position + text_blank를 출력합니다.
여백을 text_blank로 조절합니다.
: 그 외의 경우
x 값으로 default_X_Position + default_Text_blank를 출력합니다. 이때는 여백을 default_Text_blank로 조절합니다.
즉, 포인트 텍스트와 Left 텍스트 사이에 여백이 띄어쓰기가 있다면 Text_blank 값으로 여백을 적당히 조절해주고, 띄어쓰기가 없다면 default_Text_blank로 여백을 적당히 조절해줍니다.
default_Text_blank가 필요한 이유는 포인트 텍스트에 박스로 디자인을 하거나 할 때 디자인 적인 여백을 조절하기 위함입니다.


디자인을 할 때 폰트나 자간 등의 특성에 따라 변하게 되는 값이기 때문에 별도로 정의해줬습니다.
그다음 Right 텍스트의 포지션을 맞추겠습니다.
//RenderText_Right 레이어의 포지션 익스프레션
totalText = thisComp.layer("Text").text.sourceText;
//현재 레이어의 왼쪽 텍스트가 RenderText_Center 레이어로 바뀝니다.
leftTextLayer = thisComp.layer("RenderText_Center");
leftText = leftTextLayer.text.sourceText;
text_blank = thisComp.layer("RenderText_Control").effect("Text_blank")("Slider");
default_Text_blank = thisComp.layer("RenderText_Control").effect("Default_Text_blank")("Slider");
default_X_Position = leftTextLayer.transform.position[0] + leftTextLayer.sourceRectAtTime().width/2 + thisLayer.sourceRectAtTime().width/2;
// totalText.indexOf(thisLayer.text.sourceText) - totalText.indexOf(leftText) : 현재 레이어의 인덱스 값에서 포인트 텍스트 레이어의 인덱스 값을 뺐습니다. 이러면 포인트 텍스트 레이어의 뒤에 여백이 있는지 체크할 수 있습니다.
x = leftText.length !== totalText.indexOf(thisLayer.text.sourceText) - totalText.indexOf(leftText) || thisLayer.text.sourceText.length !== thisLayer.text.sourceText.replace(/^\s+/, "").length ? default_X_Position + text_blank : default_X_Position + default_Text_blank;
y = thisComp.layer("RenderText_Left").transform.position[1];
[x, y]
쉽게 이해할 수 있도록 다음 표를 참고하세요.
포인트 텍스트 | 포인트 텍스트 문자 수 | 포인트 텍스트 indexOf값 | 이 레이어 indexOf값 | indexOf값의 차이 | 포인트 텍스트 뒤 여백 여부 |
” 애프터 이펙트” | 8 | 5 | 13 | 8 | X |
“애프터 이펙트” | 7 | 6 | 13 | 7 | X |
“애프터 이펙트 “ | 7 | 6 | 14 | 8 | O |
” 애프터 이펙트 “ | 8 | 5 | 14 | 9 | O |
Center 렌더텍스트 자체가 pointText.replace(/\s+$/, “”)로 인해 뒷 공백을 삭제하고 출력하기 때문에 포인트 텍스트의 문자 수에는 뒷 여백이 체크가 되지 않습니다.

RenderText_Control의 blank 슬라이더 값을 조절하고, 텍스트를 바꿔가며 테스트 해보세요.
Box 사이즈 맞추기
이제 포인트 텍스트에 맞도록 Box 사이즈를 맞추겠습니다.
const layerName = thisLayer.name;
const renderTextLayer = thisComp.layer("RenderText_Center");
const wb = thisComp.layer(layerName+"_Control").effect("Width_Blank")("Slider");
const hb = thisComp.layer(layerName+"_Control").effect("Heigth_Blank")("Slider");
const boxWidth = renderTextLayer.sourceRectAtTime().width + wb;
const boxHeight = renderTextLayer.sourceRectAtTime().height + hb;
[boxWidth, 20]
포인트 텍스트 아래 밑줄이 쳐지도록 박스 높이는 20으로 고정하였습니다. 물론 이것도 슬라이더 콘트롤로 별도로 빼서 관리할 수 있습니다.

박스의 라운드를 8로 세팅하고 스트로크는 사용하지 않을 것이기 때문에 눈을 꺼줫습니다.
박스 컬러는 포인트 텍스트와 같은 컬러로 지정하겠습니다.

포인트 텍스트 레이어의 스타일에서 컬러값을 불러와서 넣었더니 오류가 뜹니다. 여기 컬러값은 원소가 4개인 배열이 들어가야하는데 우리가 불러온 값은 원소가 3개인 배엻이라는 것입니다. 즉, RGBA값을 넣어야 하는데 RGB값을 넣었다는 오류입니다.

실제로 빈 텍스트 레이어에서 포인트 텍스트 레이어의 fillColor를 출력해보면 원소가 3개인 RGB 값을 0~1 사이의 값으로 출력해 주는 것을 확인할 수 있습니다.
//포인트 텍스트의 컬러값을 R, G, B로 채널을 분리해서 x, y, z에 넣고 A값을 1로 출력했습니다.
x = thisComp.layer("PointText").text.sourceText.style.fillColor[0];
y = thisComp.layer("PointText").text.sourceText.style.fillColor[1];
z = thisComp.layer("PointText").text.sourceText.style.fillColor[2];
[x, y, z, 1]
//다른 방법으로 다음과 같이 작성했습니다.
//thisComp.layer("PointText").text.sourceText.style.fillColor 값이 원소가 3개인 배열이므로 배열 뒤에 1을 원소로 추가한 배열을 만들었습니다.
//a=[1,2,3]일때 a.concat(4)는 [1,2,3,4]를 출력해줍니다.
thisComp.layer("PointText").text.sourceText.style.fillColor.concat(1);

사실 A값이 적용되지는 않습니다. 왜 RBGA로 값을 넣도록 하였는지는 잘 모르겠습니다. 애프터 이펙트에서 A 값은 컬러 아래 Opacity 값으로 조절하는데 컬러에서 RGBA로 값을 받는 이유는 잘 모르겠습니다. 오류가 사라졌습니다.
포인트 텍스트 레이어의 색을 바꿔보세요. 박스와 포인트 컬러의 색이 같이 잘 바뀝니다.
Box 위치 조절하기
박스의 앵커도 텍스트의 앵커와 똑같이 가운데 아래 정렬로 맞춰주겠습니다.
a = thisLayer.sourceRectAtTime();
x = a.left + a.width/2;
y = a.top + a.height;
[x, y]
그리고 RenderText_Center레이어의 포지션 값을 그대로 따라가도록 연결해보세요.

thisComp.layer("RenderText_Center").transform.position;
이러면 박스가 정확하게 포인트 텍스트와 함께 움직입니다. 포인트 텍스트와 박스의 앵커 포인트가 정확히 같은 위치에 있기 때문입니다.

혹시 박스가 아래로 절반 정도 내려오길 원하시나요? 앵커 포인트를 가운데 정렬로 바꿔 보세요.

지금은 포인트 텍스트의 스트로그 때문에 절반보다 더 내려온 것 처럼 보이지만 정확하게 박스 높이의 절반만큼 내려온 것입니다.
적당하게 원하는대로 디자인해보세요.

밝은 배경에서 가독성을 살리기 위해 drop Shadow 효과를 추가했습니다. 단, 그림자나 블랜드 모드의 경우 값을 프리미어에서 템플릿으로 얹어서 테스트 해야합니다. 애프터 이펙트에서는 효과가 은은하게 잘 먹었을 때 프리미어에서는 제대로 효과가 적용되지 않습니다. 따라서 정확한 수치는 프리미어에서 확인하면서 조절해야합니다.
마무리하기
이제 간단하게 모션을 넣고 마무리하겠습니다.

텍스트 레이어에는 1초씩 Slow Fade On 프리셋을 주었습니다.

Slow Fade On 프리셋은 텍스트가 부드럽게 두번에 걸쳐 페이드 온 되는 느낌을 줍니다.
그리고 박스는 현재 레이어의 앵커포인트를 고정해서 포지션을 맞췄으므로 레이어가 아닌 오브젝트의 앵커와 스케일 값을 조절했습니다.

//Rectangle 오브젝트의 앵커 포인트
//박스 사이즈의 절반 만큼 -x로 이동 : 앵커의 위치를 박스의 좌측으로 정
x = content("Rectangle 1").content("Rectangle Path 1").size[0]/2;
[-x, 0]
스케일은 1초 동안 [0,100]에서 시작해서 [100,100]으로 늘어나도록 세팅했습니다.
텍스트와 나오는 시간을 얼추 맞추기 위해 천천시 나타나다가 점점 빨라지도록 그래프를 만졌습니다.

박스가 빠르게 움직일 때 모션 블러도 주었습니다.
다음 시간에 프리미어 프로에서 이 모션 그래픽 템플릿을 테스트 해보고 문제점 및 보완점에 대해 말씀 드리겠습니다.
그럼 이만~
P.S 포스팅을 하면서 템플릿들을 하나씩 정리하며 업로드 하는 사이트를 만들고 있습니다. 다운 받으시는 분들이 늘어나고 반응이 괜찮으면 회원 가입을 받고 서로의 자료를 공유하는 식으로 발전시키면 어떨까 생각됩니다. 편하게 구경해보시고 템플릿 다운받아 가세요.
답글 남기기