<template>
<div ref="crtContainerRef" class="crt-container">
<div
ref="crtContainerImgContainerRef"
class="crt-container__img-container"
:style="{
left: `${positionInfo.x}px`,
top: `${positionInfo.y}px`,
}"
@mousedown="handleMousedown"
@mousemove="handleMousemove"
@mouseup="handleMoveStop"
@mouseleave="handleMoveStop"
@mousewheel="handleMousewhell"
>
<img
class="crt-container__img"
:style="{ zoom: String(positionInfo.zoom) }"
:src="companyImg"
alt=""
@load="handleLoad"
/>
<img
v-for="(item, index) of deviceList"
:key="index"
:id="item.name"
:style="{ left: `${item.x}%`, top: `${item.y}%` }"
class="crt-container__icon"
:src="item.image"
alt=""
@click="emit('handleIconClick', item)"
@mousewheel.stop="() => {}"
/>
</div>
</div>
</template>
<script setup>
import { ref, defineProps, defineEmits, defineExpose } from "vue";
defineProps({
deviceList: {
type: Array,
default: () => [],
},
companyImg: {
type: String,
default: "",
},
});
const emit = defineEmits(["handleIconClick"]);
const positionInfo = ref({
move: false,
x: 0,
y: 0,
w: 0,
h: 0,
zoom: 1,
clientX: 0,
clientY: 0,
});
const crtContainerImgContainerRef = ref(null);
const handleLoad = () => {
positionInfo.value.w = crtContainerImgContainerRef.value.offsetWidth;
positionInfo.value.h = crtContainerImgContainerRef.value.offsetHeight;
};
const handleMousewhell = (ev) => {
if (ev.deltaY < 0) {
positionInfo.value.zoom += 0.02;
if (Number(positionInfo.value.zoom.toFixed(2)) > 1) {
positionInfo.value.zoom = 1;
} else {
positionInfo.value.x -=
(ev.offsetX / crtContainerImgContainerRef.value.offsetWidth) *
(positionInfo.value.w * 0.02);
positionInfo.value.y -=
(ev.offsetY / crtContainerImgContainerRef.value.offsetHeight) *
(positionInfo.value.h * 0.02);
}
} else {
positionInfo.value.zoom -= 0.02;
if (Number(positionInfo.value.zoom.toFixed(2)) < 0.8) {
positionInfo.value.zoom = 0.8;
} else {
positionInfo.value.x +=
(ev.offsetX / crtContainerImgContainerRef.value.offsetWidth) *
(positionInfo.value.w * 0.02);
positionInfo.value.y +=
(ev.offsetY / crtContainerImgContainerRef.value.offsetHeight) *
(positionInfo.value.h * 0.02);
}
}
};
const handleMousedown = (ev) => {
ev.preventDefault();
positionInfo.value.move = true;
positionInfo.value.x = crtContainerImgContainerRef.value.offsetLeft;
positionInfo.value.y = crtContainerImgContainerRef.value.offsetTop;
positionInfo.value.clientX = ev.clientX;
positionInfo.value.clientY = ev.clientY;
};
const handleMousemove = (ev) => {
if (positionInfo.value.move) {
positionInfo.value.x += ev.clientX - positionInfo.value.clientX;
positionInfo.value.y += ev.clientY - positionInfo.value.clientY;
positionInfo.value.clientX = ev.clientX;
positionInfo.value.clientY = ev.clientY;
}
};
const handleMoveStop = () => {
positionInfo.value.move = false;
};
const crtContainerRef = ref(null);
const handleIconCenter = (name) => {
const el = document.getElementById(name);
positionInfo.value.x = -el.offsetLeft + crtContainerRef.value.offsetWidth / 2;
positionInfo.value.y = -el.offsetTop + crtContainerRef.value.offsetHeight / 2;
};
defineExpose({
handleIconCenter,
});
</script>
<style scoped lang="scss">
.crt-container {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
}
.crt-container__img-container {
cursor: grab;
position: absolute;
width: fit-content;
}
.crt-container__img-container:active {
cursor: grabbing;
}
.crt-container__img {
display: block;
}
.crt-container__icon {
position: absolute;
width: 40px;
height: 40px;
transform: translate(-50%, -50%);
cursor: pointer;
}
</style>