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() const dataUrl = canvas.toDataURL('image/png') 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) /** 이미지 크롭 좌표 계산 */ const positionObj = getImageCoordinates() console.log('🚀 ~ handleCanvasToPng ~ positionObj:', positionObj) formData.append('width', Math.round(positionObj[1].x - positionObj[0].x + 100)) formData.append('height', Math.round(positionObj[1].y - positionObj[0].y + 100)) formData.append('left', Math.round(positionObj[0].x)) formData.append('top', Math.round(positionObj[0].y)) 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 } }