qcast-front/src/hooks/floorPlan/useImgLoader.js

124 lines
4.5 KiB
JavaScript

import { useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom'
import { useAxios } from '../useAxios'
import { usePlan } from '../usePlan'
import { POLYGON_TYPE } from '@/common/common'
import { QcastContext } from '@/app/QcastProvider'
import { useContext } from 'react'
import Config from '@/config/config.export'
/**
* 이미지 로더 hook
* 캔버스를 바이너리로 변환하고 이미지 객체에 붙여서 다시 이미지를 바이너리로 전달
* 캔버스 데이터가 바이너리로 변경되면 용량이 너무 커서 post전송에 실패할 수 있음
* @returns {function} handleCanvasToPng
*/
export function useImgLoader() {
const canvas = useRecoilValue(canvasState)
const { currentCanvasPlan } = usePlan()
const { post } = useAxios()
const { setIsGlobalLoading } = useContext(QcastContext)
/**
* 이미지 저장 시 왼쪽 위, 오른쪽 아래 좌표
* return [start, end]
*/
const getImageCoordinates = () => {
const margin = 20
const objects = canvas.getObjects().filter((obj) => [POLYGON_TYPE.ROOF, 'lengthText', 'arrow'].includes(obj.name))
const minX = objects.reduce((acc, cur) => (cur.left < acc ? cur.left : acc), objects[0].left)
const minY = objects.reduce((acc, cur) => (cur.top < acc ? cur.top : acc), objects[0].top)
const maxX = objects.reduce((acc, cur) => (cur.left + cur.width > acc ? cur.left : acc), 0)
const maxY = objects.reduce((acc, cur) => (cur.top + cur.height > acc ? cur.top : acc), 0)
return [
{ x: minX - margin, y: minY - margin },
{ x: maxX + margin, y: maxY + margin },
]
}
/**
* 캔버스를 이미지로 저장
* @param {integer} type 1: 모듈만 있는 상태, 2: 가대까지 올린 상태
*/
const handleCanvasToPng = async (type) => {
try {
toggleLineEtc(false)
canvas.getObjects('image').forEach((obj) => {
if (obj.getSrc) {
const img = new Image()
img.crossOrigin = 'anonymous'
img.src = obj.getSrc()
obj.setElement(img)
}
})
canvas.renderAll()
const formData = new FormData()
// 고해상도 캡처를 위해 multiplier 옵션 추가 (2배 해상도)
const multiplier = 2
const dataUrl = canvas.toDataURL({
format: 'png',
multiplier: multiplier,
})
const blobBin = atob(dataUrl.split(',')[1])
const array = []
for (let i = 0; i < blobBin.length; i++) {
array.push(blobBin.charCodeAt(i))
}
const file = new Blob([new Uint8Array(array)], { type: 'image/png' })
formData.append('file', file, 'canvas.png')
formData.append('objectNo', currentCanvasPlan.objectNo)
formData.append('planNo', currentCanvasPlan.planNo)
formData.append('type', type)
/** 이미지 크롭 좌표 계산 (multiplier 배율 적용) */
const positionObj = getImageCoordinates()
console.log('🚀 ~ handleCanvasToPng ~ positionObj:', positionObj)
formData.append('width', Math.round((positionObj[1].x - positionObj[0].x + 100) * multiplier))
formData.append('height', Math.round((positionObj[1].y - positionObj[0].y + 100) * multiplier))
formData.append('left', Math.round(positionObj[0].x * multiplier))
formData.append('top', Math.round(positionObj[0].y * multiplier))
console.log('🚀 ~ handleCanvasToPng ~ formData:', formData)
/** 이미지 크롭 요청 */
const result = await post({
// url: `${process.env.NEXT_PUBLIC_API_HOST_URL}/image/canvas`,
url: `${Config().baseUrl}/api/image/canvas`,
data: formData,
})
console.log('🚀 ~ handleCanvasToPng ~ result:', result)
toggleLineEtc(true)
return result
} catch (e) {
setIsGlobalLoading(false)
console.log('🚀 ~ handleCanvasToPng ~ e:', e)
}
}
/**
* 마우스 포인터 그리드, 임의그리드, 흡착점 등 제거.
*
*/
const toggleLineEtc = (visible = false) => {
if (canvas?._objects.length > 0) {
const mouseLines = canvas?._objects.filter((obj) => obj.name === 'mouseLine')
mouseLines.forEach((item) => canvas?.remove(item))
}
const adsorptionPoints = canvas?._objects.filter((obj) => obj.name === 'adsorptionPoint')
const gridLines = canvas?._objects.filter((obj) => obj.name === 'lineGrid' || obj.name === 'tempGrid' || obj.name === 'dotGrid')
adsorptionPoints.forEach((item) => item.set({ visible: visible }))
gridLines.forEach((item) => item.set({ visible: visible }))
canvas?.renderAll()
}
return { handleCanvasToPng }
}