- URL 하드코딩 → .env APP_URL 기반 동적 URL로 변경 - DB 연결 하드코딩 → .env 기반으로 변경 - MySQL strict mode DATE 오류 수정
324 lines
10 KiB
PHP
324 lines
10 KiB
PHP
<?php
|
||
$img = isset($_GET['img']) ? $_GET['img'] : '';
|
||
$rotation = isset($_GET['rotation']) ? intval($_GET['rotation']) : 0;
|
||
|
||
// 보안: tmpimg 경로만 허용
|
||
if (strpos($img, '/tmpimg/') !== 0) {
|
||
die('잘못된 접근');
|
||
}
|
||
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<title>이미지 보기</title>
|
||
<style>
|
||
body, html {
|
||
margin: 0;
|
||
padding: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
overflow: hidden;
|
||
background-color: #000;
|
||
cursor: pointer;
|
||
}
|
||
.image-container {
|
||
width: 100vw;
|
||
height: 100vh;
|
||
position: relative;
|
||
overflow: hidden;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.image-container img {
|
||
max-width: none;
|
||
max-height: none;
|
||
width: auto;
|
||
height: auto;
|
||
display: block;
|
||
transform: rotate(<?php echo $rotation; ?>deg);
|
||
object-fit: contain;
|
||
transition: transform 0.3s ease;
|
||
cursor: move;
|
||
}
|
||
.close-button {
|
||
position: fixed;
|
||
top: 20px;
|
||
right: 20px;
|
||
background: rgba(0, 0, 0, 0.7);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 50%;
|
||
width: 40px;
|
||
height: 40px;
|
||
font-size: 20px;
|
||
cursor: pointer;
|
||
z-index: 1000;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.close-button:hover {
|
||
background: rgba(0, 0, 0, 0.9);
|
||
}
|
||
.zoom-controls {
|
||
position: fixed;
|
||
top: 20px;
|
||
left: 20px;
|
||
display: flex;
|
||
gap: 10px;
|
||
z-index: 1000;
|
||
}
|
||
.zoom-button {
|
||
background: rgba(0, 0, 0, 0.7);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 50%;
|
||
width: 40px;
|
||
height: 40px;
|
||
font-size: 18px;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: background 0.3s ease;
|
||
}
|
||
.zoom-button:hover {
|
||
background: rgba(0, 0, 0, 0.9);
|
||
}
|
||
.zoom-button:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
}
|
||
.reset-button {
|
||
background: rgba(0, 0, 0, 0.7);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 20px;
|
||
padding: 8px 16px;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: background 0.3s ease;
|
||
}
|
||
.reset-button:hover {
|
||
background: rgba(0, 0, 0, 0.9);
|
||
}
|
||
.loading {
|
||
color: white;
|
||
font-size: 18px;
|
||
text-align: center;
|
||
}
|
||
.error {
|
||
color: #ff6b6b;
|
||
font-size: 18px;
|
||
text-align: center;
|
||
padding: 20px;
|
||
}
|
||
.zoom-info {
|
||
position: fixed;
|
||
bottom: 20px;
|
||
left: 20px;
|
||
background: rgba(0, 0, 0, 0.7);
|
||
color: white;
|
||
padding: 8px 12px;
|
||
border-radius: 20px;
|
||
font-size: 14px;
|
||
z-index: 1000;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<button class="close-button" onclick="closeWindow()" title="닫기 (ESC)">×</button>
|
||
|
||
<div class="zoom-controls">
|
||
<button class="zoom-button" onclick="zoomIn()" title="확대 (Ctrl + +)">+</button>
|
||
<button class="zoom-button" onclick="zoomOut()" title="축소 (Ctrl + -)">−</button>
|
||
<button class="reset-button" onclick="resetZoom()" title="원본 크기 (Ctrl + 0)">원본</button>
|
||
</div>
|
||
|
||
<div class="zoom-info" id="zoomInfo">100%</div>
|
||
|
||
<div class="image-container">
|
||
<img src="<?php echo htmlspecialchars($img); ?>" alt="이미지" id="popupImage" style="display: none;">
|
||
<div id="loadingMessage" class="loading">이미지를 불러오는 중...</div>
|
||
<div id="errorMessage" class="error" style="display: none;">이미지를 불러올 수 없습니다.</div>
|
||
</div>
|
||
|
||
<script>
|
||
let currentZoom = 1;
|
||
let isDragging = false;
|
||
let startX, startY, translateX = 0, translateY = 0;
|
||
const MIN_ZOOM = 0.1;
|
||
const MAX_ZOOM = 5;
|
||
const ZOOM_STEP = 0.2;
|
||
|
||
// 창 닫기 함수
|
||
function closeWindow() {
|
||
window.close();
|
||
}
|
||
|
||
// 줌 인
|
||
function zoomIn() {
|
||
if (currentZoom < MAX_ZOOM) {
|
||
currentZoom = Math.min(currentZoom + ZOOM_STEP, MAX_ZOOM);
|
||
updateZoom();
|
||
}
|
||
}
|
||
|
||
// 줌 아웃
|
||
function zoomOut() {
|
||
if (currentZoom > MIN_ZOOM) {
|
||
currentZoom = Math.max(currentZoom - ZOOM_STEP, MIN_ZOOM);
|
||
updateZoom();
|
||
}
|
||
}
|
||
|
||
// 줌 리셋
|
||
function resetZoom() {
|
||
currentZoom = 1;
|
||
translateX = 0;
|
||
translateY = 0;
|
||
updateZoom();
|
||
}
|
||
|
||
// 줌 업데이트
|
||
function updateZoom() {
|
||
const img = document.getElementById('popupImage');
|
||
const zoomInfo = document.getElementById('zoomInfo');
|
||
const zoomInBtn = document.querySelector('.zoom-button:first-child');
|
||
const zoomOutBtn = document.querySelector('.zoom-button:nth-child(2)');
|
||
|
||
// 회전, 확대/축소, 이동을 모두 적용
|
||
img.style.transform = `rotate(<?php echo $rotation; ?>deg) scale(${currentZoom}) translate(${translateX}px, ${translateY}px)`;
|
||
zoomInfo.textContent = Math.round(currentZoom * 100) + '%';
|
||
|
||
// 버튼 활성화/비활성화
|
||
zoomInBtn.disabled = currentZoom >= MAX_ZOOM;
|
||
zoomOutBtn.disabled = currentZoom <= MIN_ZOOM;
|
||
}
|
||
|
||
// 마우스 드래그 시작
|
||
function startDrag(e) {
|
||
if (currentZoom > 1) {
|
||
isDragging = true;
|
||
startX = e.clientX - translateX;
|
||
startY = e.clientY - translateY;
|
||
document.body.style.cursor = 'grabbing';
|
||
e.preventDefault();
|
||
}
|
||
}
|
||
|
||
// 마우스 드래그 중
|
||
function drag(e) {
|
||
if (isDragging) {
|
||
translateX = e.clientX - startX;
|
||
translateY = e.clientY - startY;
|
||
updateZoom();
|
||
e.preventDefault();
|
||
}
|
||
}
|
||
|
||
// 마우스 드래그 종료
|
||
function endDrag() {
|
||
isDragging = false;
|
||
document.body.style.cursor = 'pointer';
|
||
}
|
||
|
||
// 이미지 로딩이 완료되면 실행될 함수
|
||
function onImageLoad() {
|
||
// 로딩 메시지 숨기기
|
||
document.getElementById('loadingMessage').style.display = 'none';
|
||
// 이미지 표시
|
||
document.getElementById('popupImage').style.display = 'block';
|
||
|
||
// 이 창을 연 부모 창(opener)이 있고, 닫히지 않았는지 확인
|
||
if (window.opener && !window.opener.closed) {
|
||
// 부모 창에 hideImageLoadingModal 함수가 있는지 확인하고 호출
|
||
if (typeof window.opener.hideImageLoadingModal === 'function') {
|
||
window.opener.hideImageLoadingModal();
|
||
}
|
||
}
|
||
}
|
||
|
||
// 이미지 로딩 실패 시 실행될 함수
|
||
function onImageError() {
|
||
// 로딩 메시지 숨기기
|
||
document.getElementById('loadingMessage').style.display = 'none';
|
||
// 에러 메시지 표시
|
||
document.getElementById('errorMessage').style.display = 'block';
|
||
|
||
// 부모 창의 로딩 모달도 숨기기
|
||
if (window.opener && !window.opener.closed) {
|
||
if (typeof window.opener.hideImageLoadingModal === 'function') {
|
||
window.opener.hideImageLoadingModal();
|
||
}
|
||
}
|
||
}
|
||
|
||
const imgElement = document.getElementById('popupImage');
|
||
|
||
// 이미지의 load 이벤트에 리스너 추가
|
||
imgElement.addEventListener('load', onImageLoad);
|
||
imgElement.addEventListener('error', onImageError);
|
||
|
||
// 만약 이미지가 이미 캐시되어 있다면 'load' 이벤트가 발생하지 않을 수 있으므로,
|
||
// complete 속성을 확인하여 수동으로 함수를 호출합니다.
|
||
if (imgElement.complete) {
|
||
if (imgElement.naturalWidth > 0) {
|
||
onImageLoad();
|
||
} else {
|
||
onImageError();
|
||
}
|
||
}
|
||
|
||
// 마우스 이벤트 리스너 추가
|
||
imgElement.addEventListener('mousedown', startDrag);
|
||
document.addEventListener('mousemove', drag);
|
||
document.addEventListener('mouseup', endDrag);
|
||
|
||
// 휠 줌 기능 (이미지 위에서만)
|
||
imgElement.addEventListener('wheel', function(e) {
|
||
e.preventDefault();
|
||
if (e.deltaY < 0) {
|
||
zoomIn();
|
||
} else {
|
||
zoomOut();
|
||
}
|
||
});
|
||
|
||
// 키보드 단축키
|
||
document.addEventListener('keydown', function(event) {
|
||
if (event.key === 'Escape') {
|
||
closeWindow();
|
||
} else if (event.ctrlKey || event.metaKey) {
|
||
switch(event.key) {
|
||
case '=':
|
||
case '+':
|
||
event.preventDefault();
|
||
zoomIn();
|
||
break;
|
||
case '-':
|
||
event.preventDefault();
|
||
zoomOut();
|
||
break;
|
||
case '0':
|
||
event.preventDefault();
|
||
resetZoom();
|
||
break;
|
||
}
|
||
}
|
||
});
|
||
|
||
// 배경 클릭으로 창 닫기 (줌이 1일 때만)
|
||
document.addEventListener('click', function(event) {
|
||
if (currentZoom <= 1 && (event.target === document.body || event.target.classList.contains('image-container'))) {
|
||
closeWindow();
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|