From 04dcd461b0700f1da9519ce12912f1f2fc39e834 Mon Sep 17 00:00:00 2001 From: cyrus25 Date: Sat, 3 Sep 2022 00:47:21 +0530 Subject: [PATCH 1/3] feat: Added objectFit prop for Image --- Libraries/Image/Image.android.js | 31 ++++++++- Libraries/Image/Image.ios.js | 27 +++++++- Libraries/Image/ImageObjectFit.js | 32 ++++++++++ Libraries/Image/ImageProps.js | 6 ++ Libraries/StyleSheet/StyleSheetTypes.js | 2 + .../js/examples/Image/ImageExample.js | 64 +++++++++++++++++++ 6 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 Libraries/Image/ImageObjectFit.js diff --git a/Libraries/Image/Image.android.js b/Libraries/Image/Image.android.js index eac8a56c63eba7..03d25305361b3a 100644 --- a/Libraries/Image/Image.android.js +++ b/Libraries/Image/Image.android.js @@ -22,6 +22,8 @@ import NativeImageLoaderAndroid from './NativeImageLoaderAndroid'; import TextInlineImageNativeComponent from './TextInlineImageNativeComponent'; import type {ImageProps as ImagePropsType} from './ImageProps'; +import type {ImageObjectFit} from './ImageObjectFit'; +import type {ImageResizeMode} from './ImageResizeMode'; import type {RootTag} from '../Types/RootTagTypes'; let _requestId = 1; @@ -51,6 +53,21 @@ function getSize( ); } +function getResizeModeEquivalentFromObjectFit( + objectFit?: ?ImageObjectFit, +): ImageResizeMode { + if (!objectFit) { + return null; + } + const objectFitMappingToResizeMode = { + contain: 'contain', + cover: 'cover', + fill: 'stretch', + 'scale-down': 'contain', + }; + return objectFitMappingToResizeMode[objectFit] ?? null; +} + /** * Retrieve the width and height (in pixels) of an image prior to displaying it * with the ability to provide the headers for the request @@ -188,6 +205,11 @@ const BaseImage = (props: ImagePropsType, forwardedRef) => { ref: forwardedRef, }; + const updatedResizeMode = + getResizeModeEquivalentFromObjectFit(props.objectFit) || + getResizeModeEquivalentFromObjectFit(style.objectFit) || + props.resizeMode; + return ( {analyticTag => { @@ -206,7 +228,7 @@ const BaseImage = (props: ImagePropsType, forwardedRef) => { return ( { ); } - return ; + return ( + + ); }} ); diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js index f4c5ca3ca421a5..50566d3d09b2b9 100644 --- a/Libraries/Image/Image.ios.js +++ b/Libraries/Image/Image.ios.js @@ -17,6 +17,8 @@ import flattenStyle from '../StyleSheet/flattenStyle'; import resolveAssetSource from './resolveAssetSource'; import type {ImageProps as ImagePropsType} from './ImageProps'; +import type {ImageObjectFit} from './ImageObjectFit'; +import type {ImageResizeMode} from './ImageResizeMode'; import type {ImageStyleProp} from '../StyleSheet/StyleSheet'; import NativeImageLoaderIOS from './NativeImageLoaderIOS'; @@ -39,6 +41,21 @@ function getSize( ); } +function getResizeModeEquivalentFromObjectFit( + objectFit?: ?ImageObjectFit, +): ?ImageResizeMode { + if (!objectFit) { + return null; + } + const objectFitMappingToResizeMode = { + contain: 'contain', + cover: 'cover', + fill: 'stretch', + 'scale-down': 'contain', + }; + return objectFitMappingToResizeMode[objectFit] ?? null; +} + function getSizeWithHeaders( uri: string, headers: {[string]: string, ...}, @@ -128,6 +145,14 @@ const BaseImage = (props: ImagePropsType, forwardedRef) => { // $FlowFixMe[prop-missing] const resizeMode = props.resizeMode || style.resizeMode || 'cover'; + + const updatedResizeMode = + // $FlowFixMe[prop-missing] + getResizeModeEquivalentFromObjectFit(props.objectFit) || + // $FlowFixMe[prop-missing] + getResizeModeEquivalentFromObjectFit(style.objectFit) || + resizeMode; + // $FlowFixMe[prop-missing] const tintColor = style.tintColor; @@ -151,7 +176,7 @@ const BaseImage = (props: ImagePropsType, forwardedRef) => { {...props} ref={forwardedRef} style={style} - resizeMode={resizeMode} + resizeMode={updatedResizeMode} tintColor={tintColor} source={sources} internal_analyticTag={analyticTag} diff --git a/Libraries/Image/ImageObjectFit.js b/Libraries/Image/ImageObjectFit.js new file mode 100644 index 00000000000000..4d8cf6f8abfeba --- /dev/null +++ b/Libraries/Image/ImageObjectFit.js @@ -0,0 +1,32 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +'use strict'; + +/** + * ImageObjectFit partially equals to ImageResizeMode, defines valid values for different image resizing modes set + * via the `objectFit` style property on ``. + */ +export type ImageObjectFit = + // Resize such that the entire area of the view is covered by the image, + // potentially clipping parts of the image. + | 'cover' + + // Resize by stretching it to fill the entire frame of the view without + // clipping. This may change the aspect ratio of the image, distorting it. + | 'fill' + + // Resize such that it will be completely visible, contained within the frame + // of the View. + | 'scale-down' + + // Resize such that it will be completely visible, contained within the frame + // of the View. + | 'contain'; diff --git a/Libraries/Image/ImageProps.js b/Libraries/Image/ImageProps.js index 6bb36fedb5d4aa..63de6fb76c08e7 100644 --- a/Libraries/Image/ImageProps.js +++ b/Libraries/Image/ImageProps.js @@ -162,6 +162,12 @@ export type ImageProps = {| */ resizeMode?: ?('cover' | 'contain' | 'stretch' | 'repeat' | 'center'), + /** + * Partially equivalent to resizeMode, determines how to resize the image when the frame doesn't match the raw + * image dimensions. + */ + objectFit?: ?('cover' | 'fill' | 'scale-down' | 'contain'), + /** * A unique identifier for this element to be used in UI Automation * testing scripts. diff --git a/Libraries/StyleSheet/StyleSheetTypes.js b/Libraries/StyleSheet/StyleSheetTypes.js index 0ff1d26f64dd29..db25ff483e6f79 100644 --- a/Libraries/StyleSheet/StyleSheetTypes.js +++ b/Libraries/StyleSheet/StyleSheetTypes.js @@ -642,6 +642,7 @@ export type ____TextStyle_Internal = $ReadOnly<{ export type ____ImageStyle_InternalCore = $ReadOnly<{ ...$Exact<____ViewStyle_Internal>, resizeMode?: 'contain' | 'cover' | 'stretch' | 'center' | 'repeat', + objectFit?: 'cover' | 'fill' | 'scale-down' | 'contain', tintColor?: ____ColorValue_Internal, overlayColor?: string, }>; @@ -654,6 +655,7 @@ export type ____ImageStyle_Internal = $ReadOnly<{ export type ____DangerouslyImpreciseStyle_InternalCore = $ReadOnly<{ ...$Exact<____TextStyle_Internal>, resizeMode?: 'contain' | 'cover' | 'stretch' | 'center' | 'repeat', + objectFit?: 'cover' | 'fill' | 'scale-down' | 'contain', tintColor?: ____ColorValue_Internal, overlayColor?: string, }>; diff --git a/packages/rn-tester/js/examples/Image/ImageExample.js b/packages/rn-tester/js/examples/Image/ImageExample.js index 729673c65b778d..25c775d912142a 100644 --- a/packages/rn-tester/js/examples/Image/ImageExample.js +++ b/packages/rn-tester/js/examples/Image/ImageExample.js @@ -602,6 +602,16 @@ const styles = StyleSheet.create({ fontSize: 11, marginBottom: 3, }, + objectFit: { + width: 90, + height: 60, + borderWidth: 0.5, + borderColor: 'black', + }, + objectFitText: { + fontSize: 11, + marginBottom: 3, + }, icon: { width: 15, height: 15, @@ -1046,6 +1056,60 @@ exports.examples = [ ); }, }, + { + title: 'Object Fit', + description: + ('The `objectFit` style props that is partially equivalent to resizeMode style prop, controls how the image is ' + + 'rendered within the frame.': string), + render: function (): React.Node { + return ( + + {[smallImage, fullImage].map((image, index) => { + return ( + + + + Cover + + + + Fill + + + + + + Scale Down + + + + Contain + + + + + ); + })} + + ); + }, + }, { title: 'Animated GIF', render: function (): React.Node { From a24b5d13329cb59e016cdffbbf53f171e0cf9600 Mon Sep 17 00:00:00 2001 From: cyrus25 Date: Sat, 3 Sep 2022 01:20:44 +0530 Subject: [PATCH 2/3] fix: use objectFit only as a style property --- Libraries/Image/Image.android.js | 4 +-- Libraries/Image/Image.ios.js | 5 +--- Libraries/Image/ImageProps.js | 6 ----- .../js/examples/Image/ImageExample.js | 26 ++++++++++++------- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/Libraries/Image/Image.android.js b/Libraries/Image/Image.android.js index 03d25305361b3a..0facc5216ab5d0 100644 --- a/Libraries/Image/Image.android.js +++ b/Libraries/Image/Image.android.js @@ -206,9 +206,7 @@ const BaseImage = (props: ImagePropsType, forwardedRef) => { }; const updatedResizeMode = - getResizeModeEquivalentFromObjectFit(props.objectFit) || - getResizeModeEquivalentFromObjectFit(style.objectFit) || - props.resizeMode; + getResizeModeEquivalentFromObjectFit(style.objectFit) || props.resizeMode; return ( diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js index 50566d3d09b2b9..32d03ec3a361f9 100644 --- a/Libraries/Image/Image.ios.js +++ b/Libraries/Image/Image.ios.js @@ -148,10 +148,7 @@ const BaseImage = (props: ImagePropsType, forwardedRef) => { const updatedResizeMode = // $FlowFixMe[prop-missing] - getResizeModeEquivalentFromObjectFit(props.objectFit) || - // $FlowFixMe[prop-missing] - getResizeModeEquivalentFromObjectFit(style.objectFit) || - resizeMode; + getResizeModeEquivalentFromObjectFit(style.objectFit) || resizeMode; // $FlowFixMe[prop-missing] const tintColor = style.tintColor; diff --git a/Libraries/Image/ImageProps.js b/Libraries/Image/ImageProps.js index 63de6fb76c08e7..6bb36fedb5d4aa 100644 --- a/Libraries/Image/ImageProps.js +++ b/Libraries/Image/ImageProps.js @@ -162,12 +162,6 @@ export type ImageProps = {| */ resizeMode?: ?('cover' | 'contain' | 'stretch' | 'repeat' | 'center'), - /** - * Partially equivalent to resizeMode, determines how to resize the image when the frame doesn't match the raw - * image dimensions. - */ - objectFit?: ?('cover' | 'fill' | 'scale-down' | 'contain'), - /** * A unique identifier for this element to be used in UI Automation * testing scripts. diff --git a/packages/rn-tester/js/examples/Image/ImageExample.js b/packages/rn-tester/js/examples/Image/ImageExample.js index 25c775d912142a..3e72932d6948df 100644 --- a/packages/rn-tester/js/examples/Image/ImageExample.js +++ b/packages/rn-tester/js/examples/Image/ImageExample.js @@ -608,6 +608,18 @@ const styles = StyleSheet.create({ borderWidth: 0.5, borderColor: 'black', }, + objectFitCover: { + objectFit: 'cover', + }, + objectFitFill: { + objectFit: 'fill', + }, + objectFitScaleDown: { + objectFit: 'scale-down', + }, + objectFitContain: { + objectFit: 'contain', + }, objectFitText: { fontSize: 11, marginBottom: 3, @@ -1059,7 +1071,7 @@ exports.examples = [ { title: 'Object Fit', description: - ('The `objectFit` style props that is partially equivalent to resizeMode style prop, controls how the image is ' + + ('The `objectFit` style props that are partially equivalent to resizeMode style prop, controls how the image is ' + 'rendered within the frame.': string), render: function (): React.Node { return ( @@ -1071,16 +1083,14 @@ exports.examples = [ Cover Fill @@ -1089,16 +1099,14 @@ exports.examples = [ Scale Down Contain From c0ad1651ec8ea4ba739b6c498419ba46e095ae5b Mon Sep 17 00:00:00 2001 From: cyrus25 Date: Sat, 3 Sep 2022 02:04:45 +0530 Subject: [PATCH 3/3] fix: typescript errors --- Libraries/Image/Image.android.js | 4 ++-- Libraries/Image/Image.ios.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Libraries/Image/Image.android.js b/Libraries/Image/Image.android.js index 0facc5216ab5d0..7e29c704588d61 100644 --- a/Libraries/Image/Image.android.js +++ b/Libraries/Image/Image.android.js @@ -55,7 +55,7 @@ function getSize( function getResizeModeEquivalentFromObjectFit( objectFit?: ?ImageObjectFit, -): ImageResizeMode { +): ?ImageResizeMode { if (!objectFit) { return null; } @@ -206,7 +206,7 @@ const BaseImage = (props: ImagePropsType, forwardedRef) => { }; const updatedResizeMode = - getResizeModeEquivalentFromObjectFit(style.objectFit) || props.resizeMode; + getResizeModeEquivalentFromObjectFit(style?.objectFit) || props.resizeMode; return ( diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js index 32d03ec3a361f9..f6b3b12a7bd702 100644 --- a/Libraries/Image/Image.ios.js +++ b/Libraries/Image/Image.ios.js @@ -148,7 +148,7 @@ const BaseImage = (props: ImagePropsType, forwardedRef) => { const updatedResizeMode = // $FlowFixMe[prop-missing] - getResizeModeEquivalentFromObjectFit(style.objectFit) || resizeMode; + getResizeModeEquivalentFromObjectFit(style?.objectFit) || resizeMode; // $FlowFixMe[prop-missing] const tintColor = style.tintColor;