我使用HTMLcanvas和一张照片创建了一个地图,但照片太大了,所以当我试图以正常大小渲染它时,画布的宽度和高度与照片的相同,
但当我放大时,它崩溃了,因为画布太大了。
我有两个放大事件(2_fingers_touch和滚轮);
我还试着用一个选定的点将视图居中,但画布没有移动:D
我不知道如何从这里继续。
这是我的代码:
import React, { useContext, useEffect, useRef, useState } from 'react';
import { MapProps, TrackPoint, pwgParamsType, Pin } from '../../common/types/YotiMaps.types';
import { CrisisContext } from '../../contexts/CrisisContextProvider';
import { useGeolocation } from '../../common/hooks';
import Loading from '../Loading/Loading';
import crisisLocationImageSrc from '/maps/crisis-location-icon.png'
import pinImageSrc from '/maps/pin.png';
import locationImageSrc from '/maps/self-location-icon.png'
import mapImgSrc from '/maps/suger.png';
import { Crisis } from '../../common/types';
import { convertLatLonToITM } from '../../pages/unit-conversion-page/components/conversionHelperFunctions'
import { PopupContext } from '../../contexts/PopupContextProvider';
import './maps.scss';
const YotiMaps: React.FC<MapProps> = (props) => {
const { height, width, maxPins } = props
const { alertPopup } = useContext(PopupContext);
const pgwData = useRef<pwgParamsType | null>(null)
const pins = useRef<number>(0);
const mapImage = useRef<HTMLImageElement | null>(null)
const [pinArray] = useState<Pin[]>([])
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const pgwFilePath = '/src/components/Map/suger.pgw';
const { crisis } = useContext(CrisisContext);
const myLocation = useRef(useGeolocation())
// SCALE THINGS
const [scale, setScale] = useState(1.5);
let initialTouchDistance = 1;
// Drawing the Images first time
const pinImage = new Image();
const locationImage = new Image();
const crisisLocationImage = new Image();
pinImage.src = pinImageSrc;
locationImage.src = locationImageSrc;
crisisLocationImage.src = crisisLocationImageSrc;
// draws the map first time
useEffect(() => {
const canvas = canvasRef.current;
if (canvas) {
const ctx = canvas.getContext('2d');
if (ctx) {
const map = new Image();
map.src = mapImgSrc
map.onload = () => {
redrawCanvas(ctx, map);
mapImage.current = map
};
getPgwData();
canvas.addEventListener('click', handleCanvasClick);
canvas.addEventListener("wheel", handleWheelChange, { passive: false });
// Clean up event listener
return () => {
canvas.removeEventListener('click', handleCanvasClick);
canvas.removeEventListener("wheel", handleWheelChange)
};
}
}
}, []);
// *****FUNCTIONS******
const redrawCanvas = async (ctx: CanvasRenderingContext2D, image: HTMLImageElement) => {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(image, 0, 0);
drawMyLocation(ctx);
drawMeetupLocation(ctx, crisis)
};
const drawMyLocation = (ctx: CanvasRenderingContext2D) => {
let x: number = 0;
let y: number = 0;
let fixedCoordinates = { easting: 0, northing: 0 }
const context = ctx
myLocation.current()
.then((coords) => {
x = coords.longitude;
y = coords.latitude;
context.drawImage(locationImage, x, y, locationImage.width * 2 / 3, locationImage.height * 2 / 3);
fixedCoordinates = convertLatLonToITM(y, x)
console.log(fixedCoordinates);
})
.catch((error: ErrorOptions) => {
alertPopup(`××ק××× ×× × ×צ×`, "red");
console.error('Error loading self location', error)
});
}
const drawMeetupLocation = (ctx: CanvasRenderingContext2D, crisis: Crisis | null) => {
if (!crisis) return <Loading />
const stringMeetup = crisis.meetup
const meetup = { lat: parseFloat(stringMeetup.lat), lng: parseFloat(stringMeetup.lng) }
// const meetup = { lat: 100, lng: 100 }
ctx.drawImage(crisisLocationImage, meetup.lat, meetup.lng, crisisLocationImage.width, crisisLocationImage.height);
}
const getPgwData = async () => {
try {
const pgwResponse = await fetch(pgwFilePath);
const pgwData = await pgwResponse.text();
parsePGWFile(pgwData);
} catch (error) {
console.error('Error loading PGW file:', error);
}
}
const calculateItmCoordinates = (clickX: number, clickY: number, pgwData: pwgParamsType | null,): { transformedX: number, transformedY: number } => {
let tempObj: { transformedX: number, transformedY: number } = { transformedX: 0, transformedY: 0 }
if (pgwData) {
const transformedX = pgwData.pixelSizeX * clickX / scale + pgwData.originX;
const transformedY = pgwData.pixelSizeY * clickY / scale + pgwData.originY;
tempObj = { transformedX, transformedY }
}
return tempObj;
}
const calculatePixelCoordinates = (easting: number, northing: number, pgwData: pwgParamsType | null): { fakeX: number, fakeY: number } => {
let tempObj: { fakeX: number, fakeY: number } = { fakeX: 0, fakeY: 0 }
if (pgwData) {
tempObj.fakeX = (easting - pgwData.originX) * scale / pgwData.pixelSizeX,
tempObj.fakeY = (northing - pgwData.originY) * scale / pgwData.pixelSizeY
}
return tempObj;
}
const parsePGWFile = (pgwDataFromFetch: string): void => {
const parameters = pgwDataFromFetch.trim().split('\n');
const pixelSizeX = parseFloat(parameters[0]);
const rotationX = parseFloat(parameters[1]);
const rotationY = parseFloat(parameters[2]);
const pixelSizeY = parseFloat(parameters[3]);
const originX = parseFloat(parameters[4]);
const originY = parseFloat(parameters[5]);
pgwData.current = {
pixelSizeX,
rotationX,
rotationY,
pixelSizeY,
originX,
originY,
}
};
// **********EVENTS-FUNCTIONS************
const handleCanvasClick = (event: MouseEvent) => {
const canvas = canvasRef.current;
const ctx = canvas?.getContext('2d');
if (canvas && ctx) {
const rect = canvas.getBoundingClientRect()
const clickedX = event.clientX - rect.left;
const clickedY = event.clientY - rect.top;
if (pinArray.length < maxPins) {
let coordinatesObj: { transformedX: number, transformedY: number } = calculateItmCoordinates(clickedX, clickedY, pgwData?.current)
ctx.drawImage(pinImage, clickedX - 23, clickedY - 40, pinImage.width, pinImage.height);
const newPin: Pin = { id: pins.current, x: clickedX, y: clickedY, actualY: coordinatesObj.transformedY, actualX: coordinatesObj.transformedX };
pins.current++;
pinArray.push(newPin)
console.log(pinArray);
} else {
let coordinatesObj = calculateItmCoordinates(clickedX, clickedY, pgwData?.current)
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (mapImage.current) {
redrawCanvas(ctx, mapImage.current);
for (let i = 1; i < pinArray.length; i++) {
ctx.drawImage(pinImage, pinArray[i].x - 23, pinArray[i].y - 40, pinImage.width, pinImage.height);
}
ctx.drawImage(pinImage, clickedX - 23, clickedY - 40, pinImage.width, pinImage.height);
const newPin: Pin = { id: pins.current, x: clickedX, y: clickedY, actualY: coordinatesObj.transformedY, actualX: coordinatesObj.transformedX };
pinArray.splice(0, 1);
pinArray.push(newPin);
pins.current++;
console.log(pinArray);
}
};
}
};
function getTouchDistance(touch1: React.Touch, touch2: React.Touch): number {
const dx = touch2.clientX - touch1.clientX;
const dy = touch2.clientY - touch1.clientY;
return Math.sqrt(dx * dx + dy * dy);
}
const handleCanvasTouchStart = (event: React.TouchEvent<HTMLCanvasElement>) => {
if (event.touches.length >= 2) {
const touch1 = event.touches[0];
const touch2 = event.touches[1];
initialTouchDistance = getTouchDistance(touch1, touch2);
}
};
const handleCanvasTouchMove = (event: React.TouchEvent<HTMLCanvasElement>) => {
if (event.touches.length >= 2) {
const touch1 = event.touches[0];
const touch2 = event.touches[1];
const currentTouchDistance = getTouchDistance(touch1, touch2);
const zoomLevel = currentTouchDistance / initialTouchDistance;
setScale(zoomLevel);
}
};
const handleWheelChange = (event: WheelEvent) => {
event.preventDefault()
const delta = Math.max(-1, Math.min(1, event.deltaY));
const scaleIncrement = 0.001;
setScale(prev=> prev*delta+scaleIncrement)
}
return (
<canvas
id='yotiMaps'
ref={canvasRef}
width={14000 * scale}
height={4116 * scale}
style={{ transform: `scale(${scale})` }}
onTouchStart={handleCanvasTouchStart}
onTouchMove={handleCanvasTouchMove}
onWheel={handleWheelChange}
/>
);
};
export default YotiMaps;
我试着把照片剪成7等分,但当你只放大一张照片,而你能看到另一张照片却没有放大时,这看起来很糟糕,
我想也许可以试着做两个相等的画布,但在我做之前,我想看看是否有人有更好的想法。
在画布渲染时,我试图将大小设置为比例大小,但它会压碎,所以这是一个错误。