i lied, its not pixels
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Deploy to Cloudflare Pages / deploy (push) Successful in 32s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Deploy to Cloudflare Pages / deploy (push) Successful in 32s
				
			This commit is contained in:
		
							parent
							
								
									499caf6da5
								
							
						
					
					
						commit
						bc9cf1bf25
					
				@ -1,10 +1,10 @@
 | 
			
		||||
import { useEffect, useRef, useState } from "react";
 | 
			
		||||
import React, { ChangeEvent, useEffect, useRef, useState } from "react";
 | 
			
		||||
import DragableBox from "./DragableBox";
 | 
			
		||||
import { PhAngle } from "./PhAngle";
 | 
			
		||||
import { Coordinates } from "~/types";
 | 
			
		||||
 | 
			
		||||
type Coordinates = {
 | 
			
		||||
  x: number;
 | 
			
		||||
  y: number;
 | 
			
		||||
type ContainerDimensions = {
 | 
			
		||||
  height: number;
 | 
			
		||||
  width: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default function CropSelector() {
 | 
			
		||||
@ -20,10 +20,17 @@ export default function CropSelector() {
 | 
			
		||||
    x: 0,
 | 
			
		||||
    y: 0
 | 
			
		||||
  });
 | 
			
		||||
  const [containerSize, setContainerSize] = useState<ContainerDimensions>({
 | 
			
		||||
    height: 0,
 | 
			
		||||
    width: 0
 | 
			
		||||
  });
 | 
			
		||||
  const [userInputs, setUserInputs] = useState<ContainerDimensions>({
 | 
			
		||||
    height: 100,
 | 
			
		||||
    width: 100
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const containerRef = useRef<HTMLDivElement>(null);
 | 
			
		||||
 | 
			
		||||
  // Update selector position and dimensions based on handle positions
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setSelectorPosition({
 | 
			
		||||
      x: Math.min(firstDragable.x, secondDragable.x),
 | 
			
		||||
@ -31,28 +38,83 @@ export default function CropSelector() {
 | 
			
		||||
    });
 | 
			
		||||
  }, [firstDragable, secondDragable]);
 | 
			
		||||
 | 
			
		||||
  // Calculate selector dimensions
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (!containerRef.current) return;
 | 
			
		||||
 | 
			
		||||
    const resizeObserver = new ResizeObserver(entries => {
 | 
			
		||||
      const entry = entries[0];
 | 
			
		||||
      setContainerSize({
 | 
			
		||||
        width: entry.contentRect.width,
 | 
			
		||||
        height: entry.contentRect.height
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    resizeObserver.observe(containerRef.current);
 | 
			
		||||
 | 
			
		||||
    return () => resizeObserver.disconnect();
 | 
			
		||||
  }, [containerRef]);
 | 
			
		||||
 | 
			
		||||
  const selectorWidth = Math.abs(secondDragable.x - firstDragable.x);
 | 
			
		||||
  const selectorHeight = Math.abs(secondDragable.y - firstDragable.y);
 | 
			
		||||
 | 
			
		||||
  // Handle selector drag
 | 
			
		||||
  const handleSelectorDrag = (newPosition: Coordinates) => {
 | 
			
		||||
    const deltaX = newPosition.x - selectorPosition.x;
 | 
			
		||||
    const deltaY = newPosition.y - selectorPosition.y;
 | 
			
		||||
 | 
			
		||||
    setFirstDragable({
 | 
			
		||||
      x: Math.min(firstDragable.x, secondDragable.x) + deltaX,
 | 
			
		||||
      y: Math.min(firstDragable.y, secondDragable.y) + deltaY
 | 
			
		||||
    });
 | 
			
		||||
    const newFirstX = Math.min(firstDragable.x, secondDragable.x) + deltaX;
 | 
			
		||||
    const newFirstY = Math.min(firstDragable.y, secondDragable.y) + deltaY;
 | 
			
		||||
    const newSecondX = Math.max(firstDragable.x, secondDragable.x) + deltaX;
 | 
			
		||||
    const newSecondY = Math.max(firstDragable.y, secondDragable.y) + deltaY;
 | 
			
		||||
 | 
			
		||||
    setSecondDragable({
 | 
			
		||||
      x: Math.max(firstDragable.x, secondDragable.x) + deltaX,
 | 
			
		||||
      y: Math.max(firstDragable.y, secondDragable.y) + deltaY
 | 
			
		||||
    // Ensure the selector stays within container bounds
 | 
			
		||||
    if (newFirstX >= 0 && newSecondX <= containerSize.width &&
 | 
			
		||||
        newFirstY >= 0 && newSecondY <= containerSize.height) {
 | 
			
		||||
      setFirstDragable({
 | 
			
		||||
        x: newFirstX,
 | 
			
		||||
        y: newFirstY
 | 
			
		||||
      });
 | 
			
		||||
      setSecondDragable({
 | 
			
		||||
        x: newSecondX,
 | 
			
		||||
        y: newSecondY
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleDimensionChange = (
 | 
			
		||||
    dimension: 'width' | 'height',
 | 
			
		||||
    event: React.ChangeEvent<HTMLInputElement>
 | 
			
		||||
  ) => {
 | 
			
		||||
    const value = Math.max(0, parseInt(event.target.value) || 0);
 | 
			
		||||
    const maxValue = dimension === 'width' ? containerSize.width : containerSize.height;
 | 
			
		||||
    const clampedValue = Math.min(value, maxValue);
 | 
			
		||||
 | 
			
		||||
    setUserInputs(prev => ({
 | 
			
		||||
      ...prev,
 | 
			
		||||
      [dimension]: clampedValue
 | 
			
		||||
    }));
 | 
			
		||||
 | 
			
		||||
    // Update selector dimensions
 | 
			
		||||
    if (dimension === 'width') {
 | 
			
		||||
      const newX = Math.min(firstDragable.x, containerSize.width - clampedValue);
 | 
			
		||||
      setFirstDragable(prev => ({ ...prev, x: newX }));
 | 
			
		||||
      setSecondDragable(prev => ({ ...prev, x: newX + clampedValue }));
 | 
			
		||||
    } else {
 | 
			
		||||
      const newY = Math.min(firstDragable.y, containerSize.height - clampedValue);
 | 
			
		||||
      setFirstDragable(prev => ({ ...prev, y: newY }));
 | 
			
		||||
      setSecondDragable(prev => ({ ...prev, y: newY + clampedValue }));
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Update user inputs when dragging
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setUserInputs({
 | 
			
		||||
      width: Math.round(selectorWidth),
 | 
			
		||||
      height: Math.round(selectorHeight)
 | 
			
		||||
    });
 | 
			
		||||
  }, [selectorWidth, selectorHeight]);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="flex flex-col h-screen w-screen justify-center items-center">
 | 
			
		||||
    <div className="flex flex-col h-screen w-screen justify-center items-center gap-4">
 | 
			
		||||
      <div ref={containerRef} className="relative w-1/3 h-1/3 border-black border">
 | 
			
		||||
        <DragableBox
 | 
			
		||||
          position={selectorPosition}
 | 
			
		||||
@ -64,12 +126,39 @@ export default function CropSelector() {
 | 
			
		||||
          className="border border-black"
 | 
			
		||||
        />
 | 
			
		||||
        <DragableBox position={firstDragable} coordSetter={setFirstDragable} mode="handle">
 | 
			
		||||
          <PhAngle className="-rotate-90 absolute scale-150 -top-[13px] -left-[14px]" />
 | 
			
		||||
          <div className="bg-red-600 h-2 w-2 rounded-full hover:scale-125" />
 | 
			
		||||
        </DragableBox>
 | 
			
		||||
        <DragableBox position={secondDragable} coordSetter={setSecondDragable} mode="handle">
 | 
			
		||||
          <PhAngle />
 | 
			
		||||
          <div className="bg-red-600 h-2 w-2 rounded-full hover:scale-125" />
 | 
			
		||||
        </DragableBox>
 | 
			
		||||
      </div>
 | 
			
		||||
      
 | 
			
		||||
      <div className="flex gap-4">
 | 
			
		||||
        <div className="flex flex-col gap-2">
 | 
			
		||||
          <label htmlFor="width">Width (px)</label>
 | 
			
		||||
          <input
 | 
			
		||||
            id="width"
 | 
			
		||||
            type="number"
 | 
			
		||||
            min={0}
 | 
			
		||||
            max={containerSize.width}
 | 
			
		||||
            value={userInputs.width.toFixed()}
 | 
			
		||||
            onChange={(e: ChangeEvent<HTMLInputElement>) => handleDimensionChange('width', e)}
 | 
			
		||||
            className="w-24"
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
        <div className="flex flex-col gap-2">
 | 
			
		||||
          <label htmlFor="height">Height (px)</label>
 | 
			
		||||
          <input
 | 
			
		||||
            id="height"
 | 
			
		||||
            type="number"
 | 
			
		||||
            min={0}
 | 
			
		||||
            max={containerSize.height}
 | 
			
		||||
            value={userInputs.height.toFixed()}
 | 
			
		||||
            onChange={(e) => handleDimensionChange('height', e)}
 | 
			
		||||
            className="w-24"
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										29
									
								
								app/routes/RotatableAngle.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								app/routes/RotatableAngle.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
import { Coordinates } from "~/types";
 | 
			
		||||
import { PhAngle } from "./PhAngle";
 | 
			
		||||
 | 
			
		||||
interface ComponentProps {
 | 
			
		||||
  position: Coordinates;
 | 
			
		||||
  referencePosition: Coordinates;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default function RotatableAngle({ position, referencePosition }: ComponentProps) {
 | 
			
		||||
  // Calculate angle between points
 | 
			
		||||
  const getRotationAngle = () => {
 | 
			
		||||
    const deltaX = referencePosition.x - position.x;
 | 
			
		||||
    const deltaY = referencePosition.y - position.y;
 | 
			
		||||
    // Convert from radians to degrees and adjust for SVG orientation
 | 
			
		||||
    const degrees = Math.atan2(deltaY, deltaX) * (180 / Math.PI);
 | 
			
		||||
    return degrees - 135; // Adjust baseline rotation to point right
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <PhAngle
 | 
			
		||||
      className="absolute scale-125"
 | 
			
		||||
      style={{
 | 
			
		||||
        transform: `rotate(${getRotationAngle()}deg)`,
 | 
			
		||||
        top: '-4px',
 | 
			
		||||
        left: '-12px'
 | 
			
		||||
      }}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										4
									
								
								app/types.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								app/types.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
			
		||||
export type Coordinates = {
 | 
			
		||||
  x: number;
 | 
			
		||||
  y: number;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user