- URL 하드코딩 → .env APP_URL 기반 동적 URL로 변경 - DB 연결 하드코딩 → .env 기반으로 변경 - MySQL strict mode DATE 오류 수정
246 lines
8.6 KiB
HTML
246 lines
8.6 KiB
HTML
<?php
|
|
include $_SERVER['DOCUMENT_ROOT'] . '/load_header.php';
|
|
?>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>이미지 에디터 통합 예제</title>
|
|
|
|
<style>
|
|
body { padding: 1rem; }
|
|
dialog {
|
|
width: 800px; height: 600px;
|
|
border: none; border-radius: .5rem;
|
|
padding: 0; overflow: hidden;
|
|
}
|
|
dialog::backdrop { background: rgba(0,0,0,.5); }
|
|
#editorHeader {
|
|
background: #f8f9fa; padding: .5rem 1rem;
|
|
border-bottom: 1px solid #dee2e6;
|
|
display: flex; align-items: center;
|
|
}
|
|
#editorHeader h5 { margin: 0; flex-grow: 1; }
|
|
#editorHeader .btn-close { margin-left: .5rem; }
|
|
#editorToolbar, #editorToolbar2 {
|
|
background: #fff;
|
|
padding: .5rem; gap: .25rem;
|
|
}
|
|
#editorToolbar { display: flex; align-items: center; flex-wrap: wrap; }
|
|
#editorToolbar2 { display: flex; align-items: center; }
|
|
.toolbar-btn { width: 40px; height: 40px; padding: 0; }
|
|
.toolbar-btn i { font-size: 1.2rem; }
|
|
.toolbar-btn.active { background-color: #0d6efd; color: #fff; }
|
|
.color-btn { width: 24px; height: 24px; padding: 0; border: 2px solid #fff; cursor: pointer; }
|
|
.color-btn.active { border-color: #000; }
|
|
#editorBody { position: relative; width: 100%; height: calc(100% - 128px); }
|
|
#editorBody canvas { width: 100%; height: 100%; cursor: crosshair; }
|
|
.canvas-text-input {
|
|
position: absolute;
|
|
border: 1px dashed #666;
|
|
background: transparent;
|
|
resize: none;
|
|
outline: none;
|
|
padding: 2px;
|
|
font-family: sans-serif;
|
|
}
|
|
|
|
/* 이미지 컨테이너 스타일 */
|
|
.image-container {
|
|
position: relative;
|
|
display: inline-block;
|
|
}
|
|
|
|
.paste-overlay {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: rgba(0, 123, 255, 0.8);
|
|
color: white;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 0.375rem;
|
|
font-size: 0.875rem;
|
|
opacity: 0;
|
|
transition: opacity 0.2s;
|
|
}
|
|
|
|
.paste-overlay.show {
|
|
opacity: 1;
|
|
}
|
|
|
|
.paste-overlay i {
|
|
font-size: 2rem;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- 편집 대상 이미지 -->
|
|
<div class="image-container mb-3">
|
|
<img id="targetImage"
|
|
src="/guiderail/images/2025_07_16_15_53_07_drawing.png"
|
|
class="img-thumbnail"
|
|
alt="편집할 이미지"
|
|
style="max-width:400px; cursor: pointer;">
|
|
<div class="paste-overlay" style="display: none;">
|
|
<i class="bi bi-clipboard-plus"></i>
|
|
<span>Ctrl+V로 이미지 붙여넣기</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 에디터 열기 버튼 -->
|
|
<button id="openEditorBtn" class="btn btn-primary mb-3">
|
|
<i class="bi bi-pencil-square"></i> 그리기 시작
|
|
</button>
|
|
|
|
<!-- 다이얼로그(편집기) 마크업: imageEditor.js 호출 시 참조 -->
|
|
<dialog id="editorDialog">
|
|
<div id="editorHeader">
|
|
<h5>이미지 편집기</h5>
|
|
<button id="applyBtn" class="btn btn-success btn-sm">적용하기</button>
|
|
<button type="button" class="btn-close" aria-label="닫기" id="closeEditorBtn"></button>
|
|
</div>
|
|
|
|
<!-- 툴바 -->
|
|
<div id="editorToolbar">
|
|
<button id="polyBtn" class="btn btn-outline-primary toolbar-btn" title="폴리라인"><i class="bi bi-vector-pen"></i></button>
|
|
<button id="freeBtn" class="btn btn-outline-primary toolbar-btn" title="자유선"><i class="bi bi-brush"></i></button>
|
|
<button id="lineBtn" class="btn btn-outline-primary toolbar-btn" title="직선 (L키)"><i class="bi bi-slash-lg"></i></button>
|
|
<button id="textBtn" class="btn btn-outline-primary toolbar-btn" title="문자입력"><i class="bi bi-type"></i></button>
|
|
<button id="eraserBtn" class="btn btn-outline-warning toolbar-btn" title="지우개"><i class="bi bi-eraser-fill"></i></button>
|
|
<button id="selectBtn" class="btn btn-outline-secondary toolbar-btn" title="객체선택"><i class="bi bi-cursor-text"></i></button>
|
|
<div class="form-check form-switch ms-3">
|
|
<input class="form-check-input" type="checkbox" id="rightAngle" checked>
|
|
<label class="form-check-label" for="rightAngle">직각 고정</label>
|
|
</div>
|
|
<button id="clearBtn" class="btn btn-outline-danger ms-auto">전체 지우기</button>
|
|
</div>
|
|
|
|
<!-- 지우개 크기 & 색상 -->
|
|
<div id="editorToolbar2" class="d-flex align-items-center px-3 pb-2">
|
|
<div class="btn-group" role="group" id="colorPicker">
|
|
<button type="button" class="btn color-btn active" data-color="#000000" style="background:#000;"></button>
|
|
<button type="button" class="btn color-btn" data-color="#ff0000" style="background:#f00;"></button>
|
|
<button type="button" class="btn color-btn" data-color="#0000ff" style="background:#00f;"></button>
|
|
<button type="button" class="btn color-btn" data-color="#00aa00" style="background:#0a0;"></button>
|
|
<button type="button" class="btn color-btn" data-color="#ff8800" style="background:#f80;"></button>
|
|
<button type="button" class="btn color-btn" data-color="#800080" style="background:#808;"></button>
|
|
<button type="button" class="btn color-btn" data-color="#888888" style="background:#888;"></button>
|
|
</div>
|
|
<label class="mb-0 me-2">지우개 크기</label>
|
|
<input type="range" class="form-range me-2" id="eraserRange" min="5" max="100" step="1" value="20">
|
|
<span id="eraserSizeLabel" class="fw-bold">20</span>px
|
|
</div>
|
|
|
|
<!-- 캔버스 영역 -->
|
|
<div id="editorBody">
|
|
<canvas id="c" width="800" height="600"></canvas>
|
|
</div>
|
|
</dialog>
|
|
|
|
<!-- 모듈 스크립트 -->
|
|
<script type="module">
|
|
import { openImageEditor } from '/js/imageEditor.js';
|
|
|
|
const openBtn = document.getElementById('openEditorBtn');
|
|
const targetImg = document.getElementById('targetImage');
|
|
const imageContainer = document.querySelector('.image-container');
|
|
const pasteOverlay = document.querySelector('.paste-overlay');
|
|
|
|
// 붙여넣기 이벤트 처리 함수
|
|
function handlePaste(event) {
|
|
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
|
|
|
|
for (let item of items) {
|
|
if (item.type.indexOf('image') !== -1) {
|
|
const blob = item.getAsFile();
|
|
const reader = new FileReader();
|
|
|
|
reader.onload = function(e) {
|
|
targetImg.src = e.target.result;
|
|
console.log('이미지가 붙여넣기되었습니다:', e.target.result);
|
|
|
|
// 붙여넣기 성공 피드백
|
|
showPasteFeedback(true);
|
|
};
|
|
|
|
reader.readAsDataURL(blob);
|
|
event.preventDefault();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 붙여넣기 피드백 표시
|
|
function showPasteFeedback(success = false) {
|
|
if (success) {
|
|
pasteOverlay.innerHTML = '<i class="bi bi-check-circle"></i><span>이미지가 붙여넣기되었습니다!</span>';
|
|
pasteOverlay.style.background = 'rgba(40, 167, 69, 0.8)';
|
|
} else {
|
|
pasteOverlay.innerHTML = '<i class="bi bi-clipboard-plus"></i><span>Ctrl+V로 이미지 붙여넣기</span>';
|
|
pasteOverlay.style.background = 'rgba(0, 123, 255, 0.8)';
|
|
}
|
|
|
|
pasteOverlay.classList.add('show');
|
|
setTimeout(() => {
|
|
pasteOverlay.classList.remove('show');
|
|
}, 2000);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 그리기 관련 코드
|
|
// 붙여넣기 이벤트 리스너 추가
|
|
document.addEventListener('paste', handlePaste);
|
|
|
|
// 이미지 컨테이너 클릭 시 포커스
|
|
imageContainer.addEventListener('click', () => {
|
|
imageContainer.focus();
|
|
});
|
|
|
|
// 이미지 컨테이너에 포커스 가능하도록 설정
|
|
imageContainer.tabIndex = 0;
|
|
imageContainer.title = '클릭 후 Ctrl+V로 이미지 붙여넣기';
|
|
|
|
// 포커스 시 오버레이 표시
|
|
imageContainer.addEventListener('focus', () => {
|
|
showPasteFeedback();
|
|
});
|
|
|
|
// 포커스 해제 시 오버레이 숨김
|
|
imageContainer.addEventListener('blur', () => {
|
|
pasteOverlay.classList.remove('show');
|
|
});
|
|
|
|
openBtn.addEventListener('click', () => {
|
|
const src = targetImg.src;
|
|
openImageEditor(src)
|
|
.then(newUrl => {
|
|
// 편집된 이미지를 화면에 반영
|
|
targetImg.src = newUrl;
|
|
})
|
|
.catch(err => {
|
|
// 사용자가 취소했을 때
|
|
console.log('편집 취소:', err.message);
|
|
});
|
|
});
|
|
|
|
$(document).ready(function(){
|
|
var loader = document.getElementById('loadingOverlay');
|
|
if(loader)
|
|
loader.style.display = 'none';
|
|
});
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|