웹 프로그래밍/JavaScript

[JavaScript] 자바스크립트 API 활용

0_TLS 2024. 12. 1. 20:51

주요 API

API 설명
Drag and Drop HTML 요소 혹은 파일을 끌어서(드래그) 다른 HTML 요소에 놓을 때(드롭할 때) 
데이터를 전달하는 기능을 제공
Blob 이진 데이터를 다루는 기능을 제공
File 프로그램 여러 개를 멀티스레드로 병렬 처리하는 기능을 제공
Web Workers 대용량이며 저장 기간에 제한이 없는 데이터를 로컬에 저장하는 기능을 제공
Indexed Database 로컬에 키값 타입의 관계형 데이터 베이스 기능을 제공
WebSockets 서버와의 양방향 통신 기능을 제공
Geolocation GPS 등의 위치 정보를 다루는 기능을 제공
Canvas 2차원 3차원 그래픽스 기능을 제공

 

드래그 앤 드롭 API

*HTML 요소나 로컬 파일을 마우스로 끌어서 옮길 수 있으며 다른 요소에 드롭할 수 있음

*이때 드래그한 요소 또는 파일의 데이터는 드롭 타킷 요소에 전달

*HTML 요소를 드래그 할 수 있게 만들기

-<div draggable="true"> 드래그 할 수 있습니다. </div>

-이 속성을 지정하지 않거나 auto로 지정하면 해당 HTML요소의 기본값을 사용

-href 속성을 지정한 a 요소와 src 속성을 지정한 img 요소는 기본적으로 드래그 할 수 있도록 만들어져 있음

 

드래그 앤 드롭 이벤트

API 설명
dragstart 드래그를 시작할 때 발생
drag 드래그를 하는 동안 발생
dragend 드래그가 끝났을 때 발생
dragenter 마우스 포인터가 드롭 요소의 경계선 안쪽으로 들어갈 때 발생
dragover 마우스 포인터가 드롭 요소의 경계선 안쪽에 있을 때 발생
dragleave 마우스 포인터가 드롭 요소의 경계선 바깥으로 나왔을 때 발생
drop 요소에 드롭할 때 발생

 

*모든 드래그 앤 드롭 이벤트는 dataTransfer 프로퍼티를 가짐

*dataTransfer 프로퍼티 값은 Datatransfer 객체이며, 이 객체로 드래그 타깃 요소가 드롭 타깃 요소에 데이터를 전달할 수 있음

*DataTransfer 객체의 프로퍼티와 메소드

프로퍼티 이름/
메소드 이름
설명
type setData 메소드로 설정한 데이터 타입 목록
files 드래그한 파일 객체 목록
effectAllowed 드래그 타깃 요소가 허용하는 작업의 유형("none", "copy", "copyLink", "copyMove" 등)
dropEffect 드롭 타깃 요소에 표시하는 효과("none", "copy", "move", "link")
setData(format, data) 드래그 타깃 요소의 데이터 타입을 특정 데이터 타입으로 설정

 

데이터 전달하기(드래그 타깃 요소에서 드롭 타깃 요소에 데이터 전달)

*드래그 타깃 요소의 dragstart 이벤트 처리기 안에서 dataTransfer 프로퍼티의 setData메소드에 데이터 타입을 지정한 데이터를 추가

e.dataTransfer.setData("text/plain", value);

 

*드롭 타깃 요소의 dragover 이벤트 처리기 안에서 브라우저의 기본 동작을 취소 

드래그 타깃 요소가 드롭 타깃 요소 위에 올라가면 브라우저의 기본 동작인 drop 이벤트가 취소되기 때문.

e.preventDefault();

 

*드롭 타깃 요소의 drop 이벤트 처리기 안에서 dataTransfer 프로퍼티의 getData 메소드를 사용해서 데이터를 지정한 데이터 타입으로 가져옴

 

*var value = e.dataTransfer.getData("text/plain");

데이터는 데이터 타입(format)별로 하나만 전달할 수 있음

 

*setData 메소드로 같은 데이터 타입의 데이터를 두번 설정하면 이전에 설정한 데이터를 덮어씀

 

드래그 앤 드롭 예제

 

<드롭하는 영역을 사용자에게 알림>

