+function($){ 'use strict'; /** * Pure Canvas - Class Definition. */ // 申明一个全局变量监控是否按下空格键 var move_flag = false; var this_recWidth = null; var this_recHeight = null; // document.onkeydown=function(event){ // var e = event || window.event || arguments.callee.caller.arguments[0]; // alert(111); // if(e.keyCode==32){ // move_flag = true; // } // } // document.onkeyup=function(event){ // var e = event || window.event || arguments.callee.caller.arguments[0]; // move_flag = false; // } var PureCanvas = function(element, options){ // PureCanvas Div element 정보(createCanvas에서 추가됨) this.$element = $(element); this.$main = null; this.$container = null; // 설정 정보 this.options = options; // Canvas ID 지정 if(!this.options.setting.id) this.options.setting.id = this.createUUID.call(this); // Canvas 정보 this.canvasInfo = {}; // background image 정보 this.imageInfo = {}; // Draw 히스터리 정보 this.historyInfo = { index: -1, drawData: [] } // Canvas 생성 this.createCanvas.call(this); // Event 생성 this.makeCanvasEvent.call(this); // // 监控按键 //document.onkeydown=function(event){ //var e = event || window.event || arguments.callee.caller.arguments[0]; // if(e.shiftKey){ // move_flag = true; // } //} //document.onkeyup=function(event){ // var e = event || window.event || arguments.callee.caller.arguments[0]; // move_flag = false; //} document.addEventListener("keydown",function(event){ var e = event || window.event || arguments.callee.caller.arguments[0]; //e.preventDefault(); if(e.shiftKey){ //if(e.keyCode==32){ move_flag = true; } }); document.addEventListener("keyup",function(event){ var e = event || window.event || arguments.callee.caller.arguments[0]; if(e.shiftKey){ //if(e.keyCode==32){ move_flag = false; } move_flag = false; this_recWidth = null; this_recHeight = null; }); } PureCanvas.VERSION = '0.1'; PureCanvas.DEFIN = { // 사용자 옵션으로 호출 가능한 prototype optionName: { // Toolkit 정보 설정 toolkit: 'toolkit', // setting 정보 설정 setting: 'setting', // Canvas resizing 작업 호출 resize: 'resize', // draw undo, redo event history: 'history', } } // 사용자에 의해 설정 변경 가능한 항목 PureCanvas.DEFAULTS = { // Canvas의 설정 정보 setting: { // 그리기 권한 authForDraw: true, // 그리기 권한이 없는 경우, 마우스 커스 모양 notAuthForDrawCursor: 'not-allowed', // 마우스포인터 사용 권한 pointerForDraw: true, // 마우스포인터 클릭 시 전송 여부 pointerDownSend: true, // 화면 사이즈 설정 정보(page:쪽맞춤, rate:비율) resizeType: 'rate', // 화면 비율 정보(page일 경우 size에 맞게 계산, rate일 경우 입력 값) 1 = 100% rateVal: 1, // 마우스포인터 전송 지연 시간(ms) delayMousePoint: 3, containerStyle: {}, // point 비율 계산에 따른 소수점 자리수 pointFixed: 1, //autoWindowResize windowResizeEvent: true, }, // Toolkit 정보 toolkit: { // Toolkit Type, '$.pureCanvas.toolkit.type'에 정의된 것에 한함. type: 'Cursor', // context style 설정 style: { lineCap: 'round', lineJoin: 'round', // 선 색, 선택으로 채움색까지 사용함. strokeStyle: "rgba(0,0,0,100)", // 채움 색 fillStyle: "rgba(0,0,0,100)", // 선 굴기 lineWidth: 1, }, }, } $.extend(PureCanvas.prototype, { /** * Create UUID */ createUUID: function(){ var uuid = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); return v.toString(16); }); return uuid; }, /** * Create Canvas Element & Info */ createCanvas: function(){ // Canvas 정보 this.canvasInfo = { // Background Canvas 생성 bg: {domView: true, resize: true, clearView: false}, //true, true, false // Main Canvas 생성 - view을 비율에 맞게 조정한 Canvas main: {domView: true, resize: true, clearView: false}, //true, true, true // View Canvas 생성 - recvDraw+drawTemp+mView를 합친 Canvas view: {domView: false, resize: false, clearView: true}, // false, false, true // recvDraw Canvas 생성 - 외부 수신 데이터를 임시 Draw하는 Canvas recvDraw: {domView: false, resize: false, clearView: false}, //false, false, false // mView Canvas 생성 - 중요 포인트를 Draw하는 Canvas mView: {domView: false, resize: false, clearView: true}, //false, false, true // Pointer Canvas 생성 - 마우스포인트를 Draw하는 Canvas pointer: {domView: true, resize: true, clearView: false}, //true, true, false // DrawTemp Canvas 생성 - 원본 크기로 Draw에서 그린 데이터를 원본 크기로 임시로 다시 그리는 Canvas drawTemp: {domView: false, resize: false, clearView: false}, //false, false, false // Draw Canvas 생성 - 비율에 따른 변경되는 사용자가 임시로 그리고 있는 Canvas draw: {domView: true, resize: true, clearView: false}, //true, true, false } // Style & Attribute setting, append element this.$element.css({'display': 'table'}).attr('data-pure-canvas', 'element'); var $main = $('
').attr('data-pure-canvas', 'main') .css({'display': 'block', 'position': 'absolute', 'overflow': 'auto', 'width': 'inherit', 'height': 'inherit'}) .appendTo(this.$element); var $container = $('
').attr('data-pure-canvas', 'container') //.css({'border': '1px solid'}) .css(this.options.setting.containerStyle) .appendTo($main); this.$main = $main; this.$container = $container; // Canvas 정보 목록에 존재하는 Canvas 생성 for(var key in this.canvasInfo){ var data = this.canvasInfo[key]; // Canvas 생성 및 설정 var $canvas = $('').attr('data-pure-canvas-type', key); this.settingDefaultStyle($canvas); if(data.domView) this.$container.append($canvas); //if(!data.domView) $canvas.css({'display': 'none'}); //$container.append($canvas); // Canvas 추가 정보 설정 data.type = key; data.$canvas = $canvas; data.canvas = $canvas.get(0); data.context = $canvas.get(0).getContext('2d'); } console.debug(this.canvasInfo); }, /** * Canvas 기본 스타일 설정 */ settingDefaultStyle: function($canvas){ var $element = this.$element; console.log($element); //var divWidth = $element.width(); //var divHeight = $element.height(); //var divWidth = 1295; //var divHeight = 950; var divWidth = $element.attr("data-width"); var divHeight = $element.attr("data-height"); $canvas.get(0).width = parseInt(divWidth); $canvas.get(0).height = parseInt(divHeight); $canvas.css({'position': 'absolute'}); }, /** * Mouse, Touch Event 생성 */ makeCanvasEvent: function(){ var THIS = this; var setting = this.options.setting; var toolkit = this.options.toolkit; var $drawCanvas = this.canvasInfo.draw.$canvas; $drawCanvas.on('mousedown mousemove mouseup mouseover mouseout touchstart touchmove touchend', function(e){ // 그리기 권한이 없는 경우 이벤트를 수행하지 않음. if(!setting.authForDraw){ return; } // Touch Event인 경우 처리 if(e.type.indexOf('touch') >= 0){ e.preventDefault(); e.isTouch = true; } // 이벤트 분류 var type = e.type; var callMethod = null; switch (type) { case 'mousedown': case 'touchstart': callMethod = 'drawStart'; // Touch가 아닌 경우(Mouse 인 경우) 왼쪽 클릭 만 허용함. if(!e.isTouch && e.button !== 0){ return; } break; case 'mousemove': //case 'mouseover': case 'touchmove': callMethod = 'drawing'; break; case 'mouseup': case 'touchend': callMethod = 'drawEnd'; break; case 'mouseout': callMethod = 'drawOut'; break; } // 추가 이벤트 정보 설정 $.extend(e, { type: 'drawEvent-' + toolkit.type, callMethod: callMethod, eventType: type, toolkitType: toolkit.type }); //console.log(e.eventType, e.type, e.callMethod, e.timeStamp, e); // PureCanvas Event 호출 try{ $drawCanvas.trigger(e); }catch(ex){ console.warn('drawEvent-%s event error. [%s]', toolkit.type, ex); } }) }, /** * Loading Bar 설정 */ loadingBar: function(option, message){ // Create & Show if(!option || option === 'show'){ // loading bar 생성 if(!this.loadingBarDiv){ this.loadingBarDiv = $('
') .attr('data-pure-canvas', 'loadingBar') .css({'background-color': 'rgba(0,0,0, 0.5)', position: 'absolute', 'text-align': 'center', 'display': 'none'}) .appendTo(this.$element); var messageBox = $('
') .css({padding: '10px', 'background-color': 'rgb(0,0,0)', color: '#fff', height: '21px'}) .appendTo(this.loadingBarDiv); this.loadingBarDiv.data('messageBox', messageBox); } var messageBox = this.loadingBarDiv.data('messageBox'); // 내용 변경 this.loadingBarDiv.css({width: this.$element.width(), height: this.$element.height()}); var top = (this.$element.height() / 2) - messageBox.height(); messageBox.css({'margin-top': top}).html(message); // fade show this.loadingBarDiv.fadeIn(500); } // Hide else if(option === 'hide'){ // fade hide this.loadingBarDiv.fadeOut(500); } }, }); /** * Pure Canvas - Context Style Setting - 'toolkit' options */ PureCanvas.prototype.toolkit = function(targetName, value){ // getter if(!targetName && value == undefined) return this.options.toolkit; if(targetName && value == undefined) return this.options.toolkit[targetName]; // setter return this.toolkit[targetName] ? this.toolkit[targetName].call(this, value) : undefined; } $.extend(PureCanvas.prototype.toolkit, { type: function(value){ var toolkit = this.options.toolkit; var toolkitType = $.pureCanvas.toolkit.type[value]; if(toolkitType == undefined){ console.warn('not match toolkit type. ', value); return; } // 즉시 처리 로직 수행 if(toolkitType.instantProcess){ console.debug('setting value instantProcess.: ' + value); toolkitType.instantProcess(); return; } toolkit.type = value; console.debug('setting toolkit.type : ' + value, toolkit); // 커서 모양 변경 this.canvasInfo.draw.canvas.style.cursor = toolkitType.getCursor(); }, lineWidth: function(value){ // Use : BallPen, highlighter, StraightLine, Rectangle this.toolkit.contextStyle.call(this, 'lineWidth', value); }, lineCap: function(value){ // Use : BallPen, highlighter, StraightLine // Style : butt, round, square this.toolkit.contextStyle.call(this, 'lineCap', value); }, lineJoin: function(value){ // Use : BallPen, highlighter // Style : miter, round, bevel this.toolkit.contextStyle.call(this, 'lineJoin', value); }, strokeStyle: function(value){ if(typeof value == 'string') value = {color: value, opacity: 100}; // Use : BallPen, highlighter, StraightLine, Circle, Triangle, Rectangle this.toolkit.contextStyle.call(this, 'strokeStyle', this.pureCanvasToolkit.hexToRgba(value.color, value.opacity)); }, fillStyle: function(value){ if(typeof value == 'string') value = {color: value, opacity: 100}; // Use : BallPen, highlighter, StraightLine, Circle, Triangle, Rectangle this.toolkit.contextStyle.call(this, 'fillStyle', this.pureCanvasToolkit.hexToRgba(value.color, value.opacity)); }, contextStyle: function(styleName, value){ var toolkit = this.options.toolkit; toolkit.style[styleName] = value; console.debug('setting style [' + styleName + '] apply:' + toolkit.style[styleName] + ' , input:' + value); }, draw: function(toolkitData){ if(typeof toolkitData != 'object') return; // 이벤트 생성 및 추가 정보 설정 var event = $.Event('drawEvent-' + toolkitData.type, { callMethod: 'draw', eventType: 'draw.pureCanvas', toolkitData: toolkitData, toolkitType: toolkitData.type, }); // draw canvas에 Event 호출 try{ this.canvasInfo.draw.$canvas.trigger(event); }catch(ex){ console.warn('drawEvent-%s event error. [%s]', toolkitData.type, ex); } }, }); /** * Pure Canvas - Canvas Setting - 'setting' options */ PureCanvas.prototype.setting = function(targetName, value){ // getter if(!targetName && value == undefined) return this.options.toolkit; if(targetName && value == undefined) return this.options.setting[targetName]; // setter return this.setting[targetName] ? this.setting[targetName].call(this, value) : undefined; } $.extend(PureCanvas.prototype.setting, { authForDraw: function(value){ this.options.setting.authForDraw = value; if(value){ var cursor = $.pureCanvas.toolkit.type[this.options.toolkit.type].getCursor(); this.canvasInfo.draw.canvas.style.cursor = cursor; }else{ this.canvasInfo.draw.canvas.style.cursor = this.options.setting.notAuthForDrawCursor; } console.debug('setting setting.authForDraw : ' + value); }, pointerForDraw: function(value){ this.options.setting.pointerForDraw = value; console.debug('setting setting.pointerForDraw : ' + value); }, pointerDownSend: function(value){ this.options.setting.pointerDownSend = value; console.debug('setting setting.pointerDownSend : ' + value); }, backgroundImage: function(value){ var THIS = this; var callbackFunction; var _width = value.width; var _height = value.height; if(typeof value == 'object'){ callbackFunction = value.callback; value = value.imageSrc; } if(THIS.imageInfo && THIS.imageInfo.imgSrc === value){ console.debug('duplication. image. ' + value); return; } var image = new Image(); var sDate = new Date(); this.loadingBar.call(this, 'show', 'Loading...'); var $element = this.$element; image.onload = function(){ //var imageWidth = image.width; //var imageHeight = image.height; //var imageWidth = 1295; //var imageHeight = 950; var imageWidth = $element.attr("data-width"); var imageHeight = $element.attr("data-height"); var imageWidth = _width; var imageHeight = _height; //alert(imageHeight); // 이미지 원본 크기와 동일해야 되는 Canvas 크기 조정, cavnasInfo: resize = false $.each(THIS.canvasInfo, function(key, canvas){ if(!canvas.resize){ canvas.canvas.width = imageWidth; canvas.canvas.height = imageHeight; } }); // 이미지 정보 저장 THIS.imageInfo = { // 이미지 객체 image: image, // 이미지 주소 imgSrc: value, // 이미지 원본 가로 크기 orgImageWidth: imageWidth, // 이미지 원본 세로 크기 orgImageHeight: imageHeight, // re draw 여부 isSetting: true } THIS.historyInfo = { index: -1, drawData: [] } // canvas resize 호출 THIS.resize(); var eDate = new Date(); console.debug("image loading time: %dms", eDate - sDate); try{ // 백그라운드 이미지 출력 완료 이벤트 발생 if(callbackFunction && typeof callbackFunction == 'function'){ callbackFunction({id: THIS.options.setting.id, imageSrc: value}); } }catch(ex){ console.warn('callbackFunction event error. [%s]', ex); } // try{ // THIS.$element.trigger({ // type: 'show.bg.pureCanvas', // imageData: {imageSrc: value} // }); // }catch(ex){ // console.warn('show.bg.pureCanvas event error. [%s]', ex); // } THIS.loadingBar.call(THIS, 'hide'); } image.onerror = function(){ console.error("["+value+"] image loading Error."); } image.src = value; }, resizeType: function(value){ // page(쪽맞춤), rate(비율) if(typeof value == 'string'){ if(value.indexOf('rate') >= 0){ var split = value.split("_"); this.options.setting.resizeType = split[0]; this.options.setting.rateVal = split[1] / 100; }else{ this.options.setting.resizeType = value; this.options.setting.rateVal = 1; } }else{ this.options.setting.resizeType = value.type; this.options.setting.rateVal = value.rateVal ? value.rateVal / 100 : 1; } // canvas resize 호출 this.imageInfo.isSetting = true; this.resize(); console.debug('setting resizeType: ' + this.options.setting.resizeType, this.options.setting.rateVal); }, scroll: function(value){ this.pureCanvasToolkit.recvScrollData(value); } }); /** * Canvas 크기 변경 */ PureCanvas.prototype.resize = function(){ if(this.resize[this.options.setting.resizeType]){ var data = this.resize[this.options.setting.resizeType].call(this) this.resize.canvasResizeNDraw.call(this, data); } try{ this.$element.trigger({ type: 'canvas-resize.pureCanvas' }); }catch(ex){ console.warn('canvas-resize.pureCanvas event error. [%s]', ex); } } $.extend(PureCanvas.prototype.resize, { // 비율(%) rate: function(){ var imageInfo = this.imageInfo; var rateVal = this.options.setting.rateVal; // 이미지 원본 크기 * 비율 = 비율에 맞는 이미지 크기 var width = imageInfo.orgImageWidth * rateVal; var height = imageInfo.orgImageHeight * rateVal; return {width: width, height: height}; }, //쪽맞춤 page: function(){ var imageInfo = this.imageInfo; // main div의 크기와 원본 이미지 크기로 비율 정보 계산 var rateWidth = this.$main.width() / imageInfo.orgImageWidth; var rateHeight = this.$main.height() / imageInfo.orgImageHeight; // 가로, 세로 중 비율 정보가 작은 값 사용 var rateVal = (rateWidth > rateHeight) ? rateHeight : rateWidth; this.options.setting.rateVal = rateVal; // 이미지 원본 크기 * 비율 = 비율에 맞는 이미지 크기 var width = imageInfo.orgImageWidth * rateVal; var height = imageInfo.orgImageHeight * rateVal; // 쪽맞춤일 경우 window.resize에 따라 이미지 크기가 변경됨으로 true 값 설정 imageInfo.isSetting = true; return {width: width, height: height}; }, canvasResizeNDraw: function(data){ var width = data.width; var height = data.height; var imageInfo = this.imageInfo; var rateVal = this.options.setting.rateVal; // 왼쪽, 위 마진 정보 계산, var marginLeft = this.$main.width() / 2 - (width / 2); var marginTop = this.$main.height() / 2 - (height / 2) // border, padding size를 고려하여 가로, 세로 크기를 변경한다. width -= (this.$container.outerWidth() - this.$container.width()); height -= (this.$container.outerHeight() - this.$container.height()); this.$container.css({width: width, height: height, 'margin-left': marginLeft < 0 ? 0 : marginLeft, 'margin-top': marginTop < 0 ? 0 : marginTop}); if(imageInfo.isSetting){ // 이미지 원본 크기와 동일해야 되는 Canvas 크기 조정, cavnasInfo: resize = false $.each(this.canvasInfo, function(key, canvas){ if(canvas.resize){ canvas.canvas.width = width; canvas.canvas.height = height; } }); var mainCtx = this.canvasInfo.main.context; var bgCtx = this.canvasInfo.bg.context; // 메인의 draw data가 비율에 맞게 변경하여 다시 출력한다. mainCtx.clearCanvas(); mainCtx.save(); mainCtx.setTransform(rateVal, 0, 0, rateVal, 0, 0); mainCtx.drawImage(this.canvasInfo.view.canvas, 0, 0); mainCtx.drawImage(this.canvasInfo.mView.canvas, 0, 0); mainCtx.restore(); bgCtx.drawImage(imageInfo.image, 0, 0, width, height); imageInfo.isSetting = false; } } }); /** * History */ PureCanvas.prototype.history = function(targetName){ var isSend = true; if(typeof targetName == 'object'){ targetName = targetName.action; isSend = false; } this.history[targetName] ? this.history[targetName].call(this, isSend) : undefined; } $.extend(PureCanvas.prototype, { historyAdd: function(){ var ctx = this.canvasInfo.view.context; //此处注释掉解决out of memory //var snapshot = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height); var snapshot = null; this.historyInfo.drawData[++this.historyInfo.index] = snapshot; while (this.historyInfo.index < this.historyInfo.drawData.length -1) { this.historyInfo.drawData.pop(); } }, changeImage: function(){ var ctx = this.canvasInfo.view.context; // 초기 화면(clear) if(this.historyInfo.index < 0){ ctx.clearCanvas(); }else{ // 저장된 index 정보의 이미지로 그린다. var snapshot = this.historyInfo.drawData[this.historyInfo.index]; ctx.putImageData(snapshot, 0, 0); } this.pureCanvasToolkit.mainCanvasChange(true); } }); $.extend(PureCanvas.prototype.history, { next: function(isSend){ if(this.history.hasNext.call(this)){ this.historyInfo.index++; this.changeImage(); if(isSend){ try{ this.$element.trigger({ type: 'history.pureCanvas', historyData: {id: this.options.setting.id, action: 'next', index: this.historyInfo.index} }); }catch (ex) { console.warn('history.pureCanvas event error. [%s]', ex); } } } }, prev: function(isSend){ if(this.history.hasPrev.call(this)){ --this.historyInfo.index; this.changeImage(); if(isSend){ try{ this.$element.trigger({ type: 'history.pureCanvas', historyData: {id: this.options.setting.id, action: 'prev', index: this.historyInfo.index} }); }catch(ex){ console.warn('history.pureCanvas event error. [%s]', ex); } } } }, hasNext: function(){ return this.historyInfo.index < this.historyInfo.drawData.length - 1; }, hasPrev: function(){ return this.historyInfo.index >= 0; } }); /** * Pure Canvas - Plug-in Definition. */ function Plugin(option, _relatedTarget, _relatedValue){ var $this = $(this); var data = $this.data('pure.pureCanvas'); var options = $.extend(true, {}, PureCanvas.DEFAULTS, $this.data, typeof option == 'object' && option); if(!data){ $this.data('pure.pureCanvas', (data = new PureCanvas(this, options))); data.pureCanvasToolkit = new $.pureCanvas.toolkit(this); } if(typeof option == 'string' && PureCanvas.DEFIN.optionName[option]){ var returnValue = data[option](_relatedTarget, _relatedValue); return returnValue == undefined ? $this : returnValue; } return $this; } $.fn.pureCanvas = Plugin; $.fn.pureCanvas.Constructor = PureCanvas; /** * Pure Canvas - Event */ $(window).on('load', function(){ $(window).on('resize', function(){ $('[data-pure-canvas="element"]').each(function(index, element){ if($(element).pureCanvas('setting', 'windowResizeEvent')){ $(element).pureCanvas('resize'); } }); }); }); /*******************************************************************************************************************************/ $.pureCanvas = {}; $.pureCanvas.toolkit = function(element){ // Element 정보 this.$element = element; this.$main = this.$element.find('[data-pure-canvas="main"]'); // 설정 정보 this.options = this.$element.data('pure.pureCanvas').options; this.setting = this.options.setting; this.toolkit = this.options.toolkit; // canvas 정보 this.canvasInfo = this.$element.data('pure.pureCanvas').canvasInfo; this.drawCanvas = this.canvasInfo.draw.canvas; this.$drawCanvas = this.canvasInfo.draw.$canvas; this.drawCtx = this.canvasInfo.draw.context; // this.drawTempCtx = this.canvasInfo.drawTemp.context; this.recvDrawCtx = this.canvasInfo.recvDraw.context; this.viewCtx = this.canvasInfo.view.context; this.mViewCtx = this.canvasInfo.mView.context; this.mainCtx = this.canvasInfo.main.context; // Toolit Event 생성 this.makeEvent(); } $.extend($.pureCanvas.toolkit, { prototype: { makeEvent: function(){ var THIS = this; // Toolkit 별 event 생성 $.each($.pureCanvas.toolkit.type, function(toolkitType, toolkit){ // $.pureCanvas.toolkit의 공통 function을 사용하기 위해 $.extend 함. $.extend(toolkit, THIS); // 초기 설정이 있는 경우 if(toolkit.init) toolkit.init(); THIS.$drawCanvas.on('drawEvent-' + toolkitType, function(e){ // Canvas에 직접 Draw 하는 것만 좌표를 계산한다. if(e.eventType != 'draw.pureCanvas'){ // 좌표 계산, touchend event는 값 없음. e.point = THIS.getPoint(e); } // Toolkit Type의 callMethod가 있는 경우 수행한다. var toolkitType = $.pureCanvas.toolkit.type[e.toolkitType]; if(toolkitType[e.callMethod]) toolkitType[e.callMethod](e); }); }); }, sendDrawData: function(points){ var data = { id: this.setting.id, type: this.getType(), style: this.toolkit.style, points: points ? (Object.prototype.toString.call(points) === "[object Array]") ? points.join(',') : points : null } try{ this.$element.trigger({ type: 'complate.draw.pureCanvas', drawData: data, }); }catch(ex){ console.warn('complate.draw.pureCanvas event error. [%s]', ex); } }, sendScrollData: function(){ // Scroll bar의 크기를 구한다. var scrollBarWidth = this.$main.prop("scrollWidth") - this.$main.prop("clientWidth"); var scrollBarHeight = this.$main.prop("scrollHeight") - this.$main.prop("clientHeight"); // 현재 Scroll 위치를 구한다. var scrollLeft = this.$main.scrollLeft(); var scrollTop = this.$main.scrollTop(); // 이동한 위치의 비율을 계산한다. var leftRate = scrollLeft / scrollBarWidth; var topRate = scrollTop / scrollBarHeight; var data = { scrollBarWidth: scrollBarWidth, scrollBarHeight: scrollBarHeight, scrollLeft: scrollLeft, scrollTop: scrollTop, left: leftRate, top: topRate } try{ this.$element.trigger({ type: 'scroll-move.pureCanvas', scrollData: data }); }catch (ex) { console.warn('scroll-move.pureCanvas event error. [%s]', ex); } }, recvScrollData: function(data){ // Scroll bar의 크기를 구한다. var scrollBarWidth = this.$main.prop("scrollWidth") - this.$main.prop("clientWidth"); var scrollBarHeight = this.$main.prop("scrollHeight") - this.$main.prop("clientHeight"); // 이동한 위치의 비율을 계산한다. var scrollLeft = scrollBarWidth * data.left; var scrollTop = scrollBarHeight * data.top; // event 중복 호출 방지, 수신으로 변경된 정보가 event를 타고 나가는것 방지 this.$main.data('pureCanvas-scroll', {type: 'recv', isTrigger: false}); this.$main.scrollLeft(scrollLeft); this.$main.scrollTop(scrollTop); }, getPoint: function(e){ var x, y; // Mouse Event일 경우 if(e.eventType.indexOf("mouse") >= 0){ //console.log(e.type, e.offsetX || e.pageX - $(e.target).offset().left, e.offsetY || e.pageY - $(e.target).offset().top); x = typeof e.offsetX !== 'undefined' ? e.offsetX : e.pageX - $(e.target).offset().left; y = typeof e.offsetY !== 'undefined' ? e.offsetY : e.pageY - $(e.target).offset().top; } // Touch Event일 경우 else{ // ThuchEnd인 경우 좌표가 없으므로 공백을 반환한다. if(!e.originalEvent.targetTouches[0]){ return {org: '', rate: ''} } //console.log(e.type, e.targetTouches[0].pageX, e.targetTouches[0].pageY); var tx = e.originalEvent.targetTouches[0].pageX; var ty = e.originalEvent.targetTouches[0].pageY; var cx = this.$drawCanvas.offset().left; var cy = this.$drawCanvas.offset().top; x = tx - cx; y = ty - cy; } // 비율에 따른 좌표 계산 var rateVal = this.setting.rateVal; var rx = Math.round(x / rateVal, this.setting.pointFixed); var ry = Math.round(y / rateVal, this.setting.pointFixed); //var rx = (x / rateVal).toFixed(this.setting.pointFixed); //var ry = (y / rateVal).toFixed(this.setting.pointFixed); return {org: x+" "+y, rate: rx+" "+ry} }, /** * ","로 구분되어 있는 point 문자열을 배열로 변경한다. * @returns */ getPointSplitList: function(points){ if(!points){ console.error("points is undifine"); return; } var pointsSplit = points.split(","); return pointsSplit; }, /** * " "으로 구분되어 있는 x,y 좌표를 변경하여 Object{x:x, y:y}로 변경한다. * @returns Object{x:x, y:y} */ getPointSplit: function(point){ if(!point){ console.error("point is undifine"); return; } var pointSplit = point.split(" "); if(pointSplit.length != 2){ console.error("point is not length 2. " + pointSplit.length); return; } return {x:Number(pointSplit[0]), y:Number(pointSplit[1])}; }, /* * hex(#000000) 값을 rgba(xxx, xxx, xxx, x) 값으로 변환 */ hexToRgba: function(hex, opacity){ if(!hex){ return null; } hex = hex.replace("#",""); var r = parseInt(hex.substring(0,2), 16); var g = parseInt(hex.substring(2,4), 16); var b = parseInt(hex.substring(4,6), 16); if(!opacity) opacity = 100; return "rgba("+r+","+g+","+b+","+opacity/100+")"; }, /* * rgba(xxx, xxx, xxx, x) 값을 hex(#000000) 값으로 변환 */ rgbaToHex: function(rgba){ if(!rgba){ return {hex:"#000000", opacity: 100}; } rgba = rgba.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+(\.?\d*))[\s+]?/i); var hex, opacity; if(rgba && (rgba.length === 5 || rgba.length === 6)){ hex = "#" + ("0" + parseInt(rgba[1], 10).toString(16)).slice(-2) + ("0" + parseInt(rgba[2], 10).toString(16)).slice(-2) + ("0" + parseInt(rgba[3], 10).toString(16)).slice(-2); opacity = parseFloat(rgba[4]) * 100; } return {hex:hex, opacity: opacity}; }, /** * bg 비율에 맞게 Style을 변경한다. */ getDrawStyle: function(){ var style = $.extend({}, this.toolkit.style); style.orgLineWidth = style.lineWidth; style.lineWidth = style.orgLineWidth * this.setting.rateVal; return style; }, /** * Draw 완료 후 copy, clear의 작업을 수행한다 */ complateDraw: function(data){ var src = data.copyFrom; var target = data.copyTo; if(data.copyToPreClear){ target.context.clearCanvas(); } target.context.save(); // source-over : 새 도형은 기존 내용 위에 그려진다. 기본값 target.context.globalCompositeOperation = 'source-over'; // target canvas에 source canvas를 덥어 그린다. target.context.drawImage(src.canvas, 0, 0); target.context.restore(); if(data.clear){ if(typeof data.clear == 'object'){ $.each(data.clear, function(index, element){ element.context.clearCanvas(); }); } } this.mainCanvasChange(); move_flag = false; }, /** * view, mview의 Image를 비율에 맞게 main에 draw한다. */ mainCanvasChange: function(flag){ var mainCanvas = this.canvasInfo.main.canvas; var mainCtx = this.canvasInfo.main.context; var viewCanvas = this.canvasInfo.view.canvas; var mviewCanvas = this.canvasInfo.mView.canvas; var setting = this.$element.data('pure.pureCanvas').options.setting; if(!flag){ this.$element.data('pure.pureCanvas').historyAdd(); } mainCtx.save(); mainCtx.clearCanvas(); mainCtx.setTransform(setting.rateVal, 0, 0, setting.rateVal, 0, 0); // source-over : 새 도형은 기존 내용 위에 그려진다. 기본값 mainCtx.globalCompositeOperation = 'source-over'; // target canvas에 source canvas를 덥어 그린다. mainCtx.drawImage(viewCanvas, 0, 0); mainCtx.drawImage(mviewCanvas, 0, 0); mainCtx.restore(); }, }, // $.pureCanvas.toolkit.addToolkit addToolkit: function(toolkit){ var makeToolkit = new toolkit(); $.pureCanvas.toolkit.type[makeToolkit.getType()] = makeToolkit; }, // Toolkit Types type: {} }); /*******************************************************************************************************************************/ $.extend($.pureCanvas.toolkit.prototype, { isLastSameCurrentPoint: function(points, currentPoint){ return points && points.length > 0 ? points[points.lenght-1] == currentPoint : false; }, // 굵기, 색에 따라 그려질 정보를 출력한다. drawForPrePoint: function(ctx, drawStyle, drawPoint){ var style = $.extend({}, drawStyle); if(!drawStyle.isNotChange){ style.fillStyle = style.strokeStyle; style.strokeStyle = '#ffffff'; } var point = this.getPointSplit(drawPoint); if(!this.isDrawing) ctx.clearCanvas(); ctx.save(); ctx.beginPath(); ctx.arc(point.x, point.y, style.lineWidth / 2, 0, Math.PI * 2, false); ctx.lineWidth = 1; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; if(style.fillStyle){ ctx.fillStyle = style.fillStyle; ctx.fill(); } if(style.strokeStyle){ ctx.strokeStyle = style.strokeStyle; ctx.stroke(); } ctx.restore(); }, drawForArc: function(ctx, style, drawPoints){ var point0 = this.getPointSplit(drawPoints[0]); var point1 = this.getPointSplit(drawPoints[1]); ctx.clearCanvas(); ctx.beginPath(); // 원의 반지름 구하기 // 중심점에서 마우스 위치가 X, Y에서 더 큰쪽으로 반지름을 구한다???? var radius = point0.x - point1.x; if(radius < 0) radius = radius * -1; var radiusY = point0.y - point1.y; if(radiusY < 0) radiusY = radiusY * -1; if(radius < radiusY) radius = radiusY; ctx.arc(point0.x, point0.y, radius, 2 * Math.PI, false); ctx.lineWidth = style.lineWidth; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; if(style.isStroke){ ctx.strokeStyle = style.strokeStyle; ctx.stroke(); } if(style.isFill){ ctx.fillStyle = style.fillStyle; ctx.fill(); } }, drawForLine: function(ctx, style, drawPoints){ // drawForStraightLine if(drawPoints.length <= 2){ ctx.beginPath(); if(!style.isNotClear){ ctx.clearCanvas(); } var point0 = this.getPointSplit(drawPoints[0]); var point1; if(drawPoints.length == 2){ point1 = this.getPointSplit(drawPoints[1]); }else{ point1 = {x: point0.x+0.5, y: point0.y+0.5}; } ctx.moveTo(point0.x, point0.y); ctx.lineTo(point1.x, point1.y); ctx.lineWidth = style.lineWidth; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; ctx.strokeStyle = style.strokeStyle; ctx.stroke(); return; } if(!style.isNotClear){ ctx.clearCanvas(); } ctx.beginPath(); ctx.lineWidth = style.lineWidth; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; ctx.strokeStyle = style.strokeStyle; var point0 = this.getPointSplit(drawPoints[0]); var pointi, pointi1; ctx.moveTo(point0.x, point0.y); for(var i=1; i data.timeStamp)){ points = null; delete THIS.pointerMap[key]; } // pointer의 위치가 canvas를 떠났을 경우 points가 null로 전송된다. if(!points){ ctx.clearCanvas(); return; } var drawPoints = THIS.getPointSplitList(points); var point = THIS.getPointSplit(drawPoints[0]); if(key != 'me'){ point.x *= THIS.setting.rateVal; point.y *= THIS.setting.rateVal; } ctx.save(); ctx.beginPath(); ctx.arc(point.x, point.y, 50, 2 * Math.PI, false); // create radial gradient var grd; // down전송 시 mousedown event가 발생하지 않은 경우, mousepointer를 작게 표시 if(key == 'me' && THIS.setting.pointerDownSend && !THIS.isDrawing){ grd = ctx.createRadialGradient(point.x, point.y, 0, point.x, point.y, 13); } // mousepointer 일반적인 크기 else{ grd = ctx.createRadialGradient(point.x, point.y, 5, point.x, point.y, 15); } grd.addColorStop(0, style.strokeStyle); grd.addColorStop(1, "rgba(255,255,255,0)"); ctx.fillStyle = grd; ctx.fill(); ctx.closePath(); ctx.restore(); }); }, draw: function(e){ var toolkitData = e.toolkitData; // 좌표정보가 있는 경우 마우스포인트 정보를 설정한다. if(toolkitData.points){ this.pointerMap[toolkitData.id] = {style: toolkitData.style, points: toolkitData.points, timeStamp: e.timeStamp}; } // 좌표정보가 없는 경우 마우스포인트 정보를 삭제한다. else{ delete this.pointerMap[toolkitData.id]; } //this.drawForMousePointer(); this.startDraw(); }, } $.pureCanvas.toolkit.addToolkit(MousePointer); /************************************************************************************/ /** add native function **/ // Canvas Context - Clear Canvas CanvasRenderingContext2D.prototype.clearCanvas = function(){ this.clearRect(0, 0, this.canvas.width, this.canvas.height); }; /************************************************************************************/ // //去除多余的canvas $("canvas:eq(1)").remove(); $("canvas:eq(1)").remove(); }(jQuery);