캔버스 ( html5 ) 그림 그리기3 정보
JavaScript 캔버스 ( html5 ) 그림 그리기3
첨부파일
본문
이전 소스에서는 그리기를 클릭했을 경우 캔버스에 마우스가 움직이는대로 라인이 그려지는걸 볼수 있었는데
이를 마우스가 클릭한 상태로 드래그가 이뤄졌을 경우에만 라인이 그려지며 마우스를 놓았을 때는 해당 기능이 정지되도록
하겠습니다
이를 위해서는 변수 하나가 추가되어져야 하며 마우스 클릭 이벤트와 마우스업 이벤트를 이용합니다
이하 소스 빨간색으로 입혀진 부분이 추가된 소스입니다
---------------------------- 이하 소스 -----------------------------------
function drowing(event,objType,objID){
var jobCanvas = document.getElementById("canvas1");
var thisCanvas = document.getElementById("canvas1");
var ctx = jobCanvas.getContext('2d');
var dot = 10;
var color = "red";
var x2="";
var y2="";
var jobCanvas = document.getElementById("canvas1");
var thisCanvas = document.getElementById("canvas1");
var ctx = jobCanvas.getContext('2d');
var dot = 10;
var color = "red";
var x2="";
var y2="";
var isDraw = false; //추가 마우스 움직임 여부
thisCanvas.onmousedown = function(event){
isDraw = true;
ctx.beginPath();
var x0=(parseInt(event.clientX))-canvasLeft;
var y0=(parseInt(event.clientY))-canvasTop;
ctx.arc(x0, y0, dot/2, 0, Math.PI*2, true);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
}
document.onmouseup = function(event){
isDraw = false;
}
}
thisCanvas.onmousemove = function(event){
//캔버스 정점에서부터의 위치
var x=(parseInt(event.clientX) - canvasLeft);
var y=(parseInt(event.clientY) - canvasTop);
var x=(parseInt(event.clientX) - canvasLeft);
var y=(parseInt(event.clientY) - canvasTop);
ctx.beginPath();
ctx.moveTo( x, y );
draw(event);
x2=x; //위치 정보를 기록한다
y2=y;
}
// 그리기
this.draw=function(event){
if(isDraw ==true ){
ctx.lineTo(x2,y2);
ctx.lineWidth = dot;
ctx.strokeStyle =color;
ctx.lineCap = 'round';
ctx.stroke();
}
}
}
---------------------------- 이상 소스 -----------------------------------
var isDraw = false; //추가 마우스 움직임 여부
위의 변수는 마우스가 클릭되어 있는지 ( 왼쪽 버튼이 눌려져 있는지 )를 변수에 담아두게 됩니다
처음에 아무일도 일어나지 않은 상태이기에 false 로 담아둡니다
thisCanvas.onmousedown = function(event){
isDraw = true;
ctx.beginPath();
var x0=(parseInt(event.clientX))-canvasLeft;
var y0=(parseInt(event.clientY))-canvasTop;
ctx.arc(x0, y0, dot/2, 0, Math.PI*2, true);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
}
현재의 캔버스 ( thisCanvas를 쓴 이유는 작업에 따로 캔버스 위에 레이어 마냥 추가적인 레이어를 올려 놓고 쓸수
있기에 그려지는 캔버스가 아닌[ 작업중인 캔버스 ] 최상위 캔버스인 thisCanvas를 가지고 마우스 이벤트를 캡처합니다 )
마우스 클릭( 왼쪽 버튼 누름 )이 일어나면
isDraw를 true로 바꿔 마우스가 눌려졌음을 변수에 담아둡니다
그 이후
ctx.beginPath();
var x0=(parseInt(event.clientX))-canvasLeft;
var y0=(parseInt(event.clientY))-canvasTop;
ctx.arc(x0, y0, dot/2, 0, Math.PI*2, true);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
위에 소스를 적어 놓은 이유는 라인만 그려쓰는게 아닌 상황에 따라 점을 찍어야 할일이 있을수 있습니다
즉 점을 찍는다는 것은 클릭이 한번만 이뤄진다는 것이기에 클릭이 일어나는 시점에
그려주기를 한번 해주는 겁니다
x0, y0 는 마우스 좌표이며
ctx.arc(x0, y0, dot/2, 0, Math.PI*2, true);
ctx.fillStyle = color;
ctx.fill();
ctx.fillStyle = color;
ctx.fill();
지정된 좌표에 arc 모양 즉 원형으로 color에 담긴 색으로 fill 칠하게 됩니다
arc 맨 마지막에 true로 표기되어 있는데 true와 false에 의해서 원이 마우스를 찍은 점을 중심으로 (중심점 ) 원을 그릴 것인지 아니면 좌표를 시작점으로 해서 그려질지를 정하게 됩니다
api옵션에 관한 사항은 따로 정리하지 않고 후에 이를 좀더 자세히 기재한 사이트 링크를 올리겠습니다
위에 기재한 상황만으로 그리기 버튼을 누르고 캔버스 위에서 마우스를 클릭해서 움직였을 때 그려지는 상황까지 연출이
되었습니다
document.onmouseup = function(event){ // 마우스 클릭을 떼었을 때
isDraw = false; // 마우스를 떼었을 때
}
}
마우스를 떼었을 경우 ( 보통 마우스는 캔버스위에서도 뗄수 있지만 문서의 아무 곳에서나 뗄수 있기에
좀 더 넓은 의미의 document로 잡아서 합니다 )
이제는 기본적인 지우기 입니다
이전에 잠깐 쓴 적이 있는데 지우기에는 세가지 정도가 있습니다
하나는 캔버스 크기를 임의로 바꾸어 초기화 시키는 방법
둘째는 지우기 api에 의한 방법
셋째는 지우기라기 보다 일종의 그리기에 혹은 합치기라는 것에 더 가까운 모습인 globalCompositeOperation 방법이 있습니다
첫번째 사항인 크기를 이용한 지우기를 해보기 위해서 지우기 함수를 하나 만들어보겠습니다
/*캔버스 지우기1 */
function clearAllCanvas(){
var jobCanvas = document.getElementById("canvas1");
var thisCanvas = document.getElementById("canvas1");
jobCanvas.setAttribute("width", "150px");
jobCanvas.setAttribute("height","150px");
var jobCanvas = document.getElementById("canvas1");
var thisCanvas = document.getElementById("canvas1");
jobCanvas.setAttribute("width", "150px");
jobCanvas.setAttribute("height","150px");
// jobCanvas.setAttribute("width", "700px");
// jobCanvas.setAttribute("height","500px");
}
특이 사항은 없고 dom을 이용해서 캔버스의 속성값을 임의로 넓이 높이를 150px( setAttribute("width", "150px"); )로 맞췄습니다
이 상태로 사용하기를 원한다면 임의대로 줄인다음 원래의 크기로 넓이를 다시 잡아주면 눈으로 보이는 건
소스가 지워진 상태로 밖에 안보이겠죠 ( 주석을 풀어서 확인해 보면 됩니다 )
두번째 방법인 지우기 api를 이용하는 방법입니다
ctx.clearRect
지정된 영역만큼을 지우는 방법입니다
함수를 하나 더 만들겠습니다
/*캔버스 지우기2 */
function clearCANVAS(){
var jobCanvas = document.getElementById("canvas1");
if(jobCanvas){
var ctx = jobCanvas.getContext('2d');
ctx.clearRect(0, 0,jobCanvas.width , jobCanvas.height);
}
} // end func
function clearCANVAS(){
var jobCanvas = document.getElementById("canvas1");
if(jobCanvas){
var ctx = jobCanvas.getContext('2d');
ctx.clearRect(0, 0,jobCanvas.width , jobCanvas.height);
}
} // end func
위의 소스는 지정된 영역 즉 죄표가 좌측 상측에서 0인 곳에서부터 캔버스의 넓이 높이만큼을 클리어 시키는 api입니다
캔버스의 넓이와 높이를 가져와서 0에서 부터 클리어 시켜주니 캔버스가 백지가 됩니다
만약 높이와 넓이 좌표를 달리하면 그 영역만큼 지워지게 되겠죠
이렇게 좌표와 크기를 달리쓰면 원하는 만큼 지우개로 지우는 효과를 내게 됩니다
그러므로 위의 소스는 캔버스 지우기와 지우개 소스로 쓰일수 있기에 처음 쓰인 소스에 비해 합리적입니다
위의 소스가 지우개도도 사용된다고 했습니다
그런데 문제가 하나 있습니다 ( clearRect 은 사각형으로만 지워집니다 )
내가 지우고자 하는 곳이 네모 반듯하게만 지워지니 뭔가 허전하게 느껴질수 밖에 없습니다
때에 따라서는 원형으로 지워야 할 경우도 생길수 있습니다 그래야 좀 더 구색에 맞기 때문입니다
이럴 때 사용할 수 있는 소스가 3번째 사용될 글로벌 명령어입니다
ctx.globalCompositeOperation = "destination-out";
즉 원본과 그리는 복사본이 합쳐지는 경계를 어떻게 할것인지에 대해 정의를 하고 겹쳐지는 부분을 혹은 겹치지 않는
부분을 어떻게 할지를 결정하는 부분입니다 ( 일종의 집합 - 합집합 ,교집합 )
3번째 소스를 이용해서 지울 경우 좀더 지우개 기능에 충실한 기능을 발휘합니다
지우기 기능으로 2번째 쓰인 clearRect를 이용해서 하는 원형 지우기도 있지만 이는 소스가 더 까다롭기에
오히려 불편할것 같습니다 저도 2번째 소스를 이용해서 지우기를 한 경우를 가지고 해봤지만 그리 깔끔하게 느껴지지 않아
사용을 하지 않고 3번째를 사용하고 만족하고 있습니다
3번째는 그림을 그리듯 그리는데 다만 지우개라는 특성만 틀릴 뿐이니
먼저 만들어 놓은 드로윙 펑션에 소스를 넣어서 사용합니다
이를 위해서 몇가지를 추가합니다
var nowAction =""; // 현재의 행해지는 액션
위의 전역변수는 현재 작업이 그림그리기인지 지우기인지 색칠하기인지 혹은 대상을 크롭시킬지등 현재의 상태를 알기
위한 지정 전역 변수입니다
그리고 현재의 액션 상태를 알기 위해서 버튼 부분 ( 호출되는 버튼 ) 에 현재 상태를 알리기 위한 인자를 하나 추가합니다
<input type="button" onclick="drowing(event)" value="그리기"> 아래와 같이 'pen' 추가
<input type="button" onclick="drowing(event,'pen')" value="그리기">
함수에서도 현재 상태를 구분해서 지우기인지 색칠하기인지 또는 그림그리기인지 (pen 상태 )를 구분해줍니다
// 그리기
this.draw=function(event){
if(isDraw ==true ){
this.draw=function(event){
if(isDraw ==true ){
if(nowAction =="pen" ){
ctx.lineTo(x2,y2);
ctx.lineWidth = dot;
ctx.strokeStyle =color;
ctx.lineCap = 'round';
ctx.stroke();
}
ctx.lineTo(x2,y2);
ctx.lineWidth = dot;
ctx.strokeStyle =color;
ctx.lineCap = 'round';
ctx.stroke();
}
if(nowAction=="eraser"){ // 지우기
ctx.globalCompositeOperation = "destination-out";
ctx.arc(x2, y2,10/2,0,Math.PI*2,false);
ctx.fillStyle = color;
ctx.fill();
}
} // 마우스가 눌려진 상태라면
}
ctx.globalCompositeOperation = "destination-out";
ctx.arc(x2, y2,10/2,0,Math.PI*2,false);
ctx.fillStyle = color;
ctx.fill();
}
} // 마우스가 눌려진 상태라면
}
그리기를 클릭하게 되면 담긴 인자인 pen에 의해
function drowing(event,objType,objID){
의 두번째 인자인 objType이 받아집니다
nowAction =objType;
전역변수인 nowAction에 objType을 넣어줌으로 현재 무슨상태인지를 전역변수에 저장하게 됩니다
그러므로 현재 상태가 pen인지 혹은 , eraser( 지우기 ) 인지를 구분해서 사용하게 됩니다
ctx.arc(x2, y2,10/2,0,Math.PI*2,false);
ctx.fillStyle = color;
ctx.fill();
동그란 형태로 색을 칠하는 소스입니다 그 위에 쓰인
ctx.globalCompositeOperation = "destination-out";
와 합쳐져서 색이 칠해져야 할 부분이 오히려 지워지는 ( 클리어 )되는 효과를 냅니다 [ 지우개 ]
자 위의 소스만으로 하다 보면 지우고 나서 다시 pen( 그리기 )를 선택하게 되면 지우기만 계속 되는 것을 볼수 있습니다
즉 글로벌 선언을 해줬기 때문에 지우기가 끝나면 이를 해제 해줘야 합니다
이를 위해서
마우스업이 되었을 경우 아래의 소스를 추가해주게 됩니다
document.onmouseup = function(event){ // 마우스 클릭을 떼었을 때
isDraw = false; // 마우스를 떼었을 때
if(nowAction=="eraser"){ // 전역 설정된거 풀기
ctx.globalCompositeOperation = "source-over";
}
nowAction = ""; // 현재의 상태를 비워둠
}
if(nowAction=="eraser"){ // 전역 설정된거 풀기
ctx.globalCompositeOperation = "source-over";
}
nowAction = ""; // 현재의 상태를 비워둠
}
한가지 더 추가해서
그림을 그리고 지우고 나서 그리기를 클릭하지 않은 상태에서 캔버스를 클릭하게 되면 점이 찍히는 것을 방지 하기 위해
thisCanvas.onmousedown = function(event){
if(nowAction){
isDraw = true;
ctx.beginPath();
var x0=(parseInt(event.clientX))-canvasLeft;
var y0=(parseInt(event.clientY))-canvasTop;
isDraw = true;
ctx.beginPath();
var x0=(parseInt(event.clientX))-canvasLeft;
var y0=(parseInt(event.clientY))-canvasTop;
ctx.arc(x0, y0, dot/2, 0, Math.PI*2, true);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
}
}
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
}
}
위와 같이 nowAction 이 일어 났을 때로 한정지어 감싸줍니다
아래는 전체 소스이며
이는 파일로 올려두겠습니다
-------------------------- 이하 전체 소스 ---------------------------
<!DOCTYPE html>
<html lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR" />
<html lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR" />
<style>
html{
margin:0px;
padding :0px;
}
body{
margin:0px;
padding :0px;
}
#contentsLAYER{ /* 콘텐츠 전체 위치 레이어 */
position : absolute;
top : 100px;
left : 250px;
width : 852px;
height : 502px;
background : yellow;
}
#canvasTOOL{ /* 그리기 툴이 담긴 레이어 */
position : absolute;
top : 0px;
left : 0px;
width : 140px;
height : 200px;
background : #eee;
border : 1px solid #ccc;
}
#canvasLAYER{ /* 캔버스가 담긴 레이어 */
position : absolute;
top : 0px;
left : 150px;
width : 702px;
height : 502px;
background : #fff;
}
position : absolute;
top : 0px;
left : 150px;
width : 702px;
height : 502px;
background : #fff;
}
#canvas1{ /* 캔버스 */
border : 1px solid #ccc;
}
border : 1px solid #ccc;
}
</style>
<script type="text/javascript">
function $(id){
var element="";
if(id==""){
return false;
}
else{
element= document.getElementById(id);
return element;
}
}
//이벤트 등록
function addEvent(obj,mouseEVT,fn){
if(window.addEventListener){ // firefox
if(mouseEVT){
mouseEVT=mouseEVT.replace('on','');
obj.addEventListener(mouseEVT , fn, false);
return true;
}
} //end firefox IE8 이상
else{ // IE8 이하
// 마리홈에서는 온마우스 이벤트로 적지 않고 마우스 이벤트로 적었기 때문
mouseEVT=mouseEVT.replace('on',''); // 있다면 일단 지워준다
mouseEVT=mouseEVT.replace(mouseEVT,'on'+mouseEVT); // 추가해줌
obj.attachEvent(mouseEVT , fn);
}//end IE8 이하
}
// 이벤트 삭제
function removeEvent(obj,evt,func){
if( /MSIE/.test(navigator.userAgent) ){/* IE */
obj.detachEvent(evt , func);
}else{
evt=evt.replace('on','');
obj.removeEventListener(evt , func, false);
}
}
function removeEvent(obj,evt,func){
if( /MSIE/.test(navigator.userAgent) ){/* IE */
obj.detachEvent(evt , func);
}else{
evt=evt.replace('on','');
obj.removeEventListener(evt , func, false);
}
}
/* 온로드후 실행 함수 자주쓰기에 구분을 주기 위해 이벤트 함수를 똑 같이 하나 더 만듬*/
function onLoad_Event(fucName){
if(window.addEventListener){ // firefox
window.addEventListener("load",fucName,false);
} //end firefox
else{ // IE
window.attachEvent("onload",fucName);
} //end IE
}
function onLoad_Event(fucName){
if(window.addEventListener){ // firefox
window.addEventListener("load",fucName,false);
} //end firefox
else{ // IE
window.attachEvent("onload",fucName);
} //end IE
}
onLoad_Event(startFUNC);
var canvasTop ="";
var canvasLeft ="";
function startFUNC(){
addEvent(document.body,'mouseover', function(){ //mousedown 스크롤 위치 파악
if(self.pageYOffset>0){// 표준 브라우져
scroll_x= self.pageXOffset;
scroll_y= self.pageYOffset;
}
else
if(document.body){//IE8 이전 버전
scroll_x = document.body.scrollLeft;
scroll_y = document.body.scrollTop;
scroll_x = self.pageXOffset; // 오류로 인해 변경
}
else
if(document.documentElement &&document.documentElement.scrollTop){ //IE8 이전 버전
scroll_x = document.documentElement.scrollLeft;
scroll_y = document.documentElement.scrollTop;
}
canvasTop = ( $("contentsLAYER").offsetTop + $("canvasLAYER").offsetTop + $("canvas1").offsetTop) - parseInt(scroll_y); // 캔버스의 위치
canvasLeft = ( $("contentsLAYER").offsetLeft + $("canvasLAYER").offsetLeft + $("canvas1").offsetLeft) - parseInt(scroll_x);
});
}
}
var nowAction ="";
function drowing(event,objType,objID){
var jobCanvas = document.getElementById("canvas1");
var thisCanvas = document.getElementById("canvas1");
var ctx = jobCanvas.getContext('2d');
var x2="";
var y2="";
var dot = 10;
var color = "red";
var color = "red";
var isDraw = false; //추가 마우스 움직임 여부
nowAction =objType;
thisCanvas.onmousedown = function(event){
thisCanvas.onmousedown = function(event){
if(nowAction){
isDraw = true;
ctx.beginPath();
var x0=(parseInt(event.clientX))-canvasLeft;
var y0=(parseInt(event.clientY))-canvasTop;
isDraw = true;
ctx.beginPath();
var x0=(parseInt(event.clientX))-canvasLeft;
var y0=(parseInt(event.clientY))-canvasTop;
ctx.arc(x0, y0, dot/2, 0, Math.PI*2, true);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
}
}
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
}
}
document.onmouseup = function(event){ // 마우스 클릭을 떼었을 때
isDraw = false; // 마우스를 떼었을 때
if(nowAction=="eraser"){ // 전역 설정된거 풀기
ctx.globalCompositeOperation = "source-over";
}
nowAction = ""; // 현재의 상태를 비워둠
}
if(nowAction=="eraser"){ // 전역 설정된거 풀기
ctx.globalCompositeOperation = "source-over";
}
nowAction = ""; // 현재의 상태를 비워둠
}
thisCanvas.onmousemove = function(event){
//캔버스 정점에서부터의 위치
var x=(parseInt(event.clientX) - canvasLeft);
var y=(parseInt(event.clientY) - canvasTop);
var x=(parseInt(event.clientX) - canvasLeft);
var y=(parseInt(event.clientY) - canvasTop);
ctx.beginPath();
ctx.moveTo( x, y );
draw(event);
x2=x; //위치 정보를 기록한다
y2=y;
}
// 그리기
this.draw=function(event){
if(isDraw ==true ){
if(nowAction =="pen" ){
ctx.lineTo(x2,y2);
ctx.lineWidth = dot;
ctx.strokeStyle =color;
ctx.lineCap = 'round';
ctx.stroke();
}
if(nowAction=="eraser"){
ctx.globalCompositeOperation = "destination-out";
ctx.arc(x2, y2,10/2,0,Math.PI*2,false);
ctx.fillStyle = color;
ctx.fill();
}
}
}
ctx.lineTo(x2,y2);
ctx.lineWidth = dot;
ctx.strokeStyle =color;
ctx.lineCap = 'round';
ctx.stroke();
}
if(nowAction=="eraser"){
ctx.globalCompositeOperation = "destination-out";
ctx.arc(x2, y2,10/2,0,Math.PI*2,false);
ctx.fillStyle = color;
ctx.fill();
}
}
}
}
/*캔버스 지우기1 */
function clearAllCanvas(){
var jobCanvas = document.getElementById("canvas1");
var thisCanvas = document.getElementById("canvas1");
jobCanvas.setAttribute("width", "150px");
jobCanvas.setAttribute("height","150px");
function clearAllCanvas(){
var jobCanvas = document.getElementById("canvas1");
var thisCanvas = document.getElementById("canvas1");
jobCanvas.setAttribute("width", "150px");
jobCanvas.setAttribute("height","150px");
jobCanvas.setAttribute("width", "700px");
jobCanvas.setAttribute("height","500px");
}
jobCanvas.setAttribute("height","500px");
}
/*캔버스 지우기2 */
function clearCANVAS(){
var jobCanvas = document.getElementById("canvas1");
if(jobCanvas){
var ctx = jobCanvas.getContext('2d');
ctx.clearRect(0, 0,jobCanvas.width , jobCanvas.height);
}
} // end func
</script>
</head>
<body>
<div id="contentsLAYER">
<div id="canvasTOOL">
<input type="button" onclick="drowing(event,'pen')" value="그리기">
<input type="button" onclick="drowing(event,'eraser')" value="지우개">
<input type="button" onclick="clearAllCanvas()" value="캔버스 지우기1">
<input type="button" onclick="clearCANVAS()" value="캔버스 지우기2">
</div>
<input type="button" onclick="drowing(event,'pen')" value="그리기">
<input type="button" onclick="drowing(event,'eraser')" value="지우개">
<input type="button" onclick="clearAllCanvas()" value="캔버스 지우기1">
<input type="button" onclick="clearCANVAS()" value="캔버스 지우기2">
</div>
<div id="canvasLAYER">
<canvas id="canvas1" width="700px" height="500px"></canvas>
</div>
</div>
</body>
</html>
<canvas id="canvas1" width="700px" height="500px"></canvas>
</div>
</div>
</body>
</html>
-------------------------- 이상 전체 소스 ---------------------------
위 소스는 마우스를 떼면 그림 그리기든 지우기든 멈추게 됩니다 지우기 위해서는 또 클릭 그리기위해서는 또 클릭을
해줘야 합니다
계속 적인 진행을 원한다면
document.onmouseup = function(event){ // 마우스 클릭을 떼었을 때
isDraw = false; // 마우스를 떼었을 때
if(nowAction=="eraser"){ // 전역 설정된거 풀기
ctx.globalCompositeOperation = "source-over";
}
// nowAction = ""; // 현재의 상태를 비워둠
}
if(nowAction=="eraser"){ // 전역 설정된거 풀기
ctx.globalCompositeOperation = "source-over";
}
// nowAction = ""; // 현재의 상태를 비워둠
}
위와 같이 // nowAction = ""; // 현재의 상태를 비워둠
주석 처리를 하면 됩니다
다음 편에는
펜 또는 지우개의 크기 변경 색 변경 등에 대해 올리겠습니다
추천
0
0
댓글 1개

html5 켄버스 그리기 잘 보겠습니다