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 DragableBox from "./DragableBox";
 | 
				
			||||||
import { PhAngle } from "./PhAngle";
 | 
					import { Coordinates } from "~/types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Coordinates = {
 | 
					type ContainerDimensions = {
 | 
				
			||||||
  x: number;
 | 
					  height: number;
 | 
				
			||||||
  y: number;
 | 
					  width: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function CropSelector() {
 | 
					export default function CropSelector() {
 | 
				
			||||||
@ -20,10 +20,17 @@ export default function CropSelector() {
 | 
				
			|||||||
    x: 0,
 | 
					    x: 0,
 | 
				
			||||||
    y: 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);
 | 
					  const containerRef = useRef<HTMLDivElement>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Update selector position and dimensions based on handle positions
 | 
					 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    setSelectorPosition({
 | 
					    setSelectorPosition({
 | 
				
			||||||
      x: Math.min(firstDragable.x, secondDragable.x),
 | 
					      x: Math.min(firstDragable.x, secondDragable.x),
 | 
				
			||||||
@ -31,28 +38,83 @@ export default function CropSelector() {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }, [firstDragable, secondDragable]);
 | 
					  }, [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 selectorWidth = Math.abs(secondDragable.x - firstDragable.x);
 | 
				
			||||||
  const selectorHeight = Math.abs(secondDragable.y - firstDragable.y);
 | 
					  const selectorHeight = Math.abs(secondDragable.y - firstDragable.y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Handle selector drag
 | 
					 | 
				
			||||||
  const handleSelectorDrag = (newPosition: Coordinates) => {
 | 
					  const handleSelectorDrag = (newPosition: Coordinates) => {
 | 
				
			||||||
    const deltaX = newPosition.x - selectorPosition.x;
 | 
					    const deltaX = newPosition.x - selectorPosition.x;
 | 
				
			||||||
    const deltaY = newPosition.y - selectorPosition.y;
 | 
					    const deltaY = newPosition.y - selectorPosition.y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setFirstDragable({
 | 
					    const newFirstX = Math.min(firstDragable.x, secondDragable.x) + deltaX;
 | 
				
			||||||
      x: Math.min(firstDragable.x, secondDragable.x) + deltaX,
 | 
					    const newFirstY = Math.min(firstDragable.y, secondDragable.y) + deltaY;
 | 
				
			||||||
      y: 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({
 | 
					    // Ensure the selector stays within container bounds
 | 
				
			||||||
      x: Math.max(firstDragable.x, secondDragable.x) + deltaX,
 | 
					    if (newFirstX >= 0 && newSecondX <= containerSize.width &&
 | 
				
			||||||
      y: Math.max(firstDragable.y, secondDragable.y) + deltaY
 | 
					        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 (
 | 
					  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">
 | 
					      <div ref={containerRef} className="relative w-1/3 h-1/3 border-black border">
 | 
				
			||||||
        <DragableBox
 | 
					        <DragableBox
 | 
				
			||||||
          position={selectorPosition}
 | 
					          position={selectorPosition}
 | 
				
			||||||
@ -64,12 +126,39 @@ export default function CropSelector() {
 | 
				
			|||||||
          className="border border-black"
 | 
					          className="border border-black"
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
        <DragableBox position={firstDragable} coordSetter={setFirstDragable} mode="handle">
 | 
					        <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>
 | 
				
			||||||
        <DragableBox position={secondDragable} coordSetter={setSecondDragable} mode="handle">
 | 
					        <DragableBox position={secondDragable} coordSetter={setSecondDragable} mode="handle">
 | 
				
			||||||
          <PhAngle />
 | 
					          <div className="bg-red-600 h-2 w-2 rounded-full hover:scale-125" />
 | 
				
			||||||
        </DragableBox>
 | 
					        </DragableBox>
 | 
				
			||||||
      </div>
 | 
					      </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>
 | 
					    </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