- URL 하드코딩 → .env APP_URL 기반 동적 URL로 변경 - DB 연결 하드코딩 → .env 기반으로 변경 - MySQL strict mode DATE 오류 수정
274 lines
11 KiB
JavaScript
274 lines
11 KiB
JavaScript
// drawingTool.js
|
|
;(function($){
|
|
var DrawingTool = {
|
|
$canvas: null,
|
|
ctx: null,
|
|
drawMode: 'polyline',
|
|
drawColor: '#000000',
|
|
eraserWidth: 10,
|
|
isRightAngle: true,
|
|
polylinePoints: [],
|
|
isDrawing: false,
|
|
lastX: 0,
|
|
lastY: 0,
|
|
originalImage: null,
|
|
|
|
init: function(){
|
|
this.bindDrawBtn();
|
|
},
|
|
|
|
bindDrawBtn: function(){
|
|
var self = this;
|
|
$('#drawBtn')
|
|
.off('click.drawingTool')
|
|
.on('click.drawingTool', function(){
|
|
var active = !$(this).hasClass('active');
|
|
$(this).toggleClass('active', active).text(active ? '그리기 중지' : '그리기');
|
|
if(active) self.createUI();
|
|
else self.destroyUI();
|
|
});
|
|
},
|
|
|
|
createUI: function(){
|
|
var self = this;
|
|
$('.drawing-tools-container').remove();
|
|
|
|
var html =
|
|
'<div class="drawing-tools-container mb-3">'+
|
|
' <div class="card">'+
|
|
' <div class="card-header"><h6 class="mb-0">그리기 도구</h6></div>'+
|
|
' <div class="card-body row">'+
|
|
' <div class="col-md-8">'+
|
|
' <canvas id="drawingCanvas" style="border:1px solid #ccc; cursor:crosshair; width:100%;"></canvas>'+
|
|
' </div>'+
|
|
' <div class="col-md-4">'+
|
|
' <div class="mb-3">'+
|
|
' <label>모드:</label>'+
|
|
' <select id="drawMode" class="form-select">'+
|
|
' <option value="polyline">폴리라인</option>'+
|
|
' <option value="free">자유선</option>'+
|
|
' <option value="eraser">지우개</option>'+
|
|
' <option value="text">텍스트</option>'+
|
|
' </select>'+
|
|
' </div>'+
|
|
' <div class="form-check mb-3">'+
|
|
' <input id="rightAngleCheck" class="form-check-input" type="checkbox" checked>'+
|
|
' <label class="form-check-label" for="rightAngleCheck">직각 그리기</label>'+
|
|
' </div>'+
|
|
' <div class="mb-3">'+
|
|
' <label>색상:</label>'+
|
|
' <input id="drawColor" class="form-control" type="color" value="'+this.drawColor+'">'+
|
|
' </div>'+
|
|
' <div class="mb-3 eraser-control">'+
|
|
' <label>지우개 굵기:</label>'+
|
|
' <input id="eraserWidth" class="form-control-range" type="range" min="2" max="50" value="'+this.eraserWidth+'">'+
|
|
' <small id="eraserWidthLabel" class="text-muted">'+this.eraserWidth+'px</small>'+
|
|
' </div>'+
|
|
' <div class="mb-3">'+
|
|
' <button type="button" id="clearBtn" class="btn btn-outline-secondary btn-sm">초기화</button>'+
|
|
' </div>'+
|
|
' </div>'+
|
|
' </div>'+
|
|
' </div>'+
|
|
'</div>';
|
|
|
|
var $target = $('#materialSummaryArea').length ? $('#materialSummaryArea') : $('#myModal .modal-body');
|
|
$target.prepend(html);
|
|
|
|
this.$canvas = $('#drawingCanvas').attr({width:600, height:400});
|
|
this.ctx = this.$canvas[0].getContext('2d');
|
|
|
|
this.loadImage();
|
|
this.bindControls();
|
|
this.bindCanvas();
|
|
},
|
|
|
|
destroyUI: function(){
|
|
$('.drawing-tools-container').remove();
|
|
this.polylinePoints = [];
|
|
this.isDrawing = false;
|
|
this.drawMode = 'polyline';
|
|
this.eraserWidth = 10;
|
|
this.drawColor = '#000000';
|
|
$(document).off('.drawingTool');
|
|
},
|
|
|
|
loadImage: function(){
|
|
var src = $('#previewContainer img').attr('src');
|
|
if(src){
|
|
var img = new Image(), self=this;
|
|
img.crossOrigin='anonymous';
|
|
img.onload = function(){
|
|
self.$canvas.attr({width:img.width,height:img.height});
|
|
self.ctx.drawImage(img,0,0);
|
|
self.originalImage = img;
|
|
};
|
|
img.onerror = function(){ self.clearCanvas(); };
|
|
img.src = src;
|
|
} else this.clearCanvas();
|
|
},
|
|
|
|
clearCanvas: function(){
|
|
this.clearCanvasBackground();
|
|
if(this.originalImage) this.ctx.drawImage(this.originalImage,0,0);
|
|
this.polylinePoints=[];
|
|
},
|
|
|
|
bindControls: function(){
|
|
var self=this;
|
|
var $cont=$('.drawing-tools-container');
|
|
|
|
$cont.find('#drawMode').off('change.drawingTool').on('change.drawingTool',function(){
|
|
self.drawMode = this.value;
|
|
self.polylinePoints = [];
|
|
self.clearCanvas();
|
|
$cont.find('.eraser-control').toggle(self.drawMode==='eraser');
|
|
});
|
|
|
|
$cont.find('#rightAngleCheck').off('change.drawingTool').on('change.drawingTool',function(){
|
|
self.isRightAngle = this.checked;
|
|
});
|
|
|
|
$cont.find('#drawColor').off('change.drawingTool').on('change.drawingTool',function(){
|
|
self.drawColor = this.value;
|
|
});
|
|
|
|
$cont.find('#eraserWidth').off('input.drawingTool').on('input.drawingTool',function(){
|
|
var v = parseInt(this.value,10);
|
|
if(!isNaN(v)){
|
|
self.eraserWidth = v;
|
|
$cont.find('#eraserWidthLabel').text(v+'px');
|
|
}
|
|
});
|
|
|
|
$cont.find('#clearBtn').off('click.drawingTool').on('click.drawingTool',function(){
|
|
self.clearCanvas();
|
|
});
|
|
|
|
$(document).off('keydown.drawingTool').on('keydown.drawingTool',function(e){
|
|
if(e.key==='Escape' && self.drawMode==='polyline' && self.polylinePoints.length){
|
|
self.polylinePoints=[];
|
|
self.clearCanvas();
|
|
}
|
|
});
|
|
},
|
|
|
|
bindCanvas: function(){
|
|
var self=this, $c=this.$canvas;
|
|
$c.off('.drawingTool')
|
|
.on('mousedown.drawingTool', function(e){
|
|
var off=$c.offset(), x=e.pageX-off.left, y=e.pageY-off.top;
|
|
if(self.drawMode==='free'||self.drawMode==='eraser'){
|
|
self.isDrawing=true; self.lastX=x; self.lastY=y;
|
|
}
|
|
})
|
|
.on('mousemove.drawingTool', function(e){
|
|
var off=$c.offset(), x=e.pageX-off.left, y=e.pageY-off.top;
|
|
|
|
if(self.drawMode==='polyline' && self.polylinePoints.length){
|
|
self.clearCanvas(); if(self.originalImage) self.ctx.drawImage(self.originalImage,0,0);
|
|
self.drawPolylines();
|
|
var prev=self.polylinePoints.slice(-1)[0];
|
|
if(prev){
|
|
var dx=Math.abs(x-prev.x), dy=Math.abs(y-prev.y);
|
|
var endX=self.isRightAngle?(dx>dy?x:prev.x):x;
|
|
var endY=self.isRightAngle?(dx>dy?prev.y:y):y;
|
|
self.ctx.setLineDash([5,5]);
|
|
self.ctx.strokeStyle='#888'; self.ctx.lineWidth=1;
|
|
self.ctx.beginPath(); self.ctx.moveTo(prev.x,prev.y); self.ctx.lineTo(endX,endY); self.ctx.stroke();
|
|
self.ctx.setLineDash([]);
|
|
}
|
|
}
|
|
|
|
if(self.isDrawing){
|
|
self.ctx.globalCompositeOperation=self.drawMode==='eraser'?'destination-out':'source-over';
|
|
self.ctx.strokeStyle=self.drawMode==='eraser'?null:self.drawColor;
|
|
self.ctx.lineWidth=self.drawMode==='eraser'?self.eraserWidth:2;
|
|
self.ctx.lineCap='round';
|
|
self.ctx.beginPath(); self.ctx.moveTo(self.lastX,self.lastY); self.ctx.lineTo(x,y); self.ctx.stroke();
|
|
self.lastX=x; self.lastY=y;
|
|
}
|
|
})
|
|
.on('mouseup.drawingTool mouseleave.drawingTool', function(e){
|
|
if(self.drawMode==='polyline'){
|
|
var off=$c.offset(), x=e.pageX-off.left, y=e.pageY-off.top;
|
|
if(!self.isDrawing){
|
|
if(self.polylinePoints.length===0) self.polylinePoints.push({x:x,y:y});
|
|
else{
|
|
var prev=self.polylinePoints.slice(-1)[0];
|
|
if(prev){
|
|
var dx=Math.abs(x-prev.x), dy=Math.abs(y-prev.y);
|
|
var endX=self.isRightAngle?(dx>dy?x:prev.x):x;
|
|
var endY=self.isRightAngle?(dx>dy?prev.y:y):y;
|
|
self.polylinePoints.push({x:endX,y:endY});
|
|
self.clearCanvas(); if(self.originalImage) self.ctx.drawImage(self.originalImage,0,0);
|
|
self.drawPolylines();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
self.isDrawing=false;
|
|
})
|
|
.on('click.drawingTool', function(e){
|
|
if(self.drawMode==='text'){
|
|
var off=$c.offset(), x=e.pageX-off.left, y=e.pageY-off.top;
|
|
var $input=$('<input type="text" maxlength="100">').css({position:'absolute',left:off.left+x+'px',top:off.top+y+'px',fontSize:'14px',zIndex:9999}).appendTo('body').focus();
|
|
$input.on('keydown',function(evt){if(evt.key==='Enter'){var t=$input.val();if(t){self.ctx.fillStyle=self.drawColor;self.ctx.font='14px Arial';self.ctx.fillText(t,x,y+14);} $input.remove();}else if(evt.key==='Escape')$input.remove();}).on('blur',function(){$input.remove();});
|
|
}
|
|
});
|
|
},
|
|
|
|
drawPolylines: function(){
|
|
if(!this.polylinePoints.length) return;
|
|
this.ctx.setLineDash([]);
|
|
this.ctx.strokeStyle=this.drawColor;
|
|
this.ctx.lineWidth=2;
|
|
this.ctx.beginPath();
|
|
this.ctx.moveTo(this.polylinePoints[0].x,this.polylinePoints[0].y);
|
|
for(var i=1;i<this.polylinePoints.length;i++){
|
|
this.ctx.lineTo(this.polylinePoints[i].x,this.polylinePoints[i].y);
|
|
}
|
|
this.ctx.stroke();
|
|
},
|
|
|
|
|
|
redrawCanvas: function(){
|
|
this.clearCanvasBackground();
|
|
if (this.originalImage) this.ctx.drawImage(this.originalImage,0,0);
|
|
},
|
|
|
|
clearCanvasBackground: function(){
|
|
if (this.ctx && this.$canvas) {
|
|
this.ctx.clearRect(0, 0, this.$canvas[0].width, this.$canvas[0].height);
|
|
}
|
|
},
|
|
|
|
cleanup: function(){
|
|
$('.drawing-tools-container').remove();
|
|
this.polylinePoints=[];
|
|
this.isDrawing=false;
|
|
this.drawMode='polyline';
|
|
this.lineWidth=2;
|
|
this.drawColor='#000000';
|
|
this.isRightAngle=true;
|
|
this.originalImage=null;
|
|
}
|
|
};
|
|
|
|
// 외부 참조용 글로벌 변수로 노출
|
|
window.DrawingTool = DrawingTool;
|
|
|
|
// 문서 로드 시와, 부트스트랩 모달이 뜰 때마다 init 호출
|
|
$(function(){
|
|
console.log('DOM ready → DrawingTool.init()');
|
|
DrawingTool.init();
|
|
|
|
// 만약 Bootstrap modal 을 쓰신다면
|
|
$('#myModal').on('shown.bs.modal', function(){
|
|
console.log('modal opened → DrawingTool.init()');
|
|
DrawingTool.init();
|
|
});
|
|
});
|
|
|
|
})(jQuery);
|
|
|