提问人:Michael Hutchinson 提问时间:11/13/2023 最后编辑:DavidMichael Hutchinson 更新时间:11/13/2023 访问量:38
如何在图像上映射点并使其响应
How to map points on an image and make it responsive
问:
我试图绘制体育场的各个部分并使它们可点击,但是当我获得点并将它们添加到我的数组中时,总是有一点偏差,所以我认为我的计算可能是错误的,这是 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;
我试图通过单击下面的图像来绘制这些点,然后我控制台记录它们并将它们添加到数组中,但它们从来都不一样
答: 暂无答案
评论