반응형
<template>
<div id="canvasCon" ref="imageViewRef">
<canvas id='zoomCanvas' :width=userSettingCanvasWidth :height=userSettingCanvasHeight @mousedown='handleMouseDown'
@mousemove='handleMouseMove' @mouseup='handleMouseUp' @wheel='handleWheel'
style="border: 1px solid grey; border-radius: 4px;"></canvas>
</div>
</template>
<script setup>
import { fabric } from 'fabric';
import { reactive, onMounted, ref, watch } from 'vue';
import { useRouter } from 'vue-router';
const router = useRouter();
let canvas = null;
let context = null;
const img = new Image();
// viewerInfo
const userSettingCanvasWidth = ref(null);
const userSettingCanvasHeight = ref(null);
// viewerInfo END
let widthRatio = null;
let heightRatio = null;
let x = 0;
let y = 0;
let newScale = null;
const scale = ref(1);
const MIN_SCALE = ref(0.1);
const MAX_SCALE = ref(100);
const panning = ref(false);
let viewPos = reactive({ x: 0, y: 0 });
let startPos = reactive({ x: 0, y: 0 });
const props = defineProps({
imageUrl: { type: String, required: true },
viewerInfo: { type: Object, required: true, width: 0, height: 0 }
});
const tempW = ref(0);
const tempH = ref(0);
onMounted(() => {
window.addEventListener('resize', handleResize);
userSettingCanvasWidth.value = props.viewerInfo.width;
userSettingCanvasHeight.value = props.viewerInfo.height;
// 기본 창 사이즈 기준(1920 x 937) 으로 유지할 padding 값 구하기
tempW.value = 1920 - userSettingCanvasWidth.value;
tempH.value = 937 - userSettingCanvasHeight.value;
userSettingCanvasWidth.value = window.innerWidth - tempW.value;
userSettingCanvasHeight.value = window.innerHeight - tempH.value;
}
);
const handleResize = () => {
userSettingCanvasWidth.value = window.innerWidth - tempW.value;
userSettingCanvasHeight.value = window.innerHeight - tempH.value;
img.src = props.imageUrl;
ratioSetting();
};
watch(() => props.imageUrl, () => {
if (context !== null) context.reset();
img.src = props.imageUrl;
});
img.onload = function () {
firstDraw();
};
const firstDraw = () => {
// canvas = new fabric.Canvas('zoomCanvas');
canvas = document.getElementById('zoomCanvas');
console.log(canvas);
startPos.x = 0;
startPos.y = 0;
ratioSetting();
context = canvas.getContext('2d', { colorSpace: 'srgb' });
context.drawImage(img, x, y, widthRatio, heightRatio);
};
const ratioSetting = () => {
const canvasWidth = canvas.width;
const canvasHeight = canvas.height;
const imgWidth = img.width;
const imgHeight = img.height;
if (imgWidth > imgHeight) {
heightRatio = canvasHeight; // 이미지의 세로를 플랫폼으로 맞추고
widthRatio = canvasHeight * (imgWidth / imgHeight); // 세로에따른 비율계산
x = (canvasWidth / 2) - widthRatio / 2;
} else if (imgHeight > imgWidth) {
widthRatio = canvasWidth; // 이미지의 가로를 플랫폼으로 맞추고
heightRatio = canvasWidth * (imgHeight / imgWidth); // 가로에따른 비율계산
y = (canvasHeight / 2) - heightRatio / 2;
} else { // 이미지 가로, 세로 길이 같을때
widthRatio = canvasHeight * (imgWidth / imgHeight); // 이미지의 가로를 플랫폼으로 맞추고
heightRatio = canvasHeight * (imgHeight / imgWidth); // 가로에따른 비율계산
x = (canvasWidth / 2) - widthRatio / 2;
}
};
const draw = () => {
context = canvas.getContext('2d');
context.setTransform(
scale.value,
0,
0,
scale.value,
viewPos.x,
viewPos.y
);
context.drawImage(img, x, y, widthRatio, heightRatio);
};
const handleMouseDown = (e) => { // canvas 선택
const { offsetX, offsetY } = e;
e.preventDefault();
startPos = {
x: offsetX - viewPos.x,
y: offsetY - viewPos.y
};
panning.value = true;
};
const handleMouseUp = (e) => { // canvas 선택 해제
e.preventDefault();
panning.value = false;
};
const handleMouseMove = (e) => { // canvas 이동
const { offsetX, offsetY } = e;
e.preventDefault();
if (context === null) return;
if (!panning.value) return;
viewPos = {
x: offsetX - startPos.x,
y: offsetY - startPos.y
};
context.reset();
draw();
};
const handleWheel = (e) => { // canvas 확대
const { offsetX, offsetY } = e;
e.preventDefault();
if (context === null) return;
const xs = (offsetX - viewPos.x) / scale.value;
const ys = (offsetY - viewPos.y) / scale.value;
const delta = -e.deltaY; // 사용자 마우스 휠 조작 이벤트 캐치
newScale = delta > 0 ? scale.value * 1.2 : scale.value / 1.2; // 양수일시 1.2배 확대 음수일시 1.2배 축소
if (newScale.toFixed(1) < 1) { // 이미지 원본사이즈 이상으로는 축소 못하게
return;
}
if (newScale >= MIN_SCALE.value && newScale <= MAX_SCALE.value) {
scale.value = newScale;
viewPos = {
x: offsetX - xs * scale.value,
y: offsetY - ys * scale.value
};
context.reset();
draw();
}
};
</script>
<style>
#zoomCanvas {
z-index: 1 !important;
cursor: grab;
}
</style>
반응형
'실무 > vue.js 실무 기능' 카테고리의 다른 글
15. 비지니스 로직을 class로 만들고, 그 클래스를 Vue 컴포넌트에서 사용 (0) | 2024.01.14 |
---|---|
14. Axios 모듈화 | API 모듈화 (function -> 람다) (0) | 2024.01.14 |
12. email validation 체크 만들기 (0) | 2023.01.18 |
11. vue로 Modal창, Modal Drag 구현 | v-for, v-if (0) | 2023.01.15 |
10. vue.js 클래스에 숫자 넣고 싶을때 :clsss (0) | 2023.01.08 |