From a26949e60e609e696aabd13d7ed6dfb781012ce2 Mon Sep 17 00:00:00 2001 From: Norserium Date: Wed, 26 Oct 2022 12:38:58 +0300 Subject: [PATCH] Adapt to resize algorithm changes --- example/docs/algorithms/_category_.json | 4 + example/docs/algorithms/resize-algorithm.mdx | 11 ++ example/docs/tutorials/custom-stencil.mdx | 15 +-- .../algorithms/ResizeAlgorithm/index.scss | 18 +++ .../algorithms/ResizeAlgorithm/index.tsx | 124 ++++++++++++++++++ example/src/components/algorithms/utils.ts | 12 ++ .../components/CircleStencil.tsx | 15 +-- package.json | 4 +- src/components/service/BoundingBox.tsx | 59 ++++----- src/components/stencils/CircleStencil.tsx | 10 +- src/components/stencils/RectangleStencil.tsx | 9 +- 11 files changed, 218 insertions(+), 63 deletions(-) create mode 100644 example/docs/algorithms/_category_.json create mode 100644 example/docs/algorithms/resize-algorithm.mdx create mode 100644 example/src/components/algorithms/ResizeAlgorithm/index.scss create mode 100644 example/src/components/algorithms/ResizeAlgorithm/index.tsx create mode 100644 example/src/components/algorithms/utils.ts diff --git a/example/docs/algorithms/_category_.json b/example/docs/algorithms/_category_.json new file mode 100644 index 00000000..73df1a0d --- /dev/null +++ b/example/docs/algorithms/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Algorithms", + "position": 7 +} diff --git a/example/docs/algorithms/resize-algorithm.mdx b/example/docs/algorithms/resize-algorithm.mdx new file mode 100644 index 00000000..6c6605e3 --- /dev/null +++ b/example/docs/algorithms/resize-algorithm.mdx @@ -0,0 +1,11 @@ +--- +title: Resize Algorithm +sidebar_position: 7 +--- +import { ResizeAlgorithm } from '../../src/components/algorithms/ResizeAlgorithm'; + +# Advanced Recipes + +## Resize Algorithm + + diff --git a/example/docs/tutorials/custom-stencil.mdx b/example/docs/tutorials/custom-stencil.mdx index 39f40e7d..c1659eb6 100644 --- a/example/docs/tutorials/custom-stencil.mdx +++ b/example/docs/tutorials/custom-stencil.mdx @@ -391,17 +391,10 @@ export const CircleStencil = forwardRef( })); const onResize = (shift: MoveDirections) => { - cropper.resizeCoordinates( - { - left: -shift.top, - right: -shift.top, - top: -shift.top, - bottom: -shift.top, - }, - { - compensate: true, - }, - ); + cropper.resizeCoordinates('center', { + left: shift.left, + top: shift.left, + }); }; const onMove = (directions: MoveDirections) => { diff --git a/example/src/components/algorithms/ResizeAlgorithm/index.scss b/example/src/components/algorithms/ResizeAlgorithm/index.scss new file mode 100644 index 00000000..421da290 --- /dev/null +++ b/example/src/components/algorithms/ResizeAlgorithm/index.scss @@ -0,0 +1,18 @@ +.resize-algorithm { + width: 100%; + height: 400px; + &__boundary { + position: relative; + } + &__stencil { + position: absolute; + color: var(--ifm-color-primary); + &-line { + border-color: var(--ifm-color-primary); + } + } + &__reference { + position: absolute; + border: solid 1px var(--ifm-color-primary-dark) + } +} diff --git a/example/src/components/algorithms/ResizeAlgorithm/index.tsx b/example/src/components/algorithms/ResizeAlgorithm/index.tsx new file mode 100644 index 00000000..ccae3400 --- /dev/null +++ b/example/src/components/algorithms/ResizeAlgorithm/index.tsx @@ -0,0 +1,124 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { BoundingBox } from 'react-advanced-cropper'; +import './index.scss'; +import { coordinatesToStyle } from '@site/src/components/algorithms/utils'; +import { + approximateSize, + Coordinates, + createAspectRatio, + moveToPositionRestrictions, + anchoredResizeCoordinatesAlgorithm, + ResizeOptions, +} from 'advanced-cropper'; +import { useWindowResize } from '../../../service/useWindowResize'; + +export const ResizeAlgorithm = () => { + const exampleRef = useRef(null); + + const [aspectRatio] = useState(1); + const [boundary, setBoundary] = useState({ width: 0, height: 0 }); + const [coordinates, setCoordinates] = useState({ width: 100, height: 100, left: 0, top: 0 }); + + const [reference, setReference] = useState(null); + + const coordinatesStyle = coordinatesToStyle(coordinates); + const referenceStyle = coordinatesToStyle(reference); + + const onResize = (anchor, directions, options: ResizeOptions) => { + setReference(options.reference || null); + setCoordinates( + anchoredResizeCoordinatesAlgorithm(coordinates, anchor, directions, options, { + aspectRatio: createAspectRatio(aspectRatio), + sizeRestrictions: { + maxWidth: boundary.width, + maxHeight: boundary.height, + minWidth: 0, + minHeight: 0, + }, + positionRestrictions: { + left: 0, + top: 0, + right: boundary.width, + bottom: boundary.height, + }, + }), + ); + }; + + const onResizeEnd = () => { + setReference(null); + }; + + const updateBoundary = () => { + if (exampleRef.current) { + const updatedBoundary = { + width: exampleRef.current.clientWidth, + height: exampleRef.current.clientHeight, + }; + setBoundary(updatedBoundary); + setCoordinates((coordinates) => { + if (coordinates) { + const updatedCoordinates = { + ...coordinates, + ...approximateSize({ + width: coordinates.width, + height: coordinates.height, + aspectRatio, + sizeRestrictions: { + maxWidth: updatedBoundary.width, + maxHeight: updatedBoundary.height, + minHeight: 0, + minWidth: 0, + }, + }), + }; + return moveToPositionRestrictions(updatedCoordinates, { + left: 0, + top: 0, + bottom: updatedBoundary.height, + right: updatedBoundary.width, + }); + } + return coordinates; + }); + } + }; + + useWindowResize(updateBoundary); + + useEffect(() => { + if (exampleRef.current) { + setCoordinates({ + width: 100, + height: 100, + left: exampleRef.current.clientWidth / 2 - 50, + top: exampleRef.current.clientHeight / 2 - 50, + }); + } + updateBoundary(); + }, []); + + return ( +
+
+ +
+
+
+ ); +}; diff --git a/example/src/components/algorithms/utils.ts b/example/src/components/algorithms/utils.ts new file mode 100644 index 00000000..6d1b52e3 --- /dev/null +++ b/example/src/components/algorithms/utils.ts @@ -0,0 +1,12 @@ +import { Coordinates } from 'advanced-cropper'; + +export function coordinatesToStyle(coordinates: Coordinates | null) { + return coordinates + ? { + width: `${coordinates.width}px`, + height: `${coordinates.height}px`, + left: `${coordinates.left}px`, + top: `${coordinates.top}px`, + } + : {}; +} diff --git a/example/src/components/tutorials/CustomStencil/components/CircleStencil.tsx b/example/src/components/tutorials/CustomStencil/components/CircleStencil.tsx index d2605b08..3d2c24b6 100644 --- a/example/src/components/tutorials/CustomStencil/components/CircleStencil.tsx +++ b/example/src/components/tutorials/CustomStencil/components/CircleStencil.tsx @@ -19,17 +19,10 @@ export const CircleStencil = forwardRef(({ cropper }: })); const onResize = (shift: MoveDirections) => { - cropper.resizeCoordinates( - { - left: -shift.top, - right: -shift.top, - top: -shift.top, - bottom: -shift.top, - }, - { - compensate: true, - }, - ); + cropper.resizeCoordinates('center', { + left: shift.left, + top: shift.left, + }); }; const onMove = (directions: MoveDirections) => { diff --git a/package.json b/package.json index be1901a4..102154be 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-advanced-cropper", - "version": "0.14.0", + "version": "0.15.0", "description": "The react cropper library that gives the possibility to create croppers exactly suited for your website design", "author": "Norserium", "license": "MIT", @@ -78,7 +78,7 @@ "dist" ], "dependencies": { - "advanced-cropper": "0.14.0", + "advanced-cropper": "0.15.0", "classnames": "^2.2.6", "tslib": "^2.4.0" }, diff --git a/src/components/service/BoundingBox.tsx b/src/components/service/BoundingBox.tsx index 484d6f62..11c8d693 100644 --- a/src/components/service/BoundingBox.tsx +++ b/src/components/service/BoundingBox.tsx @@ -1,4 +1,4 @@ -import React, { ComponentType, useMemo, ReactNode, CSSProperties } from 'react'; +import React, { ComponentType, useMemo, ReactNode, CSSProperties, useState } from 'react'; import classnames from 'classnames'; import cn from 'classnames'; import { @@ -6,12 +6,13 @@ import { OrdinalDirection, HorizontalCardinalDirection, VerticalCardinalDirection, - ResizeDirections, MoveDirections, ResizeOptions, getDirectionNames, isCardinalDirection, isObject, + Coordinates, + ResizeAnchor, } from 'advanced-cropper'; import { SimpleLine } from '../lines/SimpleLine'; import { SimpleHandler } from '../handlers/SimpleHandler'; @@ -47,9 +48,10 @@ interface Props { lineClassNames?: LineClassNames; lineWrapperClassNames?: LineClassNames; disabled?: boolean; - onResize?: (directions: ResizeDirections, options: ResizeOptions) => void; + onResize?: (anchor: ResizeAnchor, directions: MoveDirections, options: ResizeOptions) => void; onResizeEnd?: () => void; children?: ReactNode; + reference?: Coordinates | null; } interface HandlerNode { @@ -113,7 +115,10 @@ export const BoundingBox = ({ lineClassNames = {}, lineWrapperClassNames = {}, disabled = false, + reference = null, }: Props) => { + const [lastReference, setLastReference] = useState(null); + const points = useMemo(() => { const result: PointNode[] = []; HORIZONTAL_DIRECTIONS.forEach((hDirection) => { @@ -198,23 +203,10 @@ export const BoundingBox = ({ ) => ({ left, top }: MoveDirections, nativeEvent: MouseEvent | TouchEvent) => { const directions = { - left: 0, - right: 0, - top: 0, - bottom: 0, + left, + top, }; - if (horizontalDirection === 'west') { - directions.left -= left; - } else if (horizontalDirection === 'east') { - directions.right += left; - } - if (verticalDirection === 'north') { - directions.top -= top; - } else if (verticalDirection === 'south') { - directions.bottom += top; - } - let respectDirection: 'width' | 'height' | undefined; if (!verticalDirection && horizontalDirection) { respectDirection = 'width'; @@ -224,20 +216,27 @@ export const BoundingBox = ({ if (!disabled) { if (onResize) { - onResize(directions, { - allowedDirections: { - left: horizontalDirection === 'west' || !horizontalDirection, - right: horizontalDirection === 'east' || !horizontalDirection, - bottom: verticalDirection === 'south' || !verticalDirection, - top: verticalDirection === 'north' || !verticalDirection, - }, - preserveAspectRatio: nativeEvent && nativeEvent.shiftKey, - respectDirection, - }); + const anchor = getDirectionNames(horizontalDirection, verticalDirection).camelCase; + if (anchor) { + onResize(anchor, directions, { + reference: lastReference || reference, + preserveAspectRatio: nativeEvent && nativeEvent.shiftKey, + respectDirection, + compensate: true, + }); + } + } + if (!lastReference) { + setLastReference(reference); } } }; + const onHandlerDragEnd = () => { + onResizeEnd?.(); + setLastReference(null); + }; + return (
{children} @@ -251,7 +250,7 @@ export const BoundingBox = ({ position={line.name} disabled={line.disabled} onDrag={onHandlerDrag(line.horizontalPosition, line.verticalPosition)} - onDragEnd={onResizeEnd} + onDragEnd={onHandlerDragEnd} /> ))}
@@ -266,7 +265,7 @@ export const BoundingBox = ({ verticalPosition={handler.verticalPosition} disabled={handler.disabled} onDrag={onHandlerDrag(handler.horizontalPosition, handler.verticalPosition)} - onDragEnd={onResizeEnd} + onDragEnd={onHandlerDragEnd} /> ); diff --git a/src/components/stencils/CircleStencil.tsx b/src/components/stencils/CircleStencil.tsx index b4a6ee45..e5d00bff 100644 --- a/src/components/stencils/CircleStencil.tsx +++ b/src/components/stencils/CircleStencil.tsx @@ -5,12 +5,12 @@ import { OrdinalDirection, CropperTransitions, CropperState, - ResizeDirections, MoveDirections, CropperImage, ResizeOptions, getStencilCoordinates, CropperInteractions, + ResizeAnchor, } from 'advanced-cropper'; import { SimpleLine } from '../lines/SimpleLine'; import { SimpleHandler } from '../handlers/SimpleHandler'; @@ -18,7 +18,6 @@ import { BoundingBox } from '../service/BoundingBox'; import { StencilOverlay } from '../service/StencilOverlay'; import { DraggableArea } from '../service/DraggableArea'; import { StencilWrapper } from '../service/StencilWrapper'; -import { StencilOptions } from '../../types'; import { StencilGrid } from '../service/StencilGrid'; type HandlerComponent = ComponentType; @@ -42,7 +41,7 @@ interface DesiredCropperRef { getTransitions: () => CropperTransitions; getInteractions: () => CropperInteractions; hasInteractions: () => boolean; - resizeCoordinates: (directions: Partial, parameters: unknown) => void; + resizeCoordinates: (anchor: ResizeAnchor, directions: Partial, parameters: unknown) => void; resizeCoordinatesEnd: () => void; moveCoordinates: (directions: Partial) => void; moveCoordinatesEnd: () => void; @@ -134,9 +133,9 @@ export const CircleStencil = forwardRef( } }; - const onResize = (directions: ResizeDirections, options: ResizeOptions) => { + const onResize = (anchor: ResizeAnchor, directions: MoveDirections, options: ResizeOptions) => { if (cropper && resizable) { - cropper.resizeCoordinates(directions, options); + cropper.resizeCoordinates(anchor, directions, options); } }; @@ -167,6 +166,7 @@ export const CircleStencil = forwardRef( transitions={transitions} > CropperTransitions; getInteractions: () => CropperInteractions; hasInteractions: () => boolean; - resizeCoordinates: (directions: Partial, parameters: unknown) => void; + resizeCoordinates: (anchor: ResizeAnchor, directions: Partial, parameters: unknown) => void; resizeCoordinatesEnd: () => void; moveCoordinates: (directions: Partial) => void; moveCoordinatesEnd: () => void; @@ -151,9 +151,9 @@ export const RectangleStencil = forwardRef( } }; - const onResize = (directions: ResizeDirections, options: ResizeOptions) => { + const onResize = (anchor: ResizeAnchor, directions: MoveDirections, options: ResizeOptions) => { if (cropper && resizable) { - cropper.resizeCoordinates(directions, options); + cropper.resizeCoordinates(anchor, directions, options); } }; @@ -189,6 +189,7 @@ export const RectangleStencil = forwardRef( transitions={transitions} >