HTML로 카드 만들기 - 1
구현하고자 하는 것
- 구현 기능
- 카드의 배경색 및 테두리색 지정
- 카드에 스티커 추가(드래그 앤 드롭 기능)
- 스티커 크기 선택 가능(S,M,L)
- Canvas API 사용
- 카드에 메세지 추가
- 컬러 및 폰트 크기 변경 가능
- 카드 세이브 및 읽어오기 기능
- 카드 지우기(백지화) 기능
- Further Issues
- UI :) ...
- 전체 지우기 기능 말고, 바로 직전의 행동 삭제하는 기능 추가
기능별 Code
- 전체 코드
- 카드의 배경색 및 테두리색 지정
// javascript var bgBtn = document.getElementById("bgBtn"); var brBtn = document.getElementById("brBtn"); var canvas = document.getElementById("canvas"); var ctx = canvas.getContext('2d'); // background color change function backgroundChange() { ctx.fillStyle = bgBtn.value; ctx.fillRect(1,1,canvas.width-2, canvas.height-2); } // border color change function borderChange() { canvas.style.border = "none"; ctx.strokeStyle = brBtn.value; ctx.strokeRect(0,0, canvas.width, canvas.height); }
- type이 color인 input태그를 생성하여 컬러를 선택할 수 있도록 한다. 선택된 값들은 나중에 Element.value로 가져올 수 있다.
- input 태그에 onclick 이벤트를 추가했고, script 영역에 해당하는 함수를 작성한다.
- script 영역에는 돔객체들을 가져다 쓸 수 있도록 변수 선언을 상단에 해준다.
- 함수 내부에는 canvas API의 문법이 사용되었는데, 테두리(stroke)를 그리거나 컬러를 채우는 등(fill) 간단한 기능을 사용하였다.
<!--body--> 배경 컬러 설정 : <input type="color" id="bgBtn" onchange="backgroundChange()"> 테두리 컬러 설정 : <input type="color" id="brBtn" onchange="borderChange()"> <!--canvas, stickerBox--> <section> <div id ="mycard"> <canvas id="canvas" width="600px" height="600px"></canvas> </div> <div id="sticker"> </div> </section>
- 이미지 드래그 앤 드롭
// javascript var mycard = document.getElementById("mycard"); var canvas = document.getElementById("canvas"); var ctx = canvas.getContext('2d'); //append imgs for(var i = 1 ; i <= 40 ; i++) { var img = document.createElement("img"); img.src = `../kakao/g${i}.png`; document.getElementById("sticker").appendChild(img); } // drag and drop function load() { var imgs = document.querySelectorAll('img'); for(var i = 0 ; i < imgs.length ; i++) { imgs[i].setAttribute('draggable',true); imgs[i].setAttribute('id', `img${i+1}`) imgs[i].addEventListener('dragstart', drag); } canvas.ondragover = function(e) { e.preventDefault(); } canvas.addEventListener('drop', drop); } function drag(e){ e.dataTransfer.setData('data', e.target.id); e.dataTransfer.setDragImage(e.target, 0, 0); } function drop(e) { e.preventDefault(); var id=e.dataTransfer.getData('data'); var el=document.getElementById(id); var posx=e.pageX-this.offsetLeft; // this === e.target var posy=e.pageY-this.offsetTop; // checked radio button for(var i = 0 ; i < radio.length ; i++) { if(radio[0].checked === true) { //small ctx.drawImage(el, posx-el.width/2+10 , posy-el.height/2+10, el.width * 3/4, el.height * 3/4); } else if(radio[1].checked === true) { //medium ctx.drawImage(el, posx-el.width/2 , posy-el.height/2, el.width *1.1, el.height*1.1); } else if(radio[2].checked === true) { //big ctx.drawImage(el, posx-el.width/2 , posy-el.height/2, el.width * 1.5, el.height *1.5); } } } window.addEventListener('load', load, false);
- 이미지 추가
- sticker 박스에다가 스티커를 렌더링하고, 이를 드래그해서 캔버스에 드롭할 수 있도록 해야한다. 그러기 위해선 먼저 sticker div영역에 스티커 이미지들을 append해서 보여줘야 한다.
createElement('img')
를 사용하여 이미지 돔 객체를 만든다. 그 다음 소스를 부여하고, 이를 보여주고자 하는 위치(sticker div)에 append한다.- 여러 개의 이미지를 추가하기 위해 for문을 사용하였다. 이를 위해서는 이미지 소스가 일관적인 숫자를 포함하여야 한다. 위 코드의 경우 이미지명을 g1, g2, g3... 이런 식으로 설정하여 for문으로 소스 부여를 처리하기 쉽게 하였다.
- 드래그 앤 드롭
- HTML의 drag and drop API를 사용하였다. 참고: MDN 문서, html5rocks.com
dragstart
,dragover
,drop
순으로 이어진다. 이 세가지 중 하나가 제대로 되지 않으면 다음 함수가 제대로 작동하지 않는다.- 우선 drag될 대상(이미지들)에
dragstart
이벤트를 추가한다. 이미지가 여러 개이기 때문에 역시 for문을 사용했다. querySelectorAll로 HTMLCollection을 불러온 뒤, for문으로 돌면서 속성을 부여했다. - 각 이미지 객체에
draggable
속성을 추가하고, id 지정,dragstart
이벤트 추가를 하였다. 여기서 id는 반드시 지정해야 하는데, 그 이유는 뒤에dataTransfer
을 사용하기 때문이다. 드래그 앤 드롭 시 데이터를 전달할 수 있는 이 API를 사용하기 위해서는 객체의 고유한 id가 있어야 한다.dataTransfer
을 사용하는데 객체에 id 속성이 없으면 데이터 전달이 이루어지지 않아 null을 리턴한다. (처음에 지정해주지 않아 뒤에서 cannot read property width of null 에러가 났었다) - dragstart 이벤트를 추가한 뒤 이번엔 드롭할 영역에
dragover
,drop
이벤트를 설정한다. 이 예제에서는 canvas 영역에 드롭할 것이므로 canvas에 이벤트를 추가한다. addEventListener로 추가할 때랑 돔객체에.
찍고 추가할 때에 효과는 같은데 이벤트명에 차이가 있다. 흔하게 on의 유무가 다르다. - 여기서 내가 이유도 모르고 계속 에러가 났던 부분은
e.preventDefault();
이다.e.preventDefault
는 해당 이벤트(여기서는 dragover, drop) 외에 별도의 브라우저 행동을 막기 위해 사용된다. 이 함수는, dragstart에는 사용하면 안 되고 dragover과 drop에는 사용해야만 한다.- 함수명에서 알 수 있듯이 이 함수는 default 행동을 막기 위한 것이다. div내에서는 default 행동이 drop하지 않는 것이기 때문에, dragover이벤트 함수에서 preventDefault 함수를 호출하여 디폴트 행동을 취소하지 않으면 drop이벤트가 실행되지 않는다. (In order to allow a drag-and-drop action on a div, must cancel the default action)
- 또한 drop이벤트 함수 내에서 이를 사용하지 않으면 drop하려는 파일을 브라우저가 실행해버리기 때문에 사용해야 한다.
- 다만 처음에 실수로 dragstart이벤트 함수 내에도 preventDefault함수를 호출하는 바람에, dragover 이벤트가 실행되지 않았다. dragstart에서 이를 호출하면 이벤트가 취소돼 버린다.
- drag이벤트 함수 내에는
dataTransfer
함수를 사용해서 데이터를 전달할 수 있도록 했다. 이를 사용해서 문자열, 파일 등 다양한 객체를 전달할 수 있지만 여기서는 간단하게 돔 객체만 grab하면 되므로 id를 통해 전달했다. (dataTransfer의 첫번째 param은 아무 문자열을 넣어도 상관없는 듯하다) - 이렇게 전달된 객체를 선택자로 선택하여 drop이벤트함수에서 전달받은 다음 canvas API를 사용해서 drop된 위치에다가 객체를 그리면 된다. (drawImage 함수 사용) 나는 라디오버튼을 통해 이미지 사이즈를 선택할 수 있게 만들었고, 체크된 항목을 grab하여 조건문을 써서 그려지는 이미지 크기를 다르게 설정했다.
- 페이지가 모두 로드된 다음에 이 함수들을 실행해야 에러가 안 나기 때문에(예를 들어 이미지도 로드가 안됐는데 드래그앤드롭을 실행하려고 하면 당연히 에러가 난다) window onload 함수를 사용했다.
- 이미지 추가
<!--canvas, stickerBox--> <section> <div id ="mycard"> <canvas id="canvas" width="600px" height="600px"></canvas> </div> <div id="sticker"> </div> </section>