|
1 |
| -import { Flex, Image, Text } from '@invoke-ai/ui-library'; |
| 1 | +import { Box, Flex, Image } from '@invoke-ai/ui-library'; |
2 | 2 | import { useAppSelector } from 'app/store/storeHooks';
|
| 3 | +import { useBoolean } from 'common/hooks/useBoolean'; |
3 | 4 | import { preventDefault } from 'common/util/stopPropagation';
|
4 |
| -import { DROP_SHADOW } from 'features/gallery/components/ImageViewer/useImageViewer'; |
5 |
| -import { memo, useCallback, useState } from 'react'; |
6 |
| -import { useTranslation } from 'react-i18next'; |
7 |
| -import type { ImageDTO } from 'services/api/types'; |
| 5 | +import type { Dimensions } from 'features/canvas/store/canvasTypes'; |
| 6 | +import { STAGE_BG_DATAURL } from 'features/controlLayers/util/renderers'; |
| 7 | +import { ImageComparisonLabel } from 'features/gallery/components/ImageViewer/ImageComparisonLabel'; |
| 8 | +import { memo, useMemo, useRef } from 'react'; |
8 | 9 |
|
9 |
| -type Props = { |
10 |
| - /** |
11 |
| - * The first image to compare |
12 |
| - */ |
13 |
| - firstImage: ImageDTO; |
14 |
| - /** |
15 |
| - * The second image to compare |
16 |
| - */ |
17 |
| - secondImage: ImageDTO; |
18 |
| -}; |
| 10 | +import type { ComparisonProps } from './common'; |
| 11 | +import { fitDimsToContainer, getSecondImageDims } from './common'; |
19 | 12 |
|
20 |
| -export const ImageComparisonHover = memo(({ firstImage, secondImage }: Props) => { |
21 |
| - const { t } = useTranslation(); |
| 13 | +export const ImageComparisonHover = memo(({ firstImage, secondImage, containerDims }: ComparisonProps) => { |
22 | 14 | const comparisonFit = useAppSelector((s) => s.gallery.comparisonFit);
|
23 |
| - const [isMouseOver, setIsMouseOver] = useState(false); |
24 |
| - const onMouseOver = useCallback(() => { |
25 |
| - setIsMouseOver(true); |
26 |
| - }, []); |
27 |
| - const onMouseOut = useCallback(() => { |
28 |
| - setIsMouseOver(false); |
29 |
| - }, []); |
| 15 | + const imageContainerRef = useRef<HTMLDivElement>(null); |
| 16 | + const mouseOver = useBoolean(false); |
| 17 | + const fittedDims = useMemo<Dimensions>( |
| 18 | + () => fitDimsToContainer(containerDims, firstImage), |
| 19 | + [containerDims, firstImage] |
| 20 | + ); |
| 21 | + const compareImageDims = useMemo<Dimensions>( |
| 22 | + () => getSecondImageDims(comparisonFit, fittedDims, firstImage, secondImage), |
| 23 | + [comparisonFit, fittedDims, firstImage, secondImage] |
| 24 | + ); |
30 | 25 | return (
|
31 | 26 | <Flex w="full" h="full" maxW="full" maxH="full" position="relative" alignItems="center" justifyContent="center">
|
32 |
| - <Flex position="absolute" maxW="full" maxH="full" aspectRatio={firstImage.width / firstImage.height}> |
33 |
| - <Image |
34 |
| - id="image-comparison-first-image" |
35 |
| - w={firstImage.width} |
36 |
| - h={firstImage.height} |
| 27 | + <Flex |
| 28 | + id="image-comparison-wrapper" |
| 29 | + w="full" |
| 30 | + h="full" |
| 31 | + maxW="full" |
| 32 | + maxH="full" |
| 33 | + position="absolute" |
| 34 | + alignItems="center" |
| 35 | + justifyContent="center" |
| 36 | + > |
| 37 | + <Box |
| 38 | + ref={imageContainerRef} |
| 39 | + position="relative" |
| 40 | + id="image-comparison-hover-image-container" |
| 41 | + w={fittedDims.width} |
| 42 | + h={fittedDims.height} |
37 | 43 | maxW="full"
|
38 | 44 | maxH="full"
|
39 |
| - src={firstImage.image_url} |
40 |
| - fallbackSrc={firstImage.thumbnail_url} |
41 |
| - objectFit="contain" |
42 |
| - /> |
43 |
| - <Text |
44 |
| - position="absolute" |
45 |
| - bottom={4} |
46 |
| - insetInlineStart={4} |
47 |
| - textOverflow="clip" |
48 |
| - whiteSpace="nowrap" |
49 |
| - filter={DROP_SHADOW} |
50 |
| - color="base.50" |
51 |
| - > |
52 |
| - {t('gallery.viewerImage')} |
53 |
| - </Text> |
54 |
| - <Flex |
55 |
| - position="absolute" |
56 |
| - top={0} |
57 |
| - right={0} |
58 |
| - bottom={0} |
59 |
| - left={0} |
60 |
| - opacity={isMouseOver ? 1 : 0} |
61 |
| - transitionDuration="0.2s" |
62 |
| - transitionProperty="common" |
| 45 | + userSelect="none" |
| 46 | + overflow="hidden" |
| 47 | + borderRadius="base" |
63 | 48 | >
|
64 | 49 | <Image
|
65 |
| - id="image-comparison-second-image" |
66 |
| - w={comparisonFit === 'fill' ? 'full' : secondImage.width} |
67 |
| - h={comparisonFit === 'fill' ? 'full' : secondImage.height} |
68 |
| - maxW={comparisonFit === 'contain' ? 'full' : undefined} |
69 |
| - maxH={comparisonFit === 'contain' ? 'full' : undefined} |
70 |
| - src={secondImage.image_url} |
71 |
| - fallbackSrc={secondImage.thumbnail_url} |
72 |
| - objectFit={comparisonFit} |
| 50 | + id="image-comparison-hover-first-image" |
| 51 | + src={firstImage.image_url} |
| 52 | + fallbackSrc={firstImage.thumbnail_url} |
| 53 | + w={fittedDims.width} |
| 54 | + h={fittedDims.height} |
| 55 | + maxW="full" |
| 56 | + maxH="full" |
| 57 | + objectFit="cover" |
73 | 58 | objectPosition="top left"
|
74 | 59 | />
|
75 |
| - <Text |
| 60 | + <ImageComparisonLabel type="first" opacity={mouseOver.isTrue ? 0 : 1} /> |
| 61 | + |
| 62 | + <Box |
| 63 | + id="image-comparison-hover-second-image-container" |
76 | 64 | position="absolute"
|
77 |
| - bottom={4} |
78 |
| - insetInlineStart={4} |
79 |
| - textOverflow="clip" |
80 |
| - whiteSpace="nowrap" |
81 |
| - filter={DROP_SHADOW} |
82 |
| - color="base.50" |
| 65 | + top={0} |
| 66 | + left={0} |
| 67 | + right={0} |
| 68 | + bottom={0} |
| 69 | + overflow="hidden" |
| 70 | + opacity={mouseOver.isTrue ? 1 : 0} |
| 71 | + transitionDuration="0.2s" |
| 72 | + transitionProperty="common" |
83 | 73 | >
|
84 |
| - {t('gallery.compareImage')} |
85 |
| - </Text> |
86 |
| - </Flex> |
87 |
| - <Flex |
88 |
| - id="image-comparison-interaction-overlay" |
89 |
| - position="absolute" |
90 |
| - top={0} |
91 |
| - right={0} |
92 |
| - bottom={0} |
93 |
| - left={0} |
94 |
| - onMouseOver={onMouseOver} |
95 |
| - onMouseOut={onMouseOut} |
96 |
| - onContextMenu={preventDefault} |
97 |
| - userSelect="none" |
98 |
| - /> |
| 74 | + <Box |
| 75 | + id="image-comparison-hover-bg" |
| 76 | + position="absolute" |
| 77 | + top={0} |
| 78 | + left={0} |
| 79 | + right={0} |
| 80 | + bottom={0} |
| 81 | + backgroundImage={STAGE_BG_DATAURL} |
| 82 | + backgroundRepeat="repeat" |
| 83 | + opacity={0.2} |
| 84 | + /> |
| 85 | + <Image |
| 86 | + position="relative" |
| 87 | + id="image-comparison-hover-second-image" |
| 88 | + src={secondImage.image_url} |
| 89 | + fallbackSrc={secondImage.thumbnail_url} |
| 90 | + w={compareImageDims.width} |
| 91 | + h={compareImageDims.height} |
| 92 | + maxW={fittedDims.width} |
| 93 | + maxH={fittedDims.height} |
| 94 | + objectFit={comparisonFit} |
| 95 | + objectPosition="top left" |
| 96 | + /> |
| 97 | + <ImageComparisonLabel type="second" opacity={mouseOver.isTrue ? 1 : 0} /> |
| 98 | + </Box> |
| 99 | + <Box |
| 100 | + id="image-comparison-hover-interaction-overlay" |
| 101 | + position="absolute" |
| 102 | + top={0} |
| 103 | + right={0} |
| 104 | + bottom={0} |
| 105 | + left={0} |
| 106 | + onMouseOver={mouseOver.setTrue} |
| 107 | + onMouseOut={mouseOver.setFalse} |
| 108 | + onContextMenu={preventDefault} |
| 109 | + userSelect="none" |
| 110 | + /> |
| 111 | + </Box> |
99 | 112 | </Flex>
|
100 | 113 | </Flex>
|
101 | 114 | );
|
|
0 commit comments