From 9ecd4c5d6fa553f1b69c383b9e129cd7fe1100cd Mon Sep 17 00:00:00 2001 From: shinkar94 Date: Thu, 26 Dec 2024 11:22:12 +0300 Subject: [PATCH 1/6] feat: add right-click mouse for brightness/contrast for context image --- .../canvas/grid-layout/canvas-layout.tsx | 6 +- .../views/context-image/context-image.tsx | 36 ++++- .../views/context-image/model/hooks/index.ts | 5 + .../model/hooks/useCanvasControl.tsx | 134 ++++++++++++++++++ .../canvas/views/context-image/model/index.ts | 5 + .../canvas/views/context-image/styles.scss | 19 ++- 6 files changed, 193 insertions(+), 12 deletions(-) create mode 100644 cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/index.ts create mode 100644 cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/useCanvasControl.tsx create mode 100644 cvat-ui/src/components/annotation-page/canvas/views/context-image/model/index.ts diff --git a/cvat-ui/src/components/annotation-page/canvas/grid-layout/canvas-layout.tsx b/cvat-ui/src/components/annotation-page/canvas/grid-layout/canvas-layout.tsx index 4c19933ab263..d9ab94092c38 100644 --- a/cvat-ui/src/components/annotation-page/canvas/grid-layout/canvas-layout.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/grid-layout/canvas-layout.tsx @@ -37,7 +37,7 @@ import defaultLayout, { ItemLayout, ViewType } from './canvas-layout.conf'; const ReactGridLayout = WidthProvider(RGL); -const ViewFabric = (itemLayout: ItemLayout): JSX.Element => { +const ViewFabric = (itemLayout: ItemLayout, fullscreenKey?: string): JSX.Element => { const { viewType: type, offset } = itemLayout; let component = null; @@ -49,7 +49,7 @@ const ViewFabric = (itemLayout: ItemLayout): JSX.Element => { component = ; break; case ViewType.RELATED_IMAGE: - component = ; + component = ; break; case ViewType.CANVAS_3D_FRONT: component = ; @@ -201,7 +201,7 @@ function CanvasLayout({ type }: { type?: DimensionType }): JSX.Element { window.dispatchEvent(new Event('resize')); }, [layoutConfig]); - const children = layoutConfig.map((value: ItemLayout) => ViewFabric(value)); + const children = layoutConfig.map((value: ItemLayout) => ViewFabric(value, fullscreenKey)); const layout = layoutConfig.map((value: ItemLayout) => ({ x: value.x, y: value.y, diff --git a/cvat-ui/src/components/annotation-page/canvas/views/context-image/context-image.tsx b/cvat-ui/src/components/annotation-page/canvas/views/context-image/context-image.tsx index f21e646b931f..0d0d9f50bcd5 100644 --- a/cvat-ui/src/components/annotation-page/canvas/views/context-image/context-image.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/views/context-image/context-image.tsx @@ -14,13 +14,15 @@ import { SettingOutlined } from '@ant-design/icons'; import CVATTooltop from 'components/common/cvat-tooltip'; import { CombinedState } from 'reducers'; import ContextImageSelector from './context-image-selector'; +import { useCanvasControl } from './model'; interface Props { offset: number[]; + fullscreenKey?: string; } function ContextImage(props: Props): JSX.Element { - const { offset } = props; + const { offset, fullscreenKey } = props; const defaultFrameOffset = (offset[0] || 0); const defaultContextImageOffset = (offset[1] || 0); @@ -38,6 +40,17 @@ function ContextImage(props: Props): JSX.Element { const [hasError, setHasError] = useState(false); const [showSelector, setShowSelector] = useState(false); + const { + canvasStyle, + handleContextMenu, + handleMouseDown, + handleMouseLeave, + handleMouseMove, + handleMouseUp, + handleZoomChange, + wrapperRef, + } = useCanvasControl(canvasRef, fullscreenKey); + useEffect(() => { let unmounted = false; const promise = job.frames.contextImage(frameIndex); @@ -82,7 +95,12 @@ function ContextImage(props: Props): JSX.Element { const contextImageName = Object.keys(contextImageData).sort()[contextImageOffset]; return ( -
+
{ relatedFiles > 1 && ( = Object.keys(contextImageData).length)) && No data } { fetching && } { - contextImageOffset < Object.keys(contextImageData).length && - + contextImageOffset < Object.keys(contextImageData).length && ( +
+ +
+ ) } { showSelector && ( , fullscreenKey: string | undefined) => { + const [zoomLevel, setZoomLevel] = useState(1); + + const [isDragging, setIsDragging] = useState(false); + const [dragOffset, setDragOffset] = useState<{ x: number; y: number }>({ + x: 0, + y: 0, + }); + const positionRef = useRef({ x: 0, y: 0 }); + const animationFrameRef = useRef(null); + const wrapperRef = useRef(null); + const [brightness, setBrightness] = useState(100); + const [contrast, setContrast] = useState(100); + + useEffect(() => { + positionRef.current = { x: 0, y: 0 }; + if (canvasRef.current) { + canvasRef.current.style.transform = `scale(${zoomLevel}) translate(0px, 0px)`; + } + }, [fullscreenKey]); + + const handleZoomChange = useCallback( + (event: React.WheelEvent) => { + const delta = event.deltaY; + const newZoomLevel = zoomLevel + delta * 0.001; + setZoomLevel(Math.max(0.1, Math.min(5, newZoomLevel))); + }, + [zoomLevel], + ); + + const handleMouseMove = useCallback( + (e: React.MouseEvent) => { + if (e.buttons === 1 && isDragging && canvasRef.current) { + const newPosition = { + x: (e.clientX - dragOffset.x) / zoomLevel, + y: (e.clientY - dragOffset.y) / zoomLevel, + }; + positionRef.current = newPosition; + if (animationFrameRef.current === null) { + animationFrameRef.current = requestAnimationFrame(() => { + if (canvasRef.current) { + canvasRef.current.style.transform = ` + scale(${zoomLevel}) translate(${positionRef.current.x}px, ${positionRef.current.y}px)`; + } + animationFrameRef.current = null; + }); + } + } else if (e.buttons === 2) { + const deltaX = e.movementX; + const deltaY = e.movementY; + + if (deltaY !== 0) { + setContrast((prevContrast) => Math.max(0, prevContrast + deltaY / 2)); + } + + if (deltaX !== 0) { + setBrightness((prevBrightness) => Math.max(0, prevBrightness + deltaX / 2)); + } + } + }, [zoomLevel, isDragging, dragOffset]); + + const handleMouseDown = useCallback( + (e: React.MouseEvent) => { + if (e.button === 0) { + setIsDragging(true); + setDragOffset({ + x: e.clientX - positionRef.current.x * zoomLevel, + y: e.clientY - positionRef.current.y * zoomLevel, + }); + if (canvasRef.current) { + canvasRef.current.style.cursor = 'grabbing'; + } + } + }, + [zoomLevel], + ); + + const canvasStyle = useMemo( + () => ({ + transform: `scale(${zoomLevel}) translate(${positionRef.current.x}px, ${positionRef.current.y}px)`, + transformOrigin: 'center', + cursor: isDragging ? 'grabbing' : 'grab', + transition: isDragging ? 'none' : 'transform 0.1s ease-out', + filter: `brightness(${brightness}%) contrast(${contrast}%)`, + }), + [zoomLevel, isDragging, brightness, contrast], + ); + + const handleMouseUp = useCallback(() => { + setIsDragging(false); + if (canvasRef.current) { + canvasRef.current.style.cursor = 'grab'; + } + if (animationFrameRef.current !== null) { + cancelAnimationFrame(animationFrameRef.current); + animationFrameRef.current = null; + } + }, []); + + const handleMouseLeave = useCallback(() => { + // setIsDragging(false); + // if (canvasRef.current) { + // canvasRef.current.style.cursor = 'grab'; + // } + }, []); + + const handleContextMenu = useCallback((e: React.MouseEvent) => { + e.preventDefault(); + }, []); + + return { + handleContextMenu, + handleZoomChange, + handleMouseLeave, + handleMouseUp, + wrapperRef, + canvasStyle, + handleMouseDown, + handleMouseMove, + }; +}; diff --git a/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/index.ts b/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/index.ts new file mode 100644 index 000000000000..f365921f4435 --- /dev/null +++ b/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/index.ts @@ -0,0 +1,5 @@ +// Copyright (C) 2024 CVAT.ai Corporation +// +// SPDX-License-Identifier: MIT + +export { useCanvasControl } from './hooks'; diff --git a/cvat-ui/src/components/annotation-page/canvas/views/context-image/styles.scss b/cvat-ui/src/components/annotation-page/canvas/views/context-image/styles.scss index 441388bb18fa..6f8e67de3565 100644 --- a/cvat-ui/src/components/annotation-page/canvas/views/context-image/styles.scss +++ b/cvat-ui/src/components/annotation-page/canvas/views/context-image/styles.scss @@ -9,6 +9,8 @@ height: 100%; display: flex; justify-content: center; + overflow: hidden; + border-radius: 10px 10px 0 0; > .ant-spin { position: absolute; @@ -62,13 +64,20 @@ } } - > canvas { - object-fit: contain; + .draggable-wrapper{ position: relative; - top: calc(50% + $grid-unit-size * 2); - transform: translateY(-50%); + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; width: 100%; - height: calc(100% - $grid-unit-size * 4); + + > canvas { + object-fit: contain; + position: absolute; + width: 100%; + height: 100%; + } } &:hover { From 0d9dcbafe191f4f4eabb4f34e94dadf8f322874c Mon Sep 17 00:00:00 2001 From: shinkar94 Date: Thu, 26 Dec 2024 17:02:38 +0300 Subject: [PATCH 2/6] feat: add right-click mouse functionality for brightness/contrast in first canvas card --- .../model/hooks/useCanvasControl.tsx | 6 +- .../canvas/canvas-context-menu.tsx | 63 +++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/useCanvasControl.tsx b/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/useCanvasControl.tsx index 466944a9ed54..e464d5edce5a 100644 --- a/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/useCanvasControl.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/useCanvasControl.tsx @@ -62,12 +62,14 @@ export const useCanvasControl = (canvasRef: RefObject, fullsc const deltaX = e.movementX; const deltaY = e.movementY; + const clamp = (value: number, min: number, max: number): number => Math.max(min, Math.min(max, value)); + if (deltaY !== 0) { - setContrast((prevContrast) => Math.max(0, prevContrast + deltaY / 2)); + setContrast((prevContrast) => clamp(prevContrast + deltaY / 2, 50, 200)); } if (deltaX !== 0) { - setBrightness((prevBrightness) => Math.max(0, prevBrightness + deltaX / 2)); + setBrightness((prevBrightness) => clamp(prevBrightness + deltaX / 2, 50, 200)); } } }, [zoomLevel, isDragging, dragOffset]); diff --git a/cvat-ui/src/containers/annotation-page/canvas/canvas-context-menu.tsx b/cvat-ui/src/containers/annotation-page/canvas/canvas-context-menu.tsx index a5e328150289..b3ce71281852 100644 --- a/cvat-ui/src/containers/annotation-page/canvas/canvas-context-menu.tsx +++ b/cvat-ui/src/containers/annotation-page/canvas/canvas-context-menu.tsx @@ -17,6 +17,7 @@ import { reviewActions, finishIssueAsync } from 'actions/review-actions'; import { ThunkDispatch } from 'utils/redux'; import { Canvas } from 'cvat-canvas-wrapper'; import { ObjectState, QualityConflict } from 'cvat-core-wrapper'; +import { changeBrightnessLevel, changeContrastLevel } from 'actions/settings-actions'; interface OwnProps { readonly?: boolean; @@ -36,6 +37,8 @@ interface StateToProps { workspace: Workspace; latestComments: string[]; activatedStateID: number | null; + brightnessLevel: number; + contrastLevel: number; } interface DispatchToProps { @@ -46,6 +49,8 @@ interface DispatchToProps { onStartIssue(position: number[]): void; openIssue(position: number[], message: string): void; onCopyObject(objectState: ObjectState): void; + onChangeBrightnessLevel(level: number): void; + onChangeContrastLevel(level: number): void; } function mapStateToProps(state: CombinedState): StateToProps { @@ -61,6 +66,12 @@ function mapStateToProps(state: CombinedState): StateToProps { }, workspace, }, + settings: { + player: { + brightnessLevel, + contrastLevel, + }, + }, review: { latestComments, frameConflicts }, } = state; @@ -90,6 +101,8 @@ function mapStateToProps(state: CombinedState): StateToProps { workspace, latestComments, frameConflicts, + brightnessLevel, + contrastLevel, }; } @@ -114,6 +127,12 @@ function mapDispatchToProps(dispatch: ThunkDispatch): DispatchToProps { dispatch(copyShape(objectState)); dispatch(pasteShapeAsync()); }, + onChangeBrightnessLevel(level: number): void { + dispatch(changeBrightnessLevel(level)); + }, + onChangeContrastLevel(level: number): void { + dispatch(changeContrastLevel(level)); + }, }; } @@ -269,6 +288,50 @@ class CanvasContextMenuContainer extends React.PureComponent { e.preventDefault(); } + if (e.buttons === 2) { + const targetElement = e.target as HTMLElement; + let isFirstCanvas = false; + const elementId = 'cvat_canvas_content'; + + const traverseParentNodes = (element: HTMLElement) => { + if (element.id && element.id === elementId) { + isFirstCanvas = true; + } + + const parentNode = element.parentNode as HTMLElement; + + if (!parentNode) { + return; + } + + if (parentNode) { + traverseParentNodes(parentNode); + } + }; + + traverseParentNodes(targetElement); + + if (isFirstCanvas) { + const { + onChangeBrightnessLevel, + onChangeContrastLevel, + brightnessLevel, + contrastLevel, + } = this.props; + + const clamp = (value: number, min: number, max: number): number => Math.max(min, Math.min(max, value)); + + const newBrightness = clamp(brightnessLevel + e.movementX / 2, 50, 200); + const newContrast = clamp(contrastLevel + e.movementY / 2, 50, 200); + + if (newBrightness !== brightnessLevel) { + onChangeBrightnessLevel(newBrightness); + } + if (newContrast !== contrastLevel) { + onChangeContrastLevel(newContrast); + } + } + } }; private updatePositionIfOutOfScreen(): void { From 3227ec10dd814f2a957b93cccd52240d2df593dd Mon Sep 17 00:00:00 2001 From: shinkar94 Date: Thu, 26 Dec 2024 18:02:50 +0300 Subject: [PATCH 3/6] feat: add reset color setting button --- .../canvas/views/context-image/context-image.tsx | 7 ++++++- .../context-image/model/hooks/useCanvasControl.tsx | 6 ++++++ .../canvas/views/context-image/styles.scss | 11 +++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/cvat-ui/src/components/annotation-page/canvas/views/context-image/context-image.tsx b/cvat-ui/src/components/annotation-page/canvas/views/context-image/context-image.tsx index 0d0d9f50bcd5..5e3f8507cdb6 100644 --- a/cvat-ui/src/components/annotation-page/canvas/views/context-image/context-image.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/views/context-image/context-image.tsx @@ -9,7 +9,7 @@ import PropTypes from 'prop-types'; import notification from 'antd/lib/notification'; import Spin from 'antd/lib/spin'; import Text from 'antd/lib/typography/Text'; -import { SettingOutlined } from '@ant-design/icons'; +import { ReloadOutlined, SettingOutlined } from '@ant-design/icons'; import CVATTooltop from 'components/common/cvat-tooltip'; import { CombinedState } from 'reducers'; @@ -48,6 +48,7 @@ function ContextImage(props: Props): JSX.Element { handleMouseMove, handleMouseUp, handleZoomChange, + resetColorSetting, wrapperRef, } = useCanvasControl(canvasRef, fullscreenKey); @@ -110,6 +111,10 @@ function ContextImage(props: Props): JSX.Element { }} /> )} +
{contextImageName} diff --git a/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/useCanvasControl.tsx b/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/useCanvasControl.tsx index e464d5edce5a..673d0bd023c4 100644 --- a/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/useCanvasControl.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/useCanvasControl.tsx @@ -32,6 +32,11 @@ export const useCanvasControl = (canvasRef: RefObject, fullsc } }, [fullscreenKey]); + const resetColorSetting = () => { + setBrightness(100); + setContrast(100); + }; + const handleZoomChange = useCallback( (event: React.WheelEvent) => { const delta = event.deltaY; @@ -132,5 +137,6 @@ export const useCanvasControl = (canvasRef: RefObject, fullsc canvasStyle, handleMouseDown, handleMouseMove, + resetColorSetting, }; }; diff --git a/cvat-ui/src/components/annotation-page/canvas/views/context-image/styles.scss b/cvat-ui/src/components/annotation-page/canvas/views/context-image/styles.scss index 6f8e67de3565..3fb51d847452 100644 --- a/cvat-ui/src/components/annotation-page/canvas/views/context-image/styles.scss +++ b/cvat-ui/src/components/annotation-page/canvas/views/context-image/styles.scss @@ -62,6 +62,17 @@ top: $grid-unit-size; right: $grid-unit-size; } + + > .cvat-context-image-reset-button{ + position: absolute; + opacity: 0.6; + top: $grid-unit-size * 1.1; + left: $grid-unit-size * 7; + + &:hover{ + opacity: 1; + } + } } .draggable-wrapper{ From 245e8fbe8d3d974e965b77e010b0c78875711c5d Mon Sep 17 00:00:00 2001 From: shinkar94 Date: Fri, 27 Dec 2024 08:42:35 +0300 Subject: [PATCH 4/6] fix: transfer the logic of contrast gamma change to the canvas-wrapper --- .../canvas/views/canvas2d/canvas-wrapper.tsx | 22 ++++++- .../canvas/canvas-context-menu.tsx | 63 ------------------- 2 files changed, 21 insertions(+), 64 deletions(-) diff --git a/cvat-ui/src/components/annotation-page/canvas/views/canvas2d/canvas-wrapper.tsx b/cvat-ui/src/components/annotation-page/canvas/views/canvas2d/canvas-wrapper.tsx index 322a345efea7..a8ef8712a533 100644 --- a/cvat-ui/src/components/annotation-page/canvas/views/canvas2d/canvas-wrapper.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/views/canvas2d/canvas-wrapper.tsx @@ -591,7 +591,7 @@ class CanvasWrapperComponent extends React.PureComponent { public componentWillUnmount(): void { const { canvasInstance } = this.props as { canvasInstance: Canvas }; - + canvasInstance.html().removeEventListener('mousemove', this.onCanvasMouseMove); canvasInstance.html().removeEventListener('mousedown', this.onCanvasMouseDown); canvasInstance.html().removeEventListener('click', this.onCanvasClicked); canvasInstance.html().removeEventListener('canvas.editstart', this.onCanvasEditStart); @@ -746,6 +746,25 @@ class CanvasWrapperComponent extends React.PureComponent { onStartIssue(points); }; + private onCanvasMouseMove = (e: MouseEvent): void => { + if (e.buttons === 2) { + const { + onChangeBrightnessLevel, + onChangeContrastLevel, + brightnessLevel, + contrastLevel, + } = this.props; + + const clamp = (value: number, min: number, max: number): number => Math.max(min, Math.min(max, value)); + + const newBrightness = clamp((brightnessLevel * 100) + (e.movementX / 2), 50, 200); + const newContrast = clamp((contrastLevel * 100) + (e.movementY / 2), 50, 200); + + onChangeBrightnessLevel(newBrightness); + onChangeContrastLevel(newContrast); + } + }; + private onCanvasMouseDown = (e: MouseEvent): void => { const { workspace, activatedStateID, onActivateObject } = this.props; @@ -1056,6 +1075,7 @@ class CanvasWrapperComponent extends React.PureComponent { { once: true }, ); + canvasInstance.html().addEventListener('mousemove', this.onCanvasMouseMove); canvasInstance.html().addEventListener('mousedown', this.onCanvasMouseDown); canvasInstance.html().addEventListener('click', this.onCanvasClicked); canvasInstance.html().addEventListener('canvas.editstart', this.onCanvasEditStart); diff --git a/cvat-ui/src/containers/annotation-page/canvas/canvas-context-menu.tsx b/cvat-ui/src/containers/annotation-page/canvas/canvas-context-menu.tsx index b3ce71281852..a5e328150289 100644 --- a/cvat-ui/src/containers/annotation-page/canvas/canvas-context-menu.tsx +++ b/cvat-ui/src/containers/annotation-page/canvas/canvas-context-menu.tsx @@ -17,7 +17,6 @@ import { reviewActions, finishIssueAsync } from 'actions/review-actions'; import { ThunkDispatch } from 'utils/redux'; import { Canvas } from 'cvat-canvas-wrapper'; import { ObjectState, QualityConflict } from 'cvat-core-wrapper'; -import { changeBrightnessLevel, changeContrastLevel } from 'actions/settings-actions'; interface OwnProps { readonly?: boolean; @@ -37,8 +36,6 @@ interface StateToProps { workspace: Workspace; latestComments: string[]; activatedStateID: number | null; - brightnessLevel: number; - contrastLevel: number; } interface DispatchToProps { @@ -49,8 +46,6 @@ interface DispatchToProps { onStartIssue(position: number[]): void; openIssue(position: number[], message: string): void; onCopyObject(objectState: ObjectState): void; - onChangeBrightnessLevel(level: number): void; - onChangeContrastLevel(level: number): void; } function mapStateToProps(state: CombinedState): StateToProps { @@ -66,12 +61,6 @@ function mapStateToProps(state: CombinedState): StateToProps { }, workspace, }, - settings: { - player: { - brightnessLevel, - contrastLevel, - }, - }, review: { latestComments, frameConflicts }, } = state; @@ -101,8 +90,6 @@ function mapStateToProps(state: CombinedState): StateToProps { workspace, latestComments, frameConflicts, - brightnessLevel, - contrastLevel, }; } @@ -127,12 +114,6 @@ function mapDispatchToProps(dispatch: ThunkDispatch): DispatchToProps { dispatch(copyShape(objectState)); dispatch(pasteShapeAsync()); }, - onChangeBrightnessLevel(level: number): void { - dispatch(changeBrightnessLevel(level)); - }, - onChangeContrastLevel(level: number): void { - dispatch(changeContrastLevel(level)); - }, }; } @@ -288,50 +269,6 @@ class CanvasContextMenuContainer extends React.PureComponent { e.preventDefault(); } - if (e.buttons === 2) { - const targetElement = e.target as HTMLElement; - let isFirstCanvas = false; - const elementId = 'cvat_canvas_content'; - - const traverseParentNodes = (element: HTMLElement) => { - if (element.id && element.id === elementId) { - isFirstCanvas = true; - } - - const parentNode = element.parentNode as HTMLElement; - - if (!parentNode) { - return; - } - - if (parentNode) { - traverseParentNodes(parentNode); - } - }; - - traverseParentNodes(targetElement); - - if (isFirstCanvas) { - const { - onChangeBrightnessLevel, - onChangeContrastLevel, - brightnessLevel, - contrastLevel, - } = this.props; - - const clamp = (value: number, min: number, max: number): number => Math.max(min, Math.min(max, value)); - - const newBrightness = clamp(brightnessLevel + e.movementX / 2, 50, 200); - const newContrast = clamp(contrastLevel + e.movementY / 2, 50, 200); - - if (newBrightness !== brightnessLevel) { - onChangeBrightnessLevel(newBrightness); - } - if (newContrast !== contrastLevel) { - onChangeContrastLevel(newContrast); - } - } - } }; private updatePositionIfOutOfScreen(): void { From 0eea71c6eb86b80ef6707aa2fc44c083320dbcd0 Mon Sep 17 00:00:00 2001 From: shinkar94 <71019041+shinkar94@users.noreply.github.com> Date: Fri, 27 Dec 2024 10:29:40 +0300 Subject: [PATCH 5/6] Update cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/useCanvasControl.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../model/hooks/useCanvasControl.tsx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/useCanvasControl.tsx b/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/useCanvasControl.tsx index 673d0bd023c4..b8c23898a6e3 100644 --- a/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/useCanvasControl.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/useCanvasControl.tsx @@ -54,15 +54,16 @@ export const useCanvasControl = (canvasRef: RefObject, fullsc y: (e.clientY - dragOffset.y) / zoomLevel, }; positionRef.current = newPosition; - if (animationFrameRef.current === null) { - animationFrameRef.current = requestAnimationFrame(() => { - if (canvasRef.current) { - canvasRef.current.style.transform = ` - scale(${zoomLevel}) translate(${positionRef.current.x}px, ${positionRef.current.y}px)`; - } - animationFrameRef.current = null; - }); + if (animationFrameRef.current !== null) { + cancelAnimationFrame(animationFrameRef.current); } + animationFrameRef.current = requestAnimationFrame(() => { + if (canvasRef.current) { + canvasRef.current.style.transform = ` + scale(${zoomLevel}) translate(${positionRef.current.x}px, ${positionRef.current.y}px)`; + } + animationFrameRef.current = null; + }); } else if (e.buttons === 2) { const deltaX = e.movementX; const deltaY = e.movementY; From e48d822c7759828a50ff6cc19ccb696ba641cac8 Mon Sep 17 00:00:00 2001 From: shinkar94 Date: Fri, 27 Dec 2024 15:09:00 +0300 Subject: [PATCH 6/6] fix: adjusted zooming behavior --- .../model/hooks/useCanvasControl.tsx | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/useCanvasControl.tsx b/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/useCanvasControl.tsx index 673d0bd023c4..15a30bb91e18 100644 --- a/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/useCanvasControl.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/views/context-image/model/hooks/useCanvasControl.tsx @@ -40,8 +40,8 @@ export const useCanvasControl = (canvasRef: RefObject, fullsc const handleZoomChange = useCallback( (event: React.WheelEvent) => { const delta = event.deltaY; - const newZoomLevel = zoomLevel + delta * 0.001; - setZoomLevel(Math.max(0.1, Math.min(5, newZoomLevel))); + const newZoomLevel = zoomLevel - delta * 0.001; + setZoomLevel(Math.max(0.5, Math.min(5, newZoomLevel))); }, [zoomLevel], ); @@ -54,15 +54,16 @@ export const useCanvasControl = (canvasRef: RefObject, fullsc y: (e.clientY - dragOffset.y) / zoomLevel, }; positionRef.current = newPosition; - if (animationFrameRef.current === null) { - animationFrameRef.current = requestAnimationFrame(() => { - if (canvasRef.current) { - canvasRef.current.style.transform = ` - scale(${zoomLevel}) translate(${positionRef.current.x}px, ${positionRef.current.y}px)`; - } - animationFrameRef.current = null; - }); + if (animationFrameRef.current !== null) { + cancelAnimationFrame(animationFrameRef.current); } + animationFrameRef.current = requestAnimationFrame(() => { + if (canvasRef.current) { + canvasRef.current.style.transform = ` + scale(${zoomLevel}) translate(${positionRef.current.x}px, ${positionRef.current.y}px)`; + } + animationFrameRef.current = null; + }); } else if (e.buttons === 2) { const deltaX = e.movementX; const deltaY = e.movementY;