Skip to content

Commit

Permalink
Merge pull request #3011 from the-deep/fix/screenshot-blur-issue
Browse files Browse the repository at this point in the history
Fix screenshot blur issue
  • Loading branch information
subinasr authored Dec 18, 2024
2 parents eb50884 + 1523e86 commit 604cf08
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 91 deletions.
181 changes: 96 additions & 85 deletions app/components/Screenshot/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { _cs } from '@togglecorp/fujs';

import { extensionChromeUrl } from '#base/configs/env';

import { getScreenshot } from '#utils/browserExtension';
import { getScreenshot as getScreenshotFromExtension } from '#utils/browserExtension';

import styles from './styles.css';

Expand Down Expand Up @@ -54,13 +54,34 @@ function getCroppedImage(
return croppedImage;
}

async function getImageFromUrl(imgUrl: string) {
const promise = new Promise<HTMLImageElement>((resolve) => {
const image = new Image();
image.onload = () => {
resolve(image);
};
image.src = imgUrl;
});

return promise;
}

interface Props {
className?: string;
onCapture: (image: string | undefined) => void;
onCaptureError: (errorComponent: React.ReactNode) => void;
onCancel: () => void;
}

interface Rect {
top: number;
right: number;
bottom: number;
left: number;
width: number;
height: number;
}

function Screenshot(props: Props) {
const {
className,
Expand All @@ -70,16 +91,16 @@ function Screenshot(props: Props) {
} = props;

const firstResizeRef = React.useRef(true);
const [imageProps, setImageProps] = React.useState<{
image: HTMLImageElement | undefined;
offsetX: number;
offsetY: number;
width: number;
height: number
}>({
image: undefined,
offsetX: 0,
offsetY: 0,
const brushContainerRef = React.useRef<SVGGElement>(null);
const canvasRef = React.useRef<HTMLCanvasElement>(null);
const containerRef = React.useRef<HTMLDivElement>(null);

const [screenshotImage, setScreenshotImage] = React.useState<HTMLImageElement | null>(null);
const [containerRect, setContainerRect] = React.useState<Rect>({
top: 0,
right: 0,
bottom: 0,
left: 0,
width: 0,
height: 0,
});
Expand All @@ -89,95 +110,89 @@ function Screenshot(props: Props) {
onCancel();
}

const rect = containerRef.current?.getBoundingClientRect();
if (rect) {
const scale = window.devicePixelRatio;

setContainerRect({
top: Math.round(rect.top * scale),
right: Math.round(rect.right * scale),
bottom: Math.round(rect.bottom * scale),
left: Math.round(rect.left * scale),
width: Math.round(rect.width * scale),
height: Math.round(rect.height * scale),
});
}

firstResizeRef.current = false;
}, [onCancel]);

const brushContainerRef = React.useRef<SVGGElement>(null);
const svgRef = React.useRef<SVGSVGElement>(null);
const canvasRef = React.useRef<HTMLCanvasElement>(null);

const handleBrush = React.useCallback((event: { selection: number[][] }) => {
if (!onCapture) {
return;
}

const r = event.selection;
if (!canvasRef.current || !imageProps.image || !r) {
if (!canvasRef.current || !screenshotImage || !r) {
onCapture(undefined);
return;
}

const croppedImage = getCroppedImage(
canvasRef.current,
imageProps.image,
screenshotImage,
r[0][0],
r[0][1],
r[1][0],
r[1][1],
);
onCapture(croppedImage);
}, [onCapture, imageProps.image]);
}, [onCapture, screenshotImage]);

React.useEffect(() => {
getScreenshot().then((result) => {
if (svgRef.current) {
const scale = window.devicePixelRatio;

const rect = svgRef.current.getBoundingClientRect();
const offsetX = rect.left * scale;
const offsetY = rect.top * scale;
const width = rect.width * scale;
const height = rect.height * scale;

const image = new Image();
image.onload = () => {
setImageProps({
image,
offsetX,
offsetY,
width,
height,
});
};

image.src = result.image;
async function getScreenshot() {
try {
const screenshotResult = await getScreenshotFromExtension();
const image = await getImageFromUrl(screenshotResult.image);
setScreenshotImage(image);
} catch {
// FIXME: use strings
const captureError = (
<div className={styles.error}>
In order to use the screenshot functionality,
you must have the Chrome extension installed.
You can download it from the chrome web store
<a
className={styles.link}
// NOTE: the `deep` username is hardcoded here
href={extensionChromeUrl}
target="_blank"
rel="noopener noreferrer"
>
here.
</a>
</div>
);
if (onCaptureError) {
onCaptureError(captureError);
}
}
}).catch(() => {
// FIXME: use strings
const captureError = (
<div className={styles.error}>
In order to use the screenshot functionality,
you must have the Chrome extension installed.
You can download it from the chrome web store
<a
className={styles.link}
// NOTE: the `deep` username is hardcoded here
href={extensionChromeUrl}
target="_blank"
rel="noopener noreferrer"
>
here.
</a>
</div>
);
if (onCaptureError) {
onCaptureError(captureError);
}
});
}

getScreenshot();
}, [onCaptureError]);

if (!svgRef.current || !brushContainerRef.current) {
React.useEffect(() => {
if (!brushContainerRef.current || !containerRect || !screenshotImage) {
return undefined;
}

const scale = window.devicePixelRatio;
const rect = svgRef.current.getBoundingClientRect();

const container = select(brushContainerRef.current);
const brushGroup = container.append('g').attr('class', 'brush');
const brush = d3Brush()
.extent([
[rect.left * scale, rect.top * scale],
[rect.right * scale, rect.bottom * scale],
[containerRect.left, containerRect.top],
[containerRect.right, containerRect.bottom],
])
.on('end', handleBrush);
brushGroup.call(brush);
Expand All @@ -187,25 +202,21 @@ function Screenshot(props: Props) {
brushGroup.remove();
}
};
}, [handleBrush, onCaptureError]);

const {
image,
offsetX,
offsetY,
width,
height,
} = imageProps;
}, [containerRect, handleBrush, onCaptureError, screenshotImage]);

return (
<div className={_cs(styles.screenshot, className)}>
<svg
ref={svgRef}
viewBox={`${offsetX} ${offsetY} ${width} ${height}`}
>
{image && <image href={image.src} /> }
<g ref={brushContainerRef} />
</svg>
<div
className={_cs(styles.screenshot, className)}
ref={containerRef}
>
{containerRect.width && containerRect.height && (
<svg
viewBox={`${containerRect.left} ${containerRect.top} ${containerRect.width} ${containerRect.height}`}
>
{screenshotImage && <image href={screenshotImage.src} /> }
<g ref={brushContainerRef} />
</svg>
)}
<canvas
ref={canvasRef}
width={0}
Expand Down
9 changes: 3 additions & 6 deletions app/components/Screenshot/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,17 @@
}

.screenshot {
display: flex;
position: absolute;
z-index: 10;
width: 100%;
height: 100%;
user-select: none;

svg {
flex-grow: 1;
position: absolute;
cursor: crosshair;

image {
flex-grow: 1;
}
width: 100%;
height: 100%;

:global {
.brush {
Expand Down

0 comments on commit 604cf08

Please sign in to comment.