如何在图像上映射点并使其响应

How to map points on an image and make it responsive

提问人:Michael Hutchinson 提问时间:11/13/2023 最后编辑:DavidMichael Hutchinson 更新时间:11/13/2023 访问量:38

问:

这是我绘制的要点

我试图绘制体育场的各个部分并使它们可点击,但是当我获得点并将它们添加到我的数组中时,总是有一点偏差,所以我认为我的计算可能是错误的,这是 nextJS 中的代码

///

'use client';
import React, { useCallback, useEffect, useRef, useState } from 'react';

import Stadium1 from '@/assets/stadium.png';
import PageWrapper from '@/components/PageWrapper/PageWrapper';
import Image from 'next/image';

interface StadiumProps {}

const exampleResponseOfStadium = [
  'Block A:429,1;429,24;449,25;449,3',
  'Block B:86.5,386.8671875;86.5,359.8671875;158.5,359.8671875;157.5,386.8671875',
  'Block C:1.5,188.8671875;0.5,123.8671875;25.5,123.8671875;24.5,188.8671875',
  'Block D:467.5,28.8671875;467.5,317.8671875;497.5,316.8671875;497.5,28.8671875',
];

const Stadium = ({}: StadiumProps) => {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const [points, setPoints] = useState<number[][]>([]);
  const [newPoints, setNewPoints] = useState<
    | {
        blockName: string;
        points: string;
      }[]
    | null
  >(null);
  const [containerSize, setContainerSize] = useState<DOMRect | null>(null);

  const [windowSize, setWindowSize] = useState({
    width: typeof window !== 'undefined' ? window.innerWidth : 0,
    height: typeof window !== 'undefined' ? window.innerHeight : 0,
  });

  useEffect(() => {
    const handleResize = () => {
      setWindowSize({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    };

    if (typeof window !== 'undefined') {
      window.addEventListener('resize', handleResize);

      return () => {
        window.removeEventListener('resize', handleResize);
      };
    }
  }, []);

  const handleCanvasClick = (event: React.MouseEvent<HTMLCanvasElement>) => {
    const canvas = canvasRef.current;
    if (canvas) {
      const rect = canvas.getBoundingClientRect();
      const x = event.clientX - rect.left;
      const y = event.clientY - rect.top;
      setPoints([...points, [x, y]]);
    }
  };

  const handleClearClick = () => {
    setPoints([]);
  };

  const handleSaveClick = () => {
    const canvas = canvasRef.current;
    if (canvas) {
      const dataURL = canvas.toDataURL();
      // console.log(dataURL);
      const joinedPonts = points.map((point) => point.join(',')).join(' ');
      console.log({ joinedPonts });
    }
  };

  const containerRef = useRef<HTMLDivElement>(null);
  const svgRef = useRef<SVGSVGElement>(null);

  const calculateNewPoints = useCallback(() => {
    if (containerSize === null) return;
    // const currPoints = '429,1 429,24 449,25 449,3';
    const mappedData = exampleResponseOfStadium.map((block) => {
      const [blockName, points] = block.split(':');
      const pointsArray = points.replaceAll(';', ' ');
      const oldWidth = 500;
      const oldHeight = 400;
      const newWidth = containerSize.width;
      const newHeight = containerSize.height;

      const scaleX = newWidth / oldWidth;
      const scaleY = newHeight / oldHeight;

      const newPoints = pointsArray
        .split(' ')
        .map((point) => {
          const [x, y] = point.split(',');
          return `${Math.floor(Number(x)) * scaleX},${
            Math.floor(Number(y)) * scaleY
          }`;
        })
        .join(' ');
      // console.log({ newPoints, pointsArray });
      return { blockName, points: newPoints };
    });
    return mappedData;
  }, [containerSize]);

  useEffect(() => {
    if (containerRef) {
      const boundingRectangle = containerRef.current?.getBoundingClientRect();
      if (boundingRectangle?.width && boundingRectangle?.height) {
        setContainerSize(boundingRectangle);
      }
    }
  }, [windowSize]);

  useEffect(() => {
    const test = calculateNewPoints();
    if (test) {
      setNewPoints(test);
    }
    const mySvg = svgRef.current;
    if (mySvg) {
      mySvg.setAttribute(
        'viewBox',
        `0 0 ${containerSize?.width} ${containerSize?.height}`
      );
      mySvg.setAttribute('width', `${containerSize?.width}`);
      mySvg.setAttribute('height', `${containerSize?.height}`);
    }
  }, [calculateNewPoints, containerSize]);

  return (
    <div className="mx-auto flex max-w-7xl items-center justify-between px-4 py-6 sm:px-6 lg:px-8">
      <div className="mx-auto flex max-w-3xl flex-col gap-8 px-4 py-10">
        <div
          className="relative h-full w-full rounded-md bg-cover bg-center"
          ref={containerRef}
        >
          https://ibb.co/sJ47kzJ
          <Image
            src={Stadium1}
            alt="stadium"
            className="relative left-0 top-0 min-h-full"
          />
          {Boolean(newPoints) && (
            <>
              <svg
                ref={svgRef}
                className="absolute left-0 top-0 z-10"
                // viewBox="0 0 1000 800"
                preserveAspectRatio="xMinYMin meet"
              >
                {newPoints?.map((block) => {
                  const { blockName, points } = block;
                  return (
                    <polygon
                      points={points}
                      className="cursor-pointer opacity-40"
                      onClick={() => alert(blockName)}
                    />
                  );
                })}
              </svg>
              {exampleResponseOfStadium.map((block) => {
                const values = block.split(':');
                const [name, left, top, height, width] = values;
                return (
                  <div
                    key={name}
                    style={{
                      top: `${top}px`,
                      left: `${left}px`,
                      width: `${width}px`,
                      height: `${height}px`,
                    }}
                    className={`absolute z-10 bg-red-500`}
                  />
                );
              })}
            </>
          )}
        </div>

        <div>
          <h2>Draw</h2>
          <div className="mx-auto flex max-w-3xl flex-col gap-8 px-4 py-10">
            <div className="relative h-[400px] w-[500px] rounded-md bg-cover bg-center">
              {/* img ur https://ibb.co/sJ47kzJ */}
              <Image
                src={Stadium1}
                alt="stadium"
                className="absolute left-0 top-0"
              />
              <canvas
                ref={canvasRef}
                className="absolute left-0 top-0 z-10"
                width={500}
                height={400}
                onClick={handleCanvasClick}
              />
              {points.length > 0 && (
                <svg className="absolute left-0 top-0" viewBox="0 0 500 400">
                  <polygon
                    points={points.map(([x, y]) => `${x},${y}`).join(' ')}
                    fill="none"
                    stroke="red"
                  />
                </svg>
              )}
            </div>
            <div className="relative z-50 flex gap-4">
              <button onClick={handleClearClick}>Clear</button>
              <button onClick={handleSaveClick}>Save</button>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default Stadium;

我试图通过单击下面的图像来绘制这些点,然后我控制台记录它们并将它们添加到数组中,但它们从来都不一样

JavaScript TypeScript Next.js 画布

评论


答: 暂无答案