kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Refactoring the puzzle and organizing files
rodzic
2aa21874f7
commit
e040c9f981
|
@ -0,0 +1,64 @@
|
||||||
|
import React, { useRef } from 'react';
|
||||||
|
|
||||||
|
interface IPuzzleCaptcha {
|
||||||
|
bg: string;
|
||||||
|
puzzle: string;
|
||||||
|
position: { x: number; y: number };
|
||||||
|
onChange(point: { x: number; y: number }): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PuzzleCaptcha: React.FC<IPuzzleCaptcha> = ({ bg, puzzle, position, onChange }) => {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const calculateNewPosition = (
|
||||||
|
clientX: number,
|
||||||
|
clientY: number,
|
||||||
|
elementWidth: number,
|
||||||
|
elementHeight: number,
|
||||||
|
dropArea: DOMRect,
|
||||||
|
) => {
|
||||||
|
const newX = Math.min(Math.max(clientX - dropArea.left - elementWidth / 2, 0), dropArea.width - elementWidth);
|
||||||
|
const newY = Math.min(Math.max(clientY - dropArea.top - elementHeight / 2, 0), dropArea.height - elementHeight);
|
||||||
|
return { x: newX, y: newY };
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePointerMove = (e: React.PointerEvent<HTMLImageElement>) => {
|
||||||
|
if (!e.currentTarget.hasPointerCapture(e.pointerId)) return;
|
||||||
|
const dropArea = ref.current?.getBoundingClientRect();
|
||||||
|
if (!dropArea) return;
|
||||||
|
|
||||||
|
const newPosition = calculateNewPosition(e.clientX, e.clientY, e.currentTarget.width, e.currentTarget.height, dropArea);
|
||||||
|
onChange(newPosition);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTouchMove = (event: React.TouchEvent<HTMLImageElement>) => {
|
||||||
|
const touch = event.touches[0];
|
||||||
|
const dropArea = ref.current?.getBoundingClientRect();
|
||||||
|
if (!dropArea) return;
|
||||||
|
|
||||||
|
const newPosition = calculateNewPosition(touch.clientX, touch.clientY, 61, 61, dropArea);
|
||||||
|
onChange(newPosition);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div id='drop-area' ref={ref} className='relative'>
|
||||||
|
<img
|
||||||
|
className='drop-shadow-black absolute z-[101] w-[61px] drop-shadow-2xl hover:cursor-grab'
|
||||||
|
src={puzzle}
|
||||||
|
alt=''
|
||||||
|
onPointerDown={(e) => e.currentTarget.setPointerCapture(e.pointerId)}
|
||||||
|
onPointerMove={handlePointerMove}
|
||||||
|
onPointerUp={(e) => e.currentTarget.releasePointerCapture(e.pointerId)}
|
||||||
|
onTouchMove={handleTouchMove}
|
||||||
|
style={{
|
||||||
|
filter: 'drop-shadow(2px 0 0 #fff) drop-shadow(0 2px 0 #fff) drop-shadow(-2px 0 0 #fff) drop-shadow(0 -2px 0 #fff)',
|
||||||
|
left: position.x,
|
||||||
|
top: position.y,
|
||||||
|
}}
|
||||||
|
draggable={false}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<img src={bg} alt='' className='rounded-2xl' draggable={false} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,109 +0,0 @@
|
||||||
import React, { useRef, useState } from 'react';
|
|
||||||
|
|
||||||
interface IPuzzleCaptcha {
|
|
||||||
bg: string;
|
|
||||||
puzzle: string;
|
|
||||||
position: { x: number; y: number };
|
|
||||||
onChange(point: { x: number; y: number }): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PuzzleCaptcha: React.FC<IPuzzleCaptcha> = ({ bg, puzzle, position, onChange }) => {
|
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
|
||||||
const [touchOffsetX, setTouchOffsetX] = useState(0);
|
|
||||||
const [touchOffsetY, setTouchOffsetY] = useState(0);
|
|
||||||
|
|
||||||
const handlePointerDown = (e: React.PointerEvent<HTMLImageElement>) => {
|
|
||||||
e.currentTarget.setPointerCapture(e.pointerId);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePointerMove = (e: React.PointerEvent<HTMLImageElement>) => {
|
|
||||||
if (!e.currentTarget.hasPointerCapture(e.pointerId)) return;
|
|
||||||
|
|
||||||
const rect = ref.current?.getBoundingClientRect();
|
|
||||||
if (!rect) return;
|
|
||||||
|
|
||||||
const newPosition = {
|
|
||||||
x: e.clientX - rect.left - e.currentTarget.width / 2,
|
|
||||||
y: e.clientY - rect.top - e.currentTarget.height / 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
onChange(newPosition);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleTouchStart = (event: React.TouchEvent<HTMLImageElement>) => {
|
|
||||||
const imageElement = event.currentTarget.getBoundingClientRect();
|
|
||||||
const touch = event.touches[0];
|
|
||||||
|
|
||||||
const offsetX = touch.clientX - imageElement.left;
|
|
||||||
const offsetY = touch.clientY - imageElement.top;
|
|
||||||
|
|
||||||
setTouchOffsetX(offsetX);
|
|
||||||
setTouchOffsetY(offsetY);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const handleTouchMove = (event: React.TouchEvent<HTMLImageElement>) => {
|
|
||||||
const touch = event.touches[0];
|
|
||||||
|
|
||||||
const dropArea = document.getElementById('drop-area')?.getBoundingClientRect();
|
|
||||||
if (!dropArea) return;
|
|
||||||
|
|
||||||
const newLeft = touch.clientX - dropArea.left - touchOffsetX;
|
|
||||||
const newTop = touch.clientY - dropArea.top - touchOffsetY;
|
|
||||||
|
|
||||||
const imageWidth = 61;
|
|
||||||
const imageHeight = 61;
|
|
||||||
const limitedLeft = Math.min(Math.max(newLeft, 0), dropArea.width - imageWidth);
|
|
||||||
const limitedTop = Math.min(Math.max(newTop, 0), dropArea.height - imageHeight);
|
|
||||||
|
|
||||||
onChange({ x: limitedLeft, y: limitedTop });
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePointerUp = (e: React.PointerEvent<HTMLImageElement>) => {
|
|
||||||
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleTouchDrop = (event: React.TouchEvent<HTMLDivElement>) => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const dropArea = document.getElementById('drop-area')?.getBoundingClientRect();
|
|
||||||
if (!dropArea) return;
|
|
||||||
|
|
||||||
const touch = event.changedTouches[0];
|
|
||||||
|
|
||||||
const newLeft = touch.clientX - dropArea.left - touchOffsetX;
|
|
||||||
const newTop = touch.clientY - dropArea.top - touchOffsetY;
|
|
||||||
|
|
||||||
const imageWidth = 61;
|
|
||||||
const imageHeight = 61;
|
|
||||||
|
|
||||||
const limitedLeft = Math.min(Math.max(newLeft, 0), dropArea.width - imageWidth);
|
|
||||||
const limitedTop = Math.min(Math.max(newTop, 0), dropArea.height - imageHeight);
|
|
||||||
|
|
||||||
onChange({ x: limitedLeft, y: limitedTop });
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id='drop-area' ref={ref} className='relative' onTouchEnd={handleTouchDrop}>
|
|
||||||
<img
|
|
||||||
className='drop-shadow-black absolute z-[101] w-[61px] drop-shadow-2xl hover:cursor-grab'
|
|
||||||
src={puzzle}
|
|
||||||
alt=''
|
|
||||||
onTouchStart={handleTouchStart}
|
|
||||||
onTouchMove={handleTouchMove}
|
|
||||||
onPointerDown={handlePointerDown}
|
|
||||||
onPointerMove={handlePointerMove}
|
|
||||||
onPointerUp={handlePointerUp}
|
|
||||||
style={{
|
|
||||||
filter: 'drop-shadow(2px 0 0 #fff) drop-shadow(0 2px 0 #fff) drop-shadow(-2px 0 0 #fff) drop-shadow(0 -2px 0 #fff)',
|
|
||||||
left: position.x,
|
|
||||||
top: position.y,
|
|
||||||
}}
|
|
||||||
draggable={false}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<img src={bg} alt='' className='rounded-2xl' draggable={false} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
Ładowanie…
Reference in New Issue