[HTML5 실습] Canvas를 이용한 텍스트-> 이미지 변환기
몇해전 MKEXDev.NET 의 로고가 필요하게 된 적이 있었다
대학생에게 무료로 개발도구를 배포하는 마이크로소프트의 DreamSpark 이벤트에 후원 커뮤니티로 선정되어 사이트 로고를 보내줘야 했다. 당시 내 사이트에는 특별한 로고가 없었으나 MS 프로모션 담당자는 이미지로 된 로고를 원했다. 그래서 급조하기로 했는데 개발을 주제로 한 사이트라 뭐.. 특별한 디자인을 하고 싶지는 않았다. 그냥 텍스트에 간단한 효과를 줘서 깔끔하게만 보이면 되겠다 싶었다.
그래서 아는 디자이너에게 매우 심플하게 만들어 달라고 부탁해서 나온 로고가 바로 아래와 같은 모습니다.
심플하면서도 깔끔한 텍스트 기반의 로고로 개인적으로 흡족하며 지금까지 사이트 로고로 사용하고 있다
이를 계기로 문득, 텍스트를 입력하면 간단한 효과만 처리해서 이미지로 변환해 주는 툴이 있으면 좋겠다는 생각이 들었는데 마침 HTML5를 접하면서 간단하게라도 데모격으로 한번 만들어 보자고 맘을 먹었다
목표는 완성도가 아니라 기능을 구현하면서 HTML5를 익히자는 것이므로 툴의 완성도는 기대하지 말기 바란다
CSS3 ? Canvas ?
텍스트에 효과를 주는 방법은 많다. HTML5 관련 스펙만 봐도 CSS3 혹은 Canvas 를 이용할 수 있다
CSS의 Transform 이나 text-stroke, opacity, Gradients 등과 같은 기능만으로도 화려한 텍스트 효과처리가 가능하다. 그러나 앞서도 밝혔지만 CSS 보다는 HTML5 주요 기능을 익히는 것이 목적이므로 Canvas를 이용하기로 결정했다. Canvas를 이용해도 텍스트 크기, 그라디에이션, 색상, 폰트, 그림자, 투명도 등 처리가 가능하다
게다가 Canvas의 내용을 이미지로 변환할 수 있으니 더욱 적합하다는 생각을 하게 되었다
데모 실행
코드를 보기 전에 먼저 데모 실행화면을 살펴 보자
하단 텍스트상자에 글자를 입력하면 상단의 캔버스에 표시되며 몇 가지 텍스트 효과를 줄 수 있다
이 모든 텍스트 효과처리는 HTML5 Canvas 관련 기능을 이용한 것이며 캔버스의 최종 결과를 이미지로 변환시켜 주는 기능이 지원된다. 텍스트상자에 글이 입력되거나 삭제될 때 바로바로 그 내용이 캔버스로 반영되며 텍스트 효과도 즉시 적용되어 캔버스에 나타난다.
참고로 텍스트 효과는 데모에 적용된 기능 이외에도 그림자 효과, 그라디에이션 효과 등 더 다양한 처리가 가능하지만 데모에서는 기본적인 효과만 집중했음을 밝힌다
IE8 이전 버전을 제외한 크롬, 사파리, 오페라에서 실행 가능하며, 다음의 주소에서 확인할 수 있다
http://mkexdev.net/m/textToImageByCanvas.html
구현 하기
전체 코드를 보기 전에 몇 가지 핵심적인 내용을 살펴 보자
자바스크립트 키 관련 이벤트
HTML5 Canvas API 설명전에 먼저 자바스크립트의 키 관련 이벤트를 짚고 넘어가자
자바스크립트에는 키보드 입력과 관련된 총 3개의 이벤트가 제공된다.
keydown, keyup, keypress 가 그것인데 이들 이벤트는 키 입력에 반응하는 것이 조금씩 다르다
keydown 과 keyup 은 유사하지만하지만 keypress 는 이 둘과 약간 차이가 있는데 데모 제작과 관련해 주목해야 할 것은 keypress 는 백스페이스키(<-) 에 반응하지 않는다는 것이다.
그리고 keydown 과 keyup 의 경우 이벤트로 넘겨진 keyCode 에는 영문 대/소문자를 구분하지 않고 모두 대문자로 취급한다는 것이다. 제작하고자 하는 데모는 대/소문자를 구분해야 하며 백스페이스키로 글자를 삭제해야 하기 때문에 하나의 이벤트로 만족할 수 없었다. 즉 keypress 이벤트로 문자 입력을 처리하고 keyup으로 백스페이스와 Delete키에 반응하도록 처리했다는 것을 먼저 알린다
아래 코드와 같이 두 이벤트를 동시에 정의해서 문자 키 입력에 두 이벤트가 반응하지만, 백스페이스와 Delete 키 등의 특수키에는 keyup 이벤트만 반응한다. keyup 이벤트는 일반 문자 입력은 무시하도록 처리한다.
<input type="text" id="myText" size="60" onkeypress="inputText(event.keyCode);" onkeyup="inputBackSpace(event.keyCode);">
캔버스 지우기
캔버스의 내용을 모두 지우기 위해서는 몇 가지 방법이 있지만,
기본적으로 캔버스의 너비 혹은 높이 정보를 다시 설정하면 캔버스내용이 모두 지워진다고 알려져 있다
canvas.width = canvas.width;
그러나 이 방법은 현재 사파리와 파이어폭스에서만 정상 동작한다
브라우저마다 HTML5 지원 현황이 달라서 발생하는 문제로 보인다.따라서 모든 브라우저에서 동작하도록 하려면 clearRect 로 캔버스 내용을 명시적으로 지워줘야 한다
아래 코드와 같이 (0,0) 부터 캔버스의 너비,높이만큼이 사각형 영역을 지움으로써 캔버스를 초기화 할 수 있다
context.clearRect(0,0,canvas.width, canvas.height);
캔버스에 텍스트 그리는 방법
Canvas의 2D Context 를 통해 각종 그리기 작업을 할 수 있는데 텍스트의 경우 fillText 와 strokeText 메서드를 통해 그릴 수 있다. fillText는 속이 찬 텍스트, strokeText는 테두리만 있는 텍스트를 그릴 수 있다
데모에서는 텍스트상자에 글자가 입력될 때 마다 바로바로 캔버스에 그리기를 수행하는데,
이때 입력된 글자 하나씩 캔버스에 그리는 방법과 캔버스를 지우고 텍스트상자에 지금까지 입력된 전체 내용을 다시 그리는 방법 중 하나를 택해야 한다
전자의 경우 텍스트가 입력되는 위치를 직접 지정해 줘야 하기 때문에 조금 복잡하며 후자의 경우 매번 캔버스를 지우고 다시 그리는 작업을 해 줘야 하기 때문에 조금 비효율적이다
따라서 데모에서는 전자의 경우처럼 텍스트 입력 시 마다 글자 하나씩 캔버스에 입력하기로 했으며 이때 알아야 하는 입력 위치는 다음과 같이 measureText 메서드를 통해 전체 문자열의 길이를 구함으로써 해결하였다
(텍스트 상자가 멀티라인을 지원하지 않기 때문에 Y 좌표는 필요치 않다)
var textWidth = context.measureText(text).width;
context.fillText(text, textWidth , 0); //글자가 위치할 캔버스 X 좌표를 설정한다
다만 백스페이스키(<-)와 Delete 키를 눌렀을 때 글자가 지워지는 것은 심플한 처리를 위해,캔버스 내용을 지우고 다시 입력하는 후자의 방식을 따르기로 했다.
(글자 지우기 역시 clearRect 를 통해 정해진 글자 영역만 지울 수 있지만 이 경우 x 좌표의 변화와 폰트 크기에 따른 clearRect 크기 변경 등 신경 쓸 부분이 많아 오히려 버그 유발 요인이 될 수 있어 사용을 피하기로 했다)
context.clearRect(0,0,canvas.width, canvas.height); //캔버스를 지우고
context.fillText(text, 0, 0); //다시 전체 글자를 그린다
그리고 텍스트에 폰트, 색상과 같은 효과 처리는 2D Context의 속성들을 통해 이뤄지는데 이미 입력된 내용은 반영되지 않기 때문에 이때도 역시 캔버스를 지우고 다시 그리는 방식을 취한다
기타 살펴볼 내용
캔버스를 통한 텍스트 효과 처리시 다음의 주요 코드가 이용 되었다
: context.textBaseline = "top"
캔버스에 텍스트가 위치하는 수평 기준선을 top 으로 해 줌으로써 글자가 기준선 아래로 표시되도록 한다
: context.font = "20px 'Tahoma'"
font 속성을 통해 캔버스에 표시될 텍스트 폰트를 처리하는데 css 의 폰트 속성과 같이 정의할 수 있다
: context.fillStyle = "red" , context.strokeStyle
fillStyle 속성을 통해 채우기 스타일을 지정할 수 있다. 기본은 검정이다. strokeStyle 속성은 선의 색상을 지정하는데 이용한다
: String.fromCharCode(keyCode)
HTML5 , Canvas와는 무관하지만 데모에서는 유용하다. 키 이벤트로 넘여온 keyCode 를 문자로 변환해 주는 자바스크립트 내장 함수이다. 한 글자씩 입력 할 경우 필요하다
: 캔버스 내용을 이미지로 변환하기
이미 살펴본 내용이다. 캔버스의 toDataURL 메서드를 통해 이미지데이터문자열을 기반으로 이미지객체를 생성한다. [HTML5 실습] Canvas에 그린 그림을 이미지로 만들기
이것으로 구현을 위한 핵심적인 내용을 대략 살표 보았다. 이제 전체 코드를 제시한다
전체 코드는 http://mkexdev.net/m/textToImageByCanvas.html 의 소스보기를 통해서도 볼 수 있다
<html>
<head></head>
<body>
<form name="myForm">
<canvas id="cv" width="400" height="70" style="position: relative; border: 1px solid #000;"></canvas>
<button onclick="clearCanvas()">Clear</button>
<input type="button" onclick="convertImage()" value="이미지로변환">
<img id="myImage">
<br>
<input type="radio" name="isFill" value="Fill" onchange="chkFill();" checked>Fill
<input type="radio" name="isFill" value="Stroke" onchange="chkFill();">Stroke
<br>
Font: <input id="fontSize" type="range" min="10" max="60" step="5" value="50" onchange="changeFont();" />
<select id="fontFace" onchange="changeFont();">
<option value="Tahoma" selected>Tahoma</option>
<option value="Verdana">Verdana</option>
<option value="Gulim">Gulim</option>
<option value="Georgia">Georgia</option>
<option value="Symbol">Symbol</option>
<option value="Terminal">Terminal</option>
</select>
<br>
Fill Color: <select id="fontColor" onchange="changeColor(1);">
<option value="Black" selected>Black</option>
<option value="Red">Red</option>
<option value="Blue">Blue</option>
<option value="Green">Green</option>
<option value="Yellow">Yellow</option>
</select>
Stroke Color: <select id="strokeColor" onchange="changeColor(2);">
<option value="Black" selected>Black</option>
<option value="Red">Red</option>
<option value="Blue">Blue</option>
<option value="Green">Green</option>
<option value="Yellow">Yellow</option>
</select>
<p>
<input type="text" id="myText" size="60" onkeypress="inputText(event.keyCode);" onkeyup="inputBackSpace(event.keyCode);">
<div id="msgDiv"></div>
</form>
</body>
</html>
<script type="text/javascript">
if(window.addEventListener){
window.addEventListener('load', Init, false);
}
var canvas, context, myText,msgDiv
var isFill = true;
function Init() {
canvas = document.getElementById('cv');
context = canvas.getContext('2d');
context.font = eval("'"+ document.getElementById('fontSize').value +'px '+ document.getElementById('fontFace').value+"'");
context.textBaseline = "top";
myText = document.getElementById('myText');
myText.focus();
msgDiv = document.getElementById("msgDiv");
}
function inputText(keyCode){
msgDiv.innerText = keyCode;
//글자의 가로위치를 구하기 위해 현재 입력된 문자열의 너비를 구한다
var textWidth = context.measureText(myText.value).width;
drawText(String.fromCharCode(keyCode), textWidth);
}
function inputBackSpace(keyCode){
if(keyCode == 8 || keyCode == 46){ //백스페이스 키와 Delete키를 받기 위한 함수
clearCanvas();
drawText(myText.value,0);
}
}
function drawText(text, posX){
if(!isFill){ context.strokeText(text, posX , 0); }
else{ context.fillText(text, posX , 0); }
}
function chkFill(){
isFill = myForm.isFill[0].checked;
clearCanvas();
drawText(myText.value,0);
}
function changeFont(){
clearCanvas();
context.font = eval("'"+ document.getElementById('fontSize').value +'px '+ document.getElementById('fontFace').value+"'");
drawText(myText.value,0);
}
function changeColor(flag){
clearCanvas();
if(flag == 1){
context.fillStyle = document.getElementById('fontColor').value;
}
else{
context.strokeStyle = document.getElementById('strokeColor').value;
}
drawText(myText.value,0);
}
function clearCanvas(){
//canvas.width = canvas.width; //사파리, 파폭에서만 동작(크롬, 오페라 X)
context.clearRect(0,0,canvas.width, canvas.height);
}
function convertImage(){
var image = new Image();
var myImage = document.getElementById('myImage');
myImage.src = canvas.toDataURL();
}
</script>
HTML5 Canvas+Text Example
한참 삘(feel) 받아서 데모를 제작하다가 이 사이트를 알게 되었다
=> http://whatdo.net/html5/example/#2
내가 구현하고자 했던 대부분의 기능이 제공되고 있었다.
사실 데모의 완성도를 꽤 올리고자 맘 먹고 시도하던 도중에 이 사이트를 보게 되었고 동기가 한풀 꺽였다
이미 제공되고 있는 사이트가 있으니 내가 만들고자 하는데모에 새로움은 별로 없기에 받았던 삘(feel)이 사그러져 버렸다 ㅎㅎ. 그래서 적당한 선에서 데모 제작을 완료하고 포스팅 하게 된 것이다
이 사이트를 보면 조금은 다른 방식으로 구현하고 있지만 더 많은 canvas 기능이 활용되었고 구현 방식 역시 배울점이 많아 보인다. 소스보기를 통해 분석해 보면 많은 도움을 얻게 될 것이니 반드시 참고 바란다