Skip to content

Commit a880712

Browse files
feat (feedback) - Make cropped screenshot area draggable (#13071)
- [x] If you've added code that should be tested, please add tests. - [x] Ensure your code lints and the test suite passes (`yarn lint`) & (`yarn test`).fe This addition adds an intuitive way to resize screenshots in the user feedback widget. The draggable area is bound by the canvas box (so it won't overflow) and is only draggable when cropped (when confirm/cancel buttons present) https://github.com/user-attachments/assets/7ddff991-a791-4d41-a2ad-b278e1ab4950
1 parent 5c03ac5 commit a880712

File tree

1 file changed

+58
-1
lines changed

1 file changed

+58
-1
lines changed

packages/feedback/src/screenshot/components/ScreenshotEditor.tsx

+58-1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export function ScreenshotEditorFactory({
8282
const croppingRef = hooks.useRef<HTMLCanvasElement>(null);
8383
const [croppingRect, setCroppingRect] = hooks.useState<Box>({ startX: 0, startY: 0, endX: 0, endY: 0 });
8484
const [confirmCrop, setConfirmCrop] = hooks.useState(false);
85+
const [isResizing, setIsResizing] = hooks.useState(false);
8586

8687
hooks.useEffect(() => {
8788
WINDOW.addEventListener('resize', resizeCropper, false);
@@ -141,11 +142,13 @@ export function ScreenshotEditorFactory({
141142

142143
function onGrabButton(e: Event, corner: string): void {
143144
setConfirmCrop(false);
145+
setIsResizing(true);
144146
const handleMouseMove = makeHandleMouseMove(corner);
145147
const handleMouseUp = (): void => {
146148
DOCUMENT.removeEventListener('mousemove', handleMouseMove);
147149
DOCUMENT.removeEventListener('mouseup', handleMouseUp);
148150
setConfirmCrop(true);
151+
setIsResizing(false);
149152
};
150153

151154
DOCUMENT.addEventListener('mouseup', handleMouseUp);
@@ -194,6 +197,56 @@ export function ScreenshotEditorFactory({
194197
};
195198
}, []);
196199

200+
// DRAGGING FUNCTIONALITY.
201+
const initialPositionRef = hooks.useRef({ initialX: 0, initialY: 0 });
202+
203+
function onDragStart(e: MouseEvent): void {
204+
if (isResizing) return;
205+
206+
initialPositionRef.current = { initialX: e.clientX, initialY: e.clientY };
207+
208+
const handleMouseMove = (moveEvent: MouseEvent): void => {
209+
const cropCanvas = croppingRef.current;
210+
if (!cropCanvas) return;
211+
212+
const deltaX = moveEvent.clientX - initialPositionRef.current.initialX;
213+
const deltaY = moveEvent.clientY - initialPositionRef.current.initialY;
214+
215+
setCroppingRect(prev => {
216+
// Math.max stops it from going outside of the borders
217+
const newStartX = Math.max(
218+
0,
219+
Math.min(prev.startX + deltaX, cropCanvas.width / DPI - (prev.endX - prev.startX)),
220+
);
221+
const newStartY = Math.max(
222+
0,
223+
Math.min(prev.startY + deltaY, cropCanvas.height / DPI - (prev.endY - prev.startY)),
224+
);
225+
// Don't want to change size, just position
226+
const newEndX = newStartX + (prev.endX - prev.startX);
227+
const newEndY = newStartY + (prev.endY - prev.startY);
228+
229+
initialPositionRef.current.initialX = moveEvent.clientX;
230+
initialPositionRef.current.initialY = moveEvent.clientY;
231+
232+
return {
233+
startX: newStartX,
234+
startY: newStartY,
235+
endX: newEndX,
236+
endY: newEndY,
237+
};
238+
});
239+
};
240+
241+
const handleMouseUp = (): void => {
242+
DOCUMENT.removeEventListener('mousemove', handleMouseMove);
243+
DOCUMENT.removeEventListener('mouseup', handleMouseUp);
244+
};
245+
246+
DOCUMENT.addEventListener('mousemove', handleMouseMove);
247+
DOCUMENT.addEventListener('mouseup', handleMouseUp);
248+
}
249+
197250
function submit(): void {
198251
const cutoutCanvas = DOCUMENT.createElement('canvas');
199252
const imageBox = constructRect(getContainedSize(imageBuffer));
@@ -263,7 +316,11 @@ export function ScreenshotEditorFactory({
263316
<style dangerouslySetInnerHTML={styles} />
264317
<div class="editor__canvas-container" ref={canvasContainerRef}>
265318
<div class="editor__crop-container" style={{ position: 'absolute', zIndex: 1 }} ref={cropContainerRef}>
266-
<canvas style={{ position: 'absolute' }} ref={croppingRef}></canvas>
319+
<canvas
320+
onMouseDown={onDragStart}
321+
style={{ position: 'absolute', cursor: confirmCrop ? 'move' : 'auto' }}
322+
ref={croppingRef}
323+
></canvas>
267324
<CropCorner
268325
left={croppingRect.startX - CROP_BUTTON_BORDER}
269326
top={croppingRect.startY - CROP_BUTTON_BORDER}

0 commit comments

Comments
 (0)