티스토리 뷰

카테고리 없음

map

이채야채 2023. 12. 18. 17:38
import {
  AirCleanerButtonWrap,
  ArrowIconImg,
  Button,
  ButtonWrap,
  ButtonWrapper,
  Container,
  DeviceIcon,
  DeviceName,
  DeviceOptions,
  Map,
  MapWrapper,
  RefreshButton,
  RefreshIconImg,
  RefrigeratorButtonWrap,
  SpanText,
  SpeechBubbleState,
  SpeecheBubbleTitle,
  SpeechBubble,
  TvButtonWrap,
  MiniMapContainer,
  MiniMapWrapper,
  MiniMap,
  VisibleArea,
  LivingRoom,
  DownArrowIcon,
} from "./styled/viewer";
import mapImg from "../assets/image/map_viewer.png";
import upArrowIcon from "../assets/icon/up_arrow.png";
import downArrowIcon from "../assets/icon/down_arrow_black.png";
import refreshIcon from "../assets/icon/refresh.png";
import tvIcon from "../assets/icon/tv.png";
import airCleanerIcon from "../assets/icon/air_cleaner.png";
import kimchiRefrigeratorIcon from "../assets/icon/kimchi_refrigerator.png";
import { useEffect, useRef, useState } from "react";
import { Link } from "react-router-dom";
import { Img } from "../components/Nav/styled/globalNav";