<!DOCTYPE html>
<html lang="ko">
<head>
	<meta charset="UTF-8">
    <title> 드롭 하는 영역을 사용자에게 알리기 </title>
    <script>
    	window.onload = function() {
            var dragbox = document.getElementById("dragbox"); //드래그박스 가져오기
            var dropbox = document.getElementById("dropbox"); //드롭박스 가져오기
            
            dropbox.addEventListener("dragenter", function(e) {
            	e.target.style.borderColor = "red";
            }, false);
            dropbox.addEventListener("dragleave", function(e) {
            	e.target.style.borderColor = "gray";
            }, false);
            dropbox.addEventListener("drop", function(e) {
            }, false);
        };
        </script>
        <style>
            #dragbox {width: 150px; border: 10px solid blue; }
            #dropbox {width: 150px; padding: 50px; border: 10px solid blue;}
        </style>
    <body>
    	<div id="dragbox" draggable="true">이것을 드래그하세요</div>
        <div id="dropbox">이곳에 드롭하세요</div>
    </body>
    </head>

 

<드래그 앤 드롭으로 배경색 설정하기>

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>드래그 앤 드롭 예제</title>
<script>
	window.onload = function() {
		var color = document.getElementById("color");
		var dropbox = document.getElementById("dropbox");
		//드래그를 시작할 때, 색상의 값을 dataTransfer 객체의 데이터로 설정한다
		color.ondragstart = function(e) {
			e.dataTransfer.setData("text/plain", e.target.value);
		};
		//드래그 타깃 요소 위에 마우스 포인터가 올라가면, 브라우저의 기본 동작을 취소한다(필수)
		dropbox.ondragover = function(e) {
			e.preventDefault();
		};
		//요소를 드롭하면, dataTransfer의 데이터로 보더 박스의 배경색을 설정한다
		dropbox.ondrop = function(e){
			e.preventDefault(); //브라우저의 기본 동작을 취소한다(선택사항)
			e.target.style.backgroundColor = e.dataTransfer.getData("text/plain");
		};
	};
</script>
<style>
	#color {margin-bottom: 10px; }
	#dropbox {width: 150px; padding: 50px; border: 1px solid gray; }
</style>
</head>
<body> 	
	<input type="color" id="color" draggable="true">
	<div id="dropbox">이곳에 드롭하세요</div>
</body>
</html>

 


[그림판 프로젝트]

painter.html

<!DOCTYPE html>
<html lang="ko">
    <head>
        <meta charset="UTF-8">
        <title>Simple Painter</title>
        <script src="../js/elt.js"></script>
        <script src="../js/painter.js"></script>
        <script>
            window.onload=function(){
                createPainter(document.body, 800, 600);
            };
        </script>
    </head>
    <body>
    </body>
</html>

 

elt.js

function elt(name, attributes){
    var node = document.createElement(name);
    if(attributes){
        for(var attr in attributes){
            if(attributes.hasOwnProperty(attr)){
                node.setAttribute(attr, attributes[attr]);
            }
        }
    }
    for(var i=2;i<arguments.length; i++){
        var child = arguments[i];
        if(typeof child =='string'){
            child = document.createTextNode(child);
        }
        node.appendChild(child);
    }
    return node;
}

 

painter.js

/*--------------------------------------------------------------------------------*
 * 화면을 구성하는 요소를 생성하고,  요소에 이벤트 처리기 등록하기
 *--------------------------------------------------------------------------------*/
function createPainter(parent, width, height) {
	// 타이틀
	var title = elt("h2", null, "Simple Painter");
	// canvas요소와 랜더링 컨텍스트 가져오기
	var [canvas,ctx] = createCanvas(width, height);
	// 도구 막대 : controls 객체의 프로퍼티를 순회하면서 등록한다
	var toolbar = elt("div", null);
	for(var name in controls) {
		toolbar.appendChild(controls[name](ctx));
	}
	toolbar.style.fontSize = "small";
	toolbar.style.marginBottom = "3px";
	// toolbar 요소와 canvas 요소를, 지정한 요소(parent)의 자식 요소로 삽입한다
	parent.appendChild(elt("div", null, title, toolbar, canvas));
}
function createCanvas(canvasWidth,canvasHeight) {
	var canvas = elt("canvas", { width: canvasWidth, height: canvasHeight });
	var ctx = canvas.getContext("2d");
	canvas.style.border = "1px solid gray";
	canvas.style.cursor = "pointer";
	// 그리기 도구를 mousedown 이벤트 처리기로 등록한다
	canvas.addEventListener("mousedown", function(e) {
		// Firefox 대책 : 색상 선택시, change 이벤트를 강제로 발생시킨다
		var event = document.createEvent("HTMLEvents");
		event.initEvent("change", false, true);
		colorInput.dispatchEvent(event);
		// 선택한 그리기 도구를 초기화
		paintTools[paintTool](e,ctx);
	}, false);
	canvas.addEventListener("dragover", function(e) {
		e.preventDefault();
	}, false);
	canvas.addEventListener("drop", function(e) {
		var files = e.dataTransfer.files;
		if( files[0].type.substring(0,6) !== "image/" ) return;
		loadImageURL(ctx, URL.createObjectURL(files[0]));
		e.preventDefault();
	}, false);
	return [canvas,ctx];
}
/*--------------------------------------------------------------------------------*
 * 유틸리티
 *--------------------------------------------------------------------------------*/
// * element의 왼쪽 위 모서리에서 마우스의 상대적 위치를 가져온다
function relativePosition(event, element) {
	var rect = element.getBoundingClientRect();
	return { x: Math.floor(event.clientX - rect.left),
			 y: Math.floor(event.clientY - rect.top ) };
}
/*--------------------------------------------------------------------------------*
 * 그리기 도구
 * paintTools 메서드는 그리기에 사용하는 도구입니다.
 * 그리기 도구는 그리기를 위한 각종 설정과 이벤트 처리기 등록을 담당합니다.
 * 각 메서드는 controls.painter를 통해 자동으로 도구 선택 메뉴에 추가됩니다.
 * 메뉴에서 선택한 도구는, 변수 paintTool에 저장되어 그림을 그릴 때 사용됩니다.
 * 그리기 도구를 추가하려면, paintTools 메서드에 새로운 그리기 도구를 추가하십시오.
 *--------------------------------------------------------------------------------*/
var paintTool; // 선택된 그리기 도구 (controls.painter로 선택)
var paintTools = Object.create(null); // 그리기 도구 객체
// * brush : 브러시 도구
paintTools.brush = function(e, ctx) {
	ctx.lineCap   = "round";
	ctx.lineJoin  = "round";
	// Canvas 화면을 img에 저장한다
	var img = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
	// canvas 요소에 대한, 마우스 포인터의 상대 위치를 구한다
	var p = relativePosition(e, ctx.canvas);
	// 경로를 정의한다
	ctx.beginPath();
	ctx.moveTo(p.x,p.y);
	// 드래그 이벤트 처리기를 등록한다
	setDragListeners(ctx, img, function(q) {
		ctx.lineTo(q.x,q.y); // 경로를 추가한다
		ctx.stroke (); // 경로를 그린다
	});
};
// * line : 선 그리기 도구
paintTools.line = function(e, ctx) {
	ctx.lineCap = "round";
	var img = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
	var p = relativePosition(e, ctx.canvas);
	setDragListeners(ctx, img, function(q) {
		ctx.beginPath();
		ctx.moveTo(p.x,p.y); ctx.lineTo(q.x,q.y);
		ctx.stroke();
	});
};
// * circle : 동그라미 그리기 도구
paintTools.circle = function(e, ctx) {
	var img = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
	var p = relativePosition(e,ctx.canvas);
	setDragListeners(ctx, img, function(q) {
		var dx = q.x - p.x;
		var dy = q.y - p.y;
		var r = Math.sqrt(dx*dx+dy*dy);
		ctx.beginPath();
		ctx.arc(p.x, p.y, r, 0, 2*Math.PI, false);
		ctx.stroke();
	});
};
// * circleFill : 채워진 동그라미 그리기 도구
paintTools.circleFill = function(e, ctx) {
	var img = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
	var p = relativePosition(e,ctx.canvas);
	setDragListeners(ctx, img, function(q) {
		var dx = q.x - p.x;
		var dy = q.y - p.y;
		var r = Math.sqrt(dx*dx+dy*dy);
		ctx.beginPath();
		ctx.arc(p.x, p.y, r, 0, 2*Math.PI,false);
		ctx.fill();
	});
};
/*--------------------------------------------------------------------------------*
 * 그리기 도구의 유틸리티
 *--------------------------------------------------------------------------------*/
 // * 마우스를 드래그할 때의 이벤트 리스너를 등록한다
 function setDragListeners(ctx,img,draw) {
 	// mousemove 이벤트 리스너를 등록한다
 	var mousemoveEventListener = function(e) {
		// 저장한 이미지를 읽어들인다
		ctx.putImageData(img, 0, 0);
		// 지정한 그리기 함수 draw로 마우스 위치까지 그린다
		draw(relativePosition(e, ctx.canvas));	
 	};
 	document.addEventListener("mousemove", mousemoveEventListener, false);
 	// mouseup 이벤트 처리기를 등록한다
	document.addEventListener("mouseup", function(e) {
		// 저장한 이미지를 읽어들인다
		ctx.putImageData(img, 0, 0);
		// 지정한 그리기 함수 draw로 마우스 위치까지 그린다
		draw(relativePosition(e, ctx.canvas));
		// mousemove, mouseup 이벤트 리스너를 제거한다
		document.removeEventListener("mousemove", mousemoveEventListener, false);
		document.removeEventListener("mouseup", arguments.callee, false);
	},false);
 }
/*--------------------------------------------------------------------------------*
 * 컨트롤러
 * 각종 설정을 변경하는 제어판을 정의합니다.
 * 각 컨트롤러는 controls 객체의 메서드로 등록되어 있습니다.
 * 각 메서드는 필요한 HTML 요소를 생성해서 반환하며, 이벤트 리스너를 등록합니다.
 * 각 메서드는, createPainter를 통해 자동으로 도구 막대에 추가됩니다.
 * 새로운 컨트롤을 추가하려면, controls 객체에 새로운 메서드를 추가하십시오.
 *--------------------------------------------------------------------------------*/
var controls = Object.create(null);	// 컨트롤러 객체
var colorInput; // Firefox의 change 이벤트 대책. input[type="color"] 객체를 저장한다
// * 그리기 도구 선택
controls.painter = function(ctx) {
	var DEFAULT_TOOL = 0;
	var select = elt("select", null);
	var label = elt("label", null, "그리기 도구 : ", select);
	for(var name in paintTools) {
		select.appendChild(elt("option", {value: name}, name));
	}
	select.selectedIndex = DEFAULT_TOOL;
	paintTool = select.children[DEFAULT_TOOL].value;
	select.addEventListener("change", function(e) {
		paintTool = this.children[this.selectedIndex].value;
	},false);
	return label;
};
// * 색상 선택 (선과 채우기를 모두 설정함 → 필요하면 별도의 컨트롤로 만드세요)
controls.color = function(ctx) {
	var input = colorInput = elt("input", {type: "color"});
	var label = elt("label", null, " 색:", input);
	input.addEventListener("change", function (e) {// 참고 : Firefox에서는 change 이벤트가 발생하지 않습니다.
		ctx.strokeStyle = this.value;
		ctx.fillStyle = this.value;
	},false);
	return label;
};
// * 선의 너비 선택
controls.brushsize = function(ctx) {
	var size = [1,2,3,4,5,6,8,10,12,14,16,20,24,28];
	var select = elt("select", null);
	for(var i=0; i<size.length; i++) {
		select.appendChild(elt("option",{value:size[i].toString()},size[i].toString()));
	}
	select.selectedIndex = 2;
	ctx.lineWidth = size[select.selectedIndex];
	var label = elt("label",null," 선의 너비:",select);
	select.addEventListener("change", function(e) {
		ctx.lineWidth = this.value;
	},false);
	return label;
};
// * 투명도 선택
controls.alpha = function(ctx) {
	var input = elt("input", {type:"number",min:"0", max:"1",step:"0.05",value:"1"});
	var label = elt("label", null, " 투명도:", input);
	input.addEventListener("change", function(e) {
		ctx.globalAlpha = this.value;
	},false);
	return label;
};
controls.save = function(ctx) {
	var input = elt("input", {type: "button", value:"저장"});
	var label = elt("label", null, " ", input);
	input.addEventListener("click", function(e) {
		var dataURL = ctx.canvas.toDataURL();
		open(dataURL, "save");
	}, false);
	return label;
};
var filterTools = Object.create(null);
controls.filter = function(ctx) {
	var DEFAULT_FILTER = 0;
	var select = elt("select",null);
	var label  = elt("label",null, " ",select);
	select.appendChild(elt("option",{value: "filter"},"필터"));
	for(var name in filterTools) {
		select.appendChild(elt("option",{value: name},name));
	}
	select.selectedIndex = DEFAULT_FILTER;
	select.addEventListener("change", function(e) {
		var filterTool = this.children[this.selectedIndex].value;
		var inputImage = ctx.getImageData(0,0,ctx.canvas.width,ctx.canvas.height);
		var outputImage = filterTools[filterTool](inputImage);
		ctx.putImageData(outputImage,0,0);
		select.selectedIndex = DEFAULT_FILTER;
	}, false);
	return label;
};
function weightedAverageFilter(image, n, Weight, keepBrightness, offset) {
	var width = image.width, height = image.height;
	var outputImage = new ImageData(width, height);
	for(var x=0; x<width; x++) {
		for(var y=0; y<height; y++) {
			var iR = 4*(width*y+x);
			for(var i=0; i<3; i++) {
				var average = 0, weightSum = 0;
				for(ix=-n; ix<=n; ix++) {
					var xp = x + ix;
					if(xp<0 || xp>=width) continue;
					for(iy=-n; iy<=n; iy++) {
						var yp = y + iy;
						if(yp<0 || yp>=height) continue;
						var w = Weight[iy+n][ix+n];
						weightSum += w;
						average += w*image.data[4*(width*yp+xp)+i];
					}
				}
				if(keepBrightness) {
					average /= weightSum;
				}
				outputImage.data[iR+i] = average + offset;
			}
			outputImage.data[iR+3] = image.data[iR+3];
		}
	}
	return outputImage;
}
// * 블러 필터
filterTools.blur = function(inputImage) {
	var size = 2;
	var W = [];
	for(var i=0; i<=2*size; i++) {
		W[i] = [];
		for(var j=0; j<=2*size; j++) {
			W[i][j] = 1;
		}
	}
	return weightedAverageFilter(inputImage, size, W, true, 0);
};
// * 샤프 필터
filterTools.sharp = function(inputImage) {
	var W = [[ 0,-1, 0],
	         [-1, 5,-1],
	         [ 0,-1, 0]];
	return weightedAverageFilter(inputImage, 1, W, false, 0);
};
// * 엠보싱 필터
filterTools.emboss = function(inputImage) {
	var W = [[-1, 0, 0],
	         [ 0, 0, 0],
	         [ 0, 0, 1]];
	return weightedAverageFilter(inputImage, 1, W, false, 128);
};
// * 테두리 강조
filterTools.edgeDetection = function(inputImage) {
	var W = [[-1,-1,-1],
	         [-1, 8,-1],
	         [-1,-1,-1]];
	return weightedAverageFilter(inputImage, 1, W, false, 0);
};
// 파일 입력 컨트롤
controls.file = function(ctx) {
	var input = elt("input", {type: "file"});
	var label = elt("label", null, " ", input);
	input.addEventListener("change", function(e) {
		if(input.files.length == 0) return;
		var reader = new FileReader();
		reader.onload = function() {
			loadImageURL(ctx, reader.result);
		};
		reader.readAsDataURL(input.files[0]);
	}, false);
	return label;
};
// url을 ctx에 그린다 (그림이 Canvas의 경계선에 내접하도록 한다)
function loadImageURL(ctx, url) {
	var image = document.createElement("img");
	image.onload = function() {
		var factor = Math.min(
			ctx.canvas.width/this.width, ctx.canvas.height/this.height
		);
		var wshift = (ctx.canvas.width  - factor*this.width )/2;
		var hshift = (ctx.canvas.height - factor*this.height)/2;
		var savedColor = ctx.fillStyle;
		ctx.fillStyle = "white";
		ctx.fillRect(0,0,ctx.canvas.width,ctx.canvas.height);
		ctx.drawImage(image, 0, 0,
			this.width, this.height, wshift, hshift,
			this.width*factor, this.height*factor
		);
		ctx.fillStyle = savedColor;
	};
	image.src = url;
}