이직 한 회사에서 Ai 코어 측에서 만들어주는 이미지 폴더안에
이런식으로 폴더가 생성되어있는곳에서 이미지를 조합해서 화면에 표시해줘야하는 작업이 생겼다.
json으로는 좌표를 표시해서 화면에서 해당 좌표에 사각형으로 표시를 해주는 작업이 추가 되었다.
이제 로직을 구현 해볼것이다.
마운티드에 함수 실행 할 부분을 추가 해준다.
const fetchTilesInfo = async (folderPath: string) => {
const url = `${apiBaseUrl}/folders?folderPath=${folderPath}`;
const response = await fetch(url);
if (!response.ok) {
tileExist.value = false;
throw new Error('Network response was not ok');
} else {
const fileNames = await response.json();
const tilesInfo = [];
fileNameResultArr.value = [];
for (const fileName of fileNames) {
if (fileName.endsWith('_files')) {
const fileNameResult = extractSubStringBeforeFiles(fileName);
fileNameResultArr.value.push(fileNameResult)
const {width, height} = await dziWidthHeight(fileNameResult)
tilesInfo.push({
Image: {
xmlns: "http://schemas.microsoft.com/deepzoom/2009",
Url: `${apiBaseUrl}/folders?folderPath=${folderPath}/${fileName}/`,
Format: "jpg",
Overlap: "1",
TileSize: "1024",
Size: {
Width: width,
Height: height
}
}
});
canvasCurrentWitdh.value = width;
canvasCurrentHeight.value = height;
}
}
tileExist.value = true;
return tilesInfo;
}
};
일단.. 웹 회사에서 근무할 때 선언형(함수형 프로그래밍)을 많이 써왔지만 사이드 이펙트가 발생 할 여지가 있지만 파일 폴더 측에 접근해서 좀 더 직관적인 명령형으로 어떤식으로 흘러가는지 파악하기 위해서 명령형으로 작성해준다.
먼저 백엔드 측에서 이미지를 CDN 방식으로 만들어둔 Api 를 통해서 응답을 받아온다.
응답 값
제대로 담기는 모습을 확인 후
viewer.value = OpenSeadragon({
id: "tiling-viewer_img_list",
animationTime: 0.4,
navigatorSizeRatio: 0.25,
showNavigator: true,
sequenceMode: true,
defaultZoomLevel: 1,
prefixUrl: `${apiBaseUrl}/folders?folderPath=D:/UIMD_Data/Res/uimdFe/images/`,
tileSources: tilesInfo,
showReferenceStrip: false,
gestureSettingsMouse: {clickToZoom: false},
maxZoomLevel: 15,
minZoomLevel: 1, // 최소 확대 레벨 설정
zoomPerClick: 1.2, // 클릭 확대 비율 설정
zoomPerScroll: 1.2, // 스크롤 확대 비율 설정
viewportMargins: {top: 0, left: 0, bottom: 0, right: 0}, // 뷰포트 여백 설정
visibilityRatio: 1.0 // 이미지를 뷰포트에 맞추기 위한 비율 설정
});
위에처럼 작성 후 담아주고
// 마그니파이어 설정 - 동그라미 줌기능
new OpenSeadragon.MouseTracker({
element: viewer.value.element,
moveHandler: function (event) {
const existingMagCanvas = document.getElementById('magCanvas');
if (existingMagCanvas) {
viewer.value.element.removeChild(existingMagCanvas);
}
if (!isMagnifyingGlass.value) {
return;
}
const {canvas} = viewer.value.drawer;
const magCanvas = document.createElement('canvas');
const magCtx = magCanvas.getContext('2d');
canvasOverlay.value = magCanvas;
if (magCtx) {
const magWidth = 200;
const magHeight = 200;
const zoomLevel = 5;
magCanvas.id = 'magCanvas';
magCanvas.width = magWidth;
magCanvas.height = magHeight;
magCanvas.style.position = 'absolute';
magCanvas.style.left = `${event.position.x - magWidth / 2}px`;
magCanvas.style.top = `${event.position.y - magHeight / 2}px`;
magCanvas.style.border = '1px solid';
magCanvas.style.borderRadius = '50%';
magCanvas.style.width = `${magWidth}px`;
magCanvas.style.height = `${magHeight}px`;
magCanvas.style.zIndex = '0';
viewer.value.element.appendChild(magCanvas);
// 줌을 위한 확대된 부분을 정확히 잘라내기 위해 drawImage 메서드 수정
magCtx.drawImage(
canvas,
event.position.x - (magWidth / 2 / zoomLevel),
event.position.y - (magHeight / 2 / zoomLevel),
magWidth / zoomLevel,
magHeight / zoomLevel,
0,
0,
magWidth,
magHeight
);
magCanvas.style.visibility = event.position.y <= 0 || event.position.x <= 0 ? 'hidden' : 'visible';
}
},
});
viewer.value.addHandler('open', function (event: any) {
// 캔버스 크기를 조정
canvas.width = event.source.Image.Size.Width;
canvas.height = event.source.Image.Size.Height;
});
viewer.value.addHandler('page', function (event: any) {
const notCanvasClick = fileNameResultArr.value[event.page] !== 'RBC_Image_0';
emits('notCanvasClick', notCanvasClick);
// 페이지가 변경될 때 오버레이를 다시 추가
if (canvas.parentElement !== viewer.value.container) {
viewer.value.addOverlay({
element: canvas,
location: new OpenSeadragon.Rect(0, 0, 1, 1),
});
}
emits('unChecked');
});
viewer.value.addHandler('zoom', () => {
if (activeRuler.value === 'None') {
return;
}
drawRuler(activeRuler.value);
});
viewer.value.addHandler('canvas-click', async (event: any) => {
if (!event.originalEvent.ctrlKey) {
await removeDiv();
}
const clickPos = viewer.value.viewport.pointFromPixel(event.position);
const canvasPos = {
x: clickPos.x * viewer.value.source.width,
y: clickPos.y * viewer.value.source.height
};
// 함수: 클릭 위치가 아이템 위치와 겹치는지 확인
const isItemSelected = (item: any) => {
const width = item.width;
const height = item.height;
return (
canvasPos.x >= Number(item.posX) && canvasPos.x <= (Number(item.posX) + width) &&
canvasPos.y >= Number(item.posY) && canvasPos.y <= (Number(item.posY) + height)
);
};
// 선택된 아이템의 classNm 저장
let selectItm = '';
for (const item of drawPath.value) {
if (item.classNm !== "Normal" && isItemSelected(item)) {
selectItm = item.classNm;
break; // 하나의 아이템만 선택됨
}
}
아래 코드는 생략
줌 기능, OPEN , 페이지넘기기, 캔버스 클릭 시 사각형 생성 등등.. 필요한 부분들을 넣어준다.
혈액 RBC에는 클래스 리스트에 맞게 체크를 넣어줘야 하므로 watch 로 현재 클릭 되어있는 체크배열을 받아서 그려주는 로직을 추가 해준다. any 가 많은건.. 혼자서 전체 로직을 짜고 데모 버전으로 ㅠ 3개월만에 완성을 시켜야해서 다급하게 하는라 타입을 지정해주지 못했다 ㅠㅠㅠㅠㅠㅠ 따로 추후에 리팩토링 과정을 통해서 전체 수정할 예정이다..!
watch(() => props.classInfoArr, (newData) => {
newItemClassInfoArr.value = newData;
if (newData.length === 0) {
removeDiv();
removeRbcMarker();
return;
}
// 모든 <ol> 요소를 선택하고, data-class-nm 값을 배열로 수집
const olElements = document.querySelectorAll('ol.overlayElement');
// newData 배열에서 존재하는 data-class-nm 값을 수집
const validClassNmSet = new Set(newData.map((el: any) => el.classNm));
olElements.forEach(el => {
const classNm = el.getAttribute('data-class-nm');
// data-class-nm이 newData에 존재하지 않으면 해당 <ol> 요소를 제거
if (!validClassNmSet.has(classNm)) {
console.log('Removing <ol> with data-class-nm:', classNm);
viewer.value.removeOverlay(el);
}
});
// rbcMarker 함수 호출
rbcMarker(newData);
}, {deep: true});
대략적인 모든 기능을 구현이 완성 된 후 화면은 아래와 같다
혼자서...3일안에 완성시킨.. 오픈시드래곤을 활용한 타일화 ! 좀 고생을 많이 했다 오픈시드래곤 공식 문서를 보면서 일일이 다 고치는라 고생한 ... 정말 힘들었다. 추후에 타입추가와 리팩토링을 걸쳐서 수정해야겠다.
'vue > csr,vue' 카테고리의 다른 글
로컬 PC _ AI 코어 장비와 통신 시 리소스 자원 관리와 성능 최적화 (0) | 2024.09.10 |
---|---|
Vue 3 Composition API: 간결하고 효율적인 상태 관리 (0) | 2023.12.08 |
Vue 주요 기능 개념 정리 (0) | 2023.12.06 |
Vue 뒤로가기했을때 재렌더링 하기 에러일지 (0) | 2023.01.13 |
vue를 이용한 파이어베이스를 fcm을 이용한 전화 수신 시 알림 창, 모달 창 띄우기 (2) | 2022.12.27 |