const Viewer = ({ isMobile }: { isMobile: boolean }) => {
  const scale = useRef(1);
  const clicked = useRef(false);
  const transformOriginX = useRef(0);
  const transformOriginY = useRef(0);
  const xAxis = useRef(0);
  const yAxis = useRef(0);
  const x = useRef(0);
  const y = useRef(0);
  const [widthPercent, setWidthPercent] = useState(100);
  const [heightPercent, setHeightPercent] = useState(100);

  const [animation, setAnimation] = useState(false);
  const [showSpeechBubble, setShowSpeechBubble] = useState(false);

  const containerRef = useRef<HTMLDivElement>(null);
  const mapWrapperRef = useRef<HTMLDivElement>(null);
  const visibleRef = useRef<HTMLDivElement>(null);
  const miniMapRef = useRef<HTMLDivElement>(null);
  const currentMapHeight = useRef(0);

  const linkStyle = {
    textDecoration: "none",
    color: "inherit",
  };

  const initMouseZoomEvent = () => {
    const zoomTarget = mapWrapperRef.current;

    if (zoomTarget === null) return;
    window.addEventListener("mouseup", end);
    zoomTarget.addEventListener("mousedown", start);
    zoomTarget.addEventListener("mousemove", move);
    zoomTarget.addEventListener("mouseup", end);
    zoomTarget.addEventListener("mouseleave", end);
    zoomTarget.addEventListener("wheel", handleMouseWheel);
  };

  const initTouchZoomEvent = () => {
    alert("웨않뒈");
  };

  const handleMouseWheel = (event: WheelEvent) => {
    const zoomTarget = mapWrapperRef.current;
    const containerOut = containerRef.current;

    if (containerOut === null || zoomTarget === null) return;

    const container = containerOut.getBoundingClientRect();
    const Imgin = zoomTarget.getBoundingClientRect();

    transformOriginX.current = event.offsetX;
    transformOriginY.current = event.offsetY;

    const scaleChange = event.deltaY > 0 ? -0.5 : 0.5;

    const newZoom = scale.current + scaleChange;
    const test = Math.min(Math.max(1.0, newZoom), 5.0);

    // 마우스가 바라보는 현재위치를 넣어야할때 => 무관해보임
    // zoomTarget.style.transformOrigin = `${event.offsetX}px ${event.offsetY}px`;

    zoomTarget.style.transform = `scale(${test})`;
    scale.current = test;

    if (currentMapHeight.current * scale.current > container.height) {
      const percentageDifference = Math.floor(
        ((currentMapHeight.current * scale.current - container.height) /
          container.height) *
          100
      );
      setHeightPercent(100 - percentageDifference);
    }

    setWidthPercent(100 / scale.current);

    if (scale.current >= 3) setAnimation(true);
    else setAnimation(false);
    checkTopSize();
    checkLeftSize();
  };

  const start = (event: MouseEvent) => {
    clicked.current = true;

    const zoomTarget = mapWrapperRef.current;
    if (zoomTarget === null) return;

    xAxis.current = (event as MouseEvent).clientX - zoomTarget.offsetLeft;
    yAxis.current = (event as MouseEvent).clientY - zoomTarget.offsetTop;
  };

  const move = (event: MouseEvent) => {
    if (!clicked.current) return;

    const containerOut = containerRef.current;
    const zoomTarget = mapWrapperRef.current;
    const miniMap = miniMapRef.current;
    const visibleArea = visibleRef.current;

    if (
      containerOut === null ||
      zoomTarget === null ||
      miniMap === null ||
      visibleArea === null
    )
      return;

    x.current = (event as MouseEvent).clientX;
    y.current = (event as MouseEvent).clientY;

    const container = containerOut.getBoundingClientRect();
    const Imgin = zoomTarget.getBoundingClientRect();
    const miniMapRect = miniMap.getBoundingClientRect();

    if (container.height >= Imgin.height && container.width >= Imgin.width) {
      return;
    }

    const moveX = x.current - xAxis.current;
    const moveY = y.current - yAxis.current;

    zoomTarget.style.cursor = `grab`;

    if (container.height <= Imgin.height) {
      zoomTarget.style.top = `${moveY}px`;

      const visibleAreaY = (moveY / Imgin.height) * miniMapRect.height;

      visibleArea.style.top = `calc(75% - ${visibleAreaY}px)`;
      checkTopSize();
    }
    if (container.width <= Imgin.width) {
      zoomTarget.style.left = `${moveX}px`;
      const visibleAreaX = (moveX / Imgin.width) * miniMapRect.width;

      visibleArea.style.left = `calc(50% - ${visibleAreaX}px)`;

      checkLeftSize();
    }
  };

  const checkTopSize = () => {
    const containerOut = containerRef.current;
    const zoomTarget = mapWrapperRef.current;
    const visibleArea = visibleRef.current;
    const miniMap = miniMapRef.current;

    if (
      containerOut === null ||
      zoomTarget === null ||
      miniMap === null ||
      visibleArea === null
    )
      return;

    const container = containerOut.getBoundingClientRect();
    const Imgin = zoomTarget.getBoundingClientRect();
    const miniMapRect = miniMap.getBoundingClientRect();

    if (Imgin.top > container.top) {
      const topEnd = zoomTarget.offsetTop - Imgin.y + containerOut.offsetTop;
      zoomTarget.style.top = `${topEnd}px`;

      //   visibleArea.style.top = `0px`;
    }
    if (Imgin.bottom < container.bottom) {
      zoomTarget.style.top = `${
        Number(zoomTarget.style.top.replace("px", "")) +
        Math.abs(Imgin.bottom - container.bottom)
      }px`;
    }
  };

  const checkLeftSize = () => {
    const containerOut = containerRef.current;
    const zoomTarget = mapWrapperRef.current;
    const visibleArea = visibleRef.current;
    const miniMap = miniMapRef.current;

    if (
      containerOut === null ||
      zoomTarget === null ||
      miniMap === null ||
      visibleArea === null
    )
      return;

    const container = containerOut.getBoundingClientRect();
    const Imgin = zoomTarget.getBoundingClientRect();
    const miniMapRect = miniMap.getBoundingClientRect();

    if (Imgin.left > container.left) {
      const leftEnd = (Imgin.width - container.width) / 2;
      const visibleAreaX = (leftEnd / Imgin.width) * miniMapRect.width;
      zoomTarget.style.left = `${leftEnd}px`;
      visibleArea.style.left = `calc(50% - ${visibleAreaX}px)`;
    } else if (Imgin.right < container.right) {
      const leftEnd = (Imgin.width - container.width) / 2;
      const visibleAreaX = (leftEnd / Imgin.width) * miniMapRect.width;
      zoomTarget.style.left = `-${(Imgin.width - container.width) / 2}px`;
      visibleArea.style.left = `calc(50% + ${visibleAreaX}px)`;
    }
  };

  const end = (e: any) => {
    clicked.current = false;
  };

  useEffect(() => {
    const mapWrapper = mapWrapperRef.current;
    if (mapWrapper === null) return;

    scale.current = 2;

    const newHeight = mapWrapper.getBoundingClientRect().height;
    currentMapHeight.current = newHeight;
    setWidthPercent(100 / scale.current);

    if (mapWrapperRef) {
      mapWrapper.style.transform = `scale(2)`;
      mapWrapper.style.transition = `transform 0.8s ease`;
    }

    setTimeout(() => {
      mapWrapper.style.removeProperty("transition");

      if (isMobile) initTouchZoomEvent();
      else initMouseZoomEvent();
    }, 900);

    return () => {
      if (mapWrapperRef) {
        mapWrapper.style.transform = `scale(1)`;
      }
    };
  }, []);

  return (
    <Container ref={containerRef} isMobile={isMobile}>
      <MapWrapper ref={mapWrapperRef}>
        <Map src={mapImg} alt="map_alt" />
        <TvButtonWrap>
          <Button animation={animation}>
            <DeviceIcon src={tvIcon} animation={animation} />
          </Button>
          <DeviceName animation={animation}>TV</DeviceName>
        </TvButtonWrap>
        <RefrigeratorButtonWrap>
          <Button
            animation={animation}
            onClick={() => {
              setShowSpeechBubble(true);
            }}
          >
            <Link to="/device/김치냉장고" style={linkStyle}>
              <SpeechBubble showSpeechBubble={showSpeechBubble}>
                <div style={{ position: "relative" }}>
                  <SpeecheBubbleTitle>김치냉장고</SpeecheBubbleTitle>
                  <SpeechBubbleState>문닫힘</SpeechBubbleState>
                  <DownArrowIcon src={downArrowIcon} />
                </div>
              </SpeechBubble>
            </Link>
            <DeviceIcon src={kimchiRefrigeratorIcon} animation={animation} />
          </Button>
          <DeviceName animation={animation}>김치냉장고</DeviceName>
        </RefrigeratorButtonWrap>
        <AirCleanerButtonWrap>
          <Button animation={animation}>
            <DeviceIcon src={airCleanerIcon} animation={animation} />
          </Button>
          <DeviceName animation={animation}>공기청정기</DeviceName>
        </AirCleanerButtonWrap>
        <LivingRoom>거실</LivingRoom>
      </MapWrapper>

      <ButtonWrapper>
        <DeviceOptions>
          <SpanText>모든기기</SpanText>
          <ArrowIconImg src={upArrowIcon} alt="up_arrow_icon" />
        </DeviceOptions>
        <RefreshButton>
          <RefreshIconImg src={refreshIcon} alt="refresh_icon" />
        </RefreshButton>
      </ButtonWrapper>

      <MiniMapContainer>
        <MiniMapWrapper ref={miniMapRef}>
          <MiniMap src={mapImg} alt="map_alt" />
          <VisibleArea
            ref={visibleRef}
            widthPercent={`${widthPercent}%`}
            heightPercent={`${heightPercent}%`}
          ></VisibleArea>
        </MiniMapWrapper>
      </MiniMapContainer>
    </Container>
  );
};

export default Viewer;
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG more
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함