Files
react-native/packages/rn-tester/js/examples/Image/ImageExample.js
T
Peter Abbondanzo 018d0621c1 Declare public resources from images that are loaded by name (#45421)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/45421

RNTester contains Android resources that are loaded by name and not resolved by Metro. As a result, these assets are not automatically linked when RNTester JS code is embedded in other projects. This is considered "legacy" loading and is generally discouraged, but is still showcased as an alernative way of loading resources.

I also modified the Image test to ensure that flag status is printed so it's obvious why the vector drawable hasn't loaded.

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D59585555

fbshipit-source-id: d42fb44d8846d8e7c7aa01dca4cec89ae85a9195
2024-07-15 12:39:06 -07:00

1547 lines
42 KiB
JavaScript

/**
* 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.
*
* @format
* @flow strict-local
*/
'use strict';
import type {LayoutEvent} from 'react-native/Libraries/Types/CoreEventTypes';
import * as ReactNativeFeatureFlags from 'react-native/src/private/featureflags/ReactNativeFeatureFlags';
const ImageCapInsetsExample = require('./ImageCapInsetsExample');
const React = require('react');
const {
Image,
ImageBackground,
StyleSheet,
Text,
View,
} = require('react-native');
const IMAGE1 =
'https://www.facebook.com/assets/fb_lite_messaging/E2EE-settings@3x.png';
const IMAGE2 =
'https://www.facebook.com/ar_effect/external_textures/648609739826677.png';
const base64Icon =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAABLCAQAAACSR7JhAAADtUlEQVR4Ac3YA2Bj6QLH0XPT1Fzbtm29tW3btm3bfLZtv7e2ObZnms7d8Uw098tuetPzrxv8wiISrtVudrG2JXQZ4VOv+qUfmqCGGl1mqLhoA52oZlb0mrjsnhKpgeUNEs91Z0pd1kvihA3ULGVHiQO2narKSHKkEMulm9VgUyE60s1aWoMQUbpZOWE+kaqs4eLEjdIlZTcFZB0ndc1+lhB1lZrIuk5P2aib1NBpZaL+JaOGIt0ls47SKzLC7CqrlGF6RZ09HGoNy1lYl2aRSWL5GuzqWU1KafRdoRp0iOQEiDzgZPnG6DbldcomadViflnl/cL93tOoVbsOLVM2jylvdWjXolWX1hmfZbGR/wjypDjFLSZIRov09BgYmtUqPQPlQrPapecLgTIy0jMgPKtTeob2zWtrGH3xvjUkPCtNg/tm1rjwrMa+mdUkPd3hWbH0jArPGiU9ufCsNNWFZ40wpwn+62/66R2RUtoso1OB34tnLOcy7YB1fUdc9e0q3yru8PGM773vXsuZ5YIZX+5xmHwHGVvlrGPN6ZSiP1smOsMMde40wKv2VmwPPVXNut4sVpUreZiLBHi0qln/VQeI/LTMYXpsJtFiclUN+5HVZazim+Ky+7sAvxWnvjXrJFneVtLWLyPJu9K3cXLWeOlbMTlrIelbMDlrLenrjEQOtIF+fuI9xRp9ZBFp6+b6WT8RrxEpdK64BuvHgDk+vUy+b5hYk6zfyfs051gRoNO1usU12WWRWL73/MMEy9pMi9qIrR4ZpV16Rrvduxazmy1FSvuFXRkqTnE7m2kdb5U8xGjLw/spRr1uTov4uOgQE+0N/DvFrG/Jt7i/FzwxbA9kDanhf2w+t4V97G8lrT7wc08aA2QNUkuTfW/KimT01wdlfK4yEw030VfT0RtZbzjeMprNq8m8tnSTASrTLti64oBNdpmMQm0eEwvfPwRbUBywG5TzjPCsdwk3IeAXjQblLCoXnDVeoAz6SfJNk5TTzytCNZk/POtTSV40NwOFWzw86wNJRpubpXsn60NJFlHeqlYRbslqZm2jnEZ3qcSKgm0kTli3zZVS7y/iivZTweYXJ26Y+RTbV1zh3hYkgyFGSTKPfRVbRqWWVReaxYeSLarYv1Qqsmh1s95S7G+eEWK0f3jYKTbV6bOwepjfhtafsvUsqrQvrGC8YhmnO9cSCk3yuY984F1vesdHYhWJ5FvASlacshUsajFt2mUM9pqzvKGcyNJW0arTKN1GGGzQlH0tXwLDgQTurS8eIQAAAABJRU5ErkJggg==';
const IMAGE_PREFETCH_URL = `${IMAGE1}?r=1&t=${Date.now()}`;
const prefetchTask = Image.prefetch(IMAGE_PREFETCH_URL);
type ImageSource = $ReadOnly<{|
uri: string,
|}>;
type BlobImageState = {|
objectURL: ?string,
|};
type BlobImageProps = $ReadOnly<{|
url: string,
|}>;
class BlobImage extends React.Component<BlobImageProps, BlobImageState> {
state: BlobImageState = {
objectURL: null,
};
UNSAFE_componentWillMount() {
// $FlowFixMe[unused-promise]
(async () => {
const result = await fetch(this.props.url);
const blob = await result.blob();
const objectURL = URL.createObjectURL(blob);
this.setState({objectURL});
})();
}
render(): React.Node {
return this.state.objectURL !== null ? (
<Image source={{uri: this.state.objectURL}} style={styles.base} />
) : (
<Text>Object URL not created yet</Text>
);
}
}
type BlobImageExampleState = {||};
type BlobImageExampleProps = $ReadOnly<{|
urls: string[],
|}>;
class BlobImageExample extends React.Component<
BlobImageExampleProps,
BlobImageExampleState,
> {
render(): React.Node {
return (
<View style={styles.horizontal}>
{this.props.urls.map(url => (
<BlobImage key={url} url={url} />
))}
</View>
);
}
}
type NetworkImageCallbackExampleState = {|
events: Array<string>,
startLoadPrefetched: boolean,
mountTime: number,
imageHash: number,
|};
type NetworkImageCallbackExampleProps = $ReadOnly<{|
source: ImageSource,
prefetchedSource: ImageSource,
|}>;
class NetworkImageCallbackExample extends React.Component<
NetworkImageCallbackExampleProps,
NetworkImageCallbackExampleState,
> {
state: NetworkImageCallbackExampleState = {
events: [],
startLoadPrefetched: false,
mountTime: Date.now(),
imageHash: Date.now(),
};
UNSAFE_componentWillMount() {
this.setState({mountTime: Date.now()});
}
_loadEventFired = (event: string) => {
this.setState(state => ({
events: [...state.events, event],
}));
};
updateLoadingImageHash = () => {
this.setState({imageHash: Date.now()});
};
render(): React.Node {
const {mountTime} = this.state;
return (
<View>
<Image
source={this.props.source}
style={[styles.base, styles.visibleOverflow]}
onLoadStart={() =>
this._loadEventFired(`✔ onLoadStart (+${Date.now() - mountTime}ms)`)
}
onProgress={event => {
const {loaded, total} = event.nativeEvent;
const percent = Math.round((loaded / total) * 100);
this._loadEventFired(
`✔ onProgress ${percent}% (+${Date.now() - mountTime}ms)`,
);
}}
onLoad={event => {
if (event.nativeEvent.source) {
const url = event.nativeEvent.source.uri;
this._loadEventFired(
`✔ onLoad (+${Date.now() - mountTime}ms) for URL ${url}`,
);
} else {
this._loadEventFired(`✔ onLoad (+${Date.now() - mountTime}ms)`);
}
}}
onLoadEnd={() => {
this._loadEventFired(`✔ onLoadEnd (+${Date.now() - mountTime}ms)`);
this.setState({startLoadPrefetched: true}, () => {
prefetchTask.then(
() => {
this._loadEventFired(
`✔ prefetch OK (+${Date.now() - mountTime}ms)`,
);
// $FlowFixMe[unused-promise]
Image.queryCache([IMAGE_PREFETCH_URL]).then(map => {
const result = map[IMAGE_PREFETCH_URL];
if (result) {
this._loadEventFired(
`✔ queryCache "${result}" (+${
Date.now() - mountTime
}ms)`,
);
} else {
this._loadEventFired(
`✘ queryCache (+${Date.now() - mountTime}ms)`,
);
}
});
},
error => {
this._loadEventFired(
`✘ prefetch failed (+${Date.now() - mountTime}ms)`,
);
},
);
});
}}
/>
{this.state.startLoadPrefetched ? (
<Image
source={this.props.prefetchedSource}
style={[styles.base, styles.visibleOverflow]}
onLoadStart={() =>
this._loadEventFired(
`✔ (prefetched) onLoadStart (+${Date.now() - mountTime}ms)`,
)
}
onLoad={event => {
// Currently this image source feature is only available on iOS.
if (event.nativeEvent.source) {
const url = event.nativeEvent.source.uri;
this._loadEventFired(
`✔ (prefetched) onLoad (+${
Date.now() - mountTime
}ms) for URL ${url}`,
);
} else {
this._loadEventFired(
`✔ (prefetched) onLoad (+${Date.now() - mountTime}ms)`,
);
}
}}
onLoadEnd={() =>
this._loadEventFired(
`✔ (prefetched) onLoadEnd (+${Date.now() - mountTime}ms)`,
)
}
/>
) : null}
<Text style={styles.networkImageText}>
{this.state.events.join('\n')}
</Text>
</View>
);
}
}
type NetworkImageExampleState = {|
error: ?string,
loading: boolean,
progress: $ReadOnlyArray<number>,
|};
type NetworkImageExampleProps = $ReadOnly<{|
source: ImageSource,
|}>;
class NetworkImageExample extends React.Component<
NetworkImageExampleProps,
NetworkImageExampleState,
> {
state: NetworkImageExampleState = {
error: null,
loading: false,
progress: [],
};
render(): React.Node {
return this.state.error != null ? (
<Text>{this.state.error}</Text>
) : (
<>
<Image
source={this.props.source}
style={[styles.base, styles.visibleOverflow]}
onLoadStart={e => this.setState({loading: true})}
onError={e =>
this.setState({error: e.nativeEvent.error, loading: false})
}
onProgress={e => {
const {loaded, total} = e.nativeEvent;
this.setState(prevState => ({
progress: [
...prevState.progress,
Math.round((100 * loaded) / total),
],
}));
}}
onLoad={() => this.setState({loading: false, error: null})}
/>
<Text>
{this.state.progress.map(progress => `${progress}%`).join('\n')}
</Text>
</>
);
}
}
type ImageSizeExampleState = {|
width: number,
height: number,
|};
type ImageSizeExampleProps = $ReadOnly<{|
source: ImageSource,
|}>;
class ImageSizeExample extends React.Component<
ImageSizeExampleProps,
ImageSizeExampleState,
> {
state: ImageSizeExampleState = {
width: 0,
height: 0,
};
componentDidMount() {
Image.getSize(this.props.source.uri, (width, height) => {
this.setState({width, height});
});
}
render(): React.Node {
return (
<View style={styles.flexRow}>
<Image style={styles.imageSizeExample} source={this.props.source} />
<Text>
Actual dimensions:{'\n'}
Width: {this.state.width}, Height: {this.state.height}
</Text>
</View>
);
}
}
type MultipleSourcesExampleState = {|
width: number,
height: number,
|};
type MultipleSourcesExampleProps = $ReadOnly<{||}>;
class MultipleSourcesExample extends React.Component<
MultipleSourcesExampleProps,
MultipleSourcesExampleState,
> {
state: MultipleSourcesExampleState = {
width: 30,
height: 30,
};
increaseImageSize = () => {
if (this.state.width >= 100) {
return;
}
this.setState({
width: this.state.width + 10,
height: this.state.height + 10,
});
};
decreaseImageSize = () => {
if (this.state.width <= 10) {
return;
}
this.setState({
width: this.state.width - 10,
height: this.state.height - 10,
});
};
render(): React.Node {
return (
<View>
<View style={styles.spaceBetweenView}>
<Text style={styles.touchableText} onPress={this.decreaseImageSize}>
Decrease image size
</Text>
<Text style={styles.touchableText} onPress={this.increaseImageSize}>
Increase image size
</Text>
</View>
<Text>
Container image size: {this.state.width}x{this.state.height}{' '}
</Text>
<View style={{height: this.state.height, width: this.state.width}}>
<Image
style={styles.flex}
source={[
{
uri: IMAGE1,
width: 38,
height: 38,
},
{
uri: IMAGE2,
width: 100,
height: 100,
},
]}
/>
</View>
</View>
);
}
}
type LoadingIndicatorSourceExampleState = {|
imageHash: number,
|};
type LoadingIndicatorSourceExampleProps = $ReadOnly<{||}>;
class LoadingIndicatorSourceExample extends React.Component<
LoadingIndicatorSourceExampleProps,
LoadingIndicatorSourceExampleState,
> {
state: LoadingIndicatorSourceExampleState = {
imageHash: Date.now(),
};
reloadImage = () => {
this.setState({
imageHash: Date.now(),
});
};
loaderGif: {uri: string} = {
uri: 'https://media1.giphy.com/media/3oEjI6SIIHBdRxXI40/200.gif',
};
render(): React.Node {
const loadingImage = {
uri: `${IMAGE2}?hash=${this.state.imageHash}`,
};
return (
<View>
<View style={styles.spaceBetweenView}>
<Text style={styles.touchableText} onPress={this.reloadImage}>
Refresh Image
</Text>
</View>
<Image
loadingIndicatorSource={this.loaderGif}
source={loadingImage}
style={styles.base}
/>
<Text>Image Hash: {this.state.imageHash}</Text>
<Text>Image URI: {loadingImage.uri}</Text>
</View>
);
}
}
type FadeDurationExampleState = {|
imageHash: number,
|};
type FadeDurationExampleProps = $ReadOnly<{||}>;
class FadeDurationExample extends React.Component<
FadeDurationExampleProps,
FadeDurationExampleState,
> {
state: FadeDurationExampleState = {
imageHash: Date.now(),
};
reloadImage = () => {
this.setState({
imageHash: Date.now(),
});
};
render(): React.Node {
const loadingImage = {
uri: `${IMAGE2}?hash=${this.state.imageHash}`,
};
return (
<View>
<View style={styles.spaceBetweenView}>
<Text style={styles.touchableText} onPress={this.reloadImage}>
Refresh Image
</Text>
</View>
<Image fadeDuration={1500} source={loadingImage} style={styles.base} />
<Text>This image will fade in over the time of 1.5s.</Text>
</View>
);
}
}
type OnLayoutExampleState = {|
width: number,
height: number,
layoutHandlerMessage: string,
|};
type OnLayoutExampleProps = $ReadOnly<{||}>;
class OnLayoutExample extends React.Component<
OnLayoutExampleProps,
OnLayoutExampleState,
> {
state: OnLayoutExampleState = {
width: 30,
height: 30,
layoutHandlerMessage: 'No Message',
};
onLayoutHandler = (event: LayoutEvent) => {
this.setState({
width: this.state.width,
height: this.state.height,
layoutHandlerMessage: JSON.stringify(event.nativeEvent),
});
console.log(event.nativeEvent);
};
increaseImageSize = () => {
if (this.state.width >= 100) {
return;
}
this.setState({
width: this.state.width + 10,
height: this.state.height + 10,
});
};
decreaseImageSize = () => {
if (this.state.width <= 10) {
return;
}
this.setState({
width: this.state.width - 10,
height: this.state.height - 10,
});
};
render(): React.Node {
return (
<View>
<Text>Adjust the image size to trigger the OnLayout handler.</Text>
<View style={styles.spaceBetweenView}>
<Text style={styles.touchableText} onPress={this.decreaseImageSize}>
Decrease image size
</Text>
<Text style={styles.touchableText} onPress={this.increaseImageSize}>
Increase image size
</Text>
</View>
<Text>
Container image size: {this.state.width}x{this.state.height}{' '}
</Text>
<View style={{height: this.state.height, width: this.state.width}}>
<Image
onLayout={this.onLayoutHandler}
style={styles.flex}
source={[
{
uri: IMAGE1,
width: 38,
height: 38,
},
{
uri: IMAGE1,
width: 76,
height: 76,
},
{
uri: IMAGE2,
width: 400,
height: 400,
},
]}
/>
</View>
<Text>Layout Handler Message: {this.state.layoutHandlerMessage}</Text>
</View>
);
}
}
type OnPartialLoadExampleState = {|
hasLoaded: boolean,
|};
type OnPartialLoadExampleProps = $ReadOnly<{||}>;
class OnPartialLoadExample extends React.Component<
OnPartialLoadExampleProps,
OnPartialLoadExampleState,
> {
state: OnPartialLoadExampleState = {
hasLoaded: false,
};
partialLoadHandler = () => {
this.setState({
hasLoaded: true,
});
};
render(): React.Node {
return (
<View>
<Text>
Partial Load Function Executed: {JSON.stringify(this.state.hasLoaded)}
</Text>
<Image
source={{
uri: `https://images.pexels.com/photos/671557/pexels-photo-671557.jpeg?&buster=${Math.random()}`,
}}
onPartialLoad={this.partialLoadHandler}
style={styles.base}
/>
</View>
);
}
}
type VectorDrawableExampleState = {||};
type VectorDrawableExampleProps = $ReadOnly<{||}>;
class VectorDrawableExample extends React.Component<
VectorDrawableExampleProps,
VectorDrawableExampleState,
> {
state: VectorDrawableExampleState = {};
render(): React.Node {
const isEnabled = ReactNativeFeatureFlags.loadVectorDrawablesOnImages();
return (
<View style={styles.flex}>
<Text>Enabled: {isEnabled ? 'true' : 'false'}</Text>
<Image source={{uri: 'ic_android'}} style={{height: 64, width: 64}} />
</View>
);
}
}
const fullImage: ImageSource = {
uri: IMAGE2,
};
const smallImage = {
uri: IMAGE1,
};
const styles = StyleSheet.create({
base: {
width: 64,
height: 64,
margin: 4,
},
visibleOverflow: {
overflow: 'visible',
},
leftMargin: {
marginLeft: 10,
},
background: {
backgroundColor: '#222222',
},
sectionText: {
marginVertical: 6,
},
nestedText: {
marginLeft: 12,
marginTop: 20,
backgroundColor: 'transparent',
color: 'white',
},
resizeMode: {
width: 90,
height: 60,
borderWidth: 0.5,
borderColor: 'black',
},
resizeModeText: {
fontSize: 11,
marginBottom: 3,
},
icon: {
width: 15,
height: 15,
margin: 4,
},
horizontal: {
flexDirection: 'row',
flexWrap: 'wrap',
},
gif: {
flex: 1,
height: 200,
},
base64: {
flex: 1,
height: 50,
resizeMode: 'contain',
},
touchableText: {
fontWeight: '500',
color: 'blue',
},
networkImageText: {
marginTop: 20,
},
flex: {
flex: 1,
},
imageWithBorderRadius: {
borderRadius: 5,
},
imageSizeExample: {
width: 60,
height: 60,
backgroundColor: 'transparent',
marginRight: 10,
},
flexRow: {
flexDirection: 'row',
},
spaceBetweenView: {
flexDirection: 'row',
justifyContent: 'space-between',
},
customBorderColor: {
borderWidth: 5,
borderColor: '#f099f0',
},
borderTopLeftRadius: {
borderTopLeftRadius: 20,
},
opacity1: {
opacity: 1,
},
opacity2: {
opacity: 0.8,
},
opacity3: {
opacity: 0.6,
},
opacity4: {
opacity: 0.4,
},
opacity5: {
opacity: 0.2,
},
opacity6: {
opacity: 0,
},
transparentImageBackground: {
width: 60,
height: 60,
backgroundColor: 'transparent',
},
tintColor1: {
tintColor: '#ff2d55',
},
tintColor2: {
tintColor: '#5ac8fa',
},
tintColor3: {
tintColor: '#4cd964',
},
tintColor4: {
tintColor: '#8e8e93',
},
objectFitContain: {
objectFit: 'contain',
},
objectFitCover: {
objectFit: 'cover',
},
objectFitFill: {
objectFit: 'fill',
},
objectFitScaleDown: {
objectFit: 'scale-down',
},
imageInBundle: {
borderColor: 'yellow',
borderWidth: 4,
},
imageInAssetCatalog: {
marginLeft: 10,
borderColor: 'blue',
borderWidth: 4,
},
backgroundColor1: {
backgroundColor: 'rgba(0, 0, 100, 0.25)',
},
backgroundColor2: {
backgroundColor: 'red',
},
backgroundColor3: {
backgroundColor: 'red',
borderColor: 'green',
borderWidth: 3,
borderRadius: 25,
},
borderRadius1: {
borderRadius: 19,
},
borderRadius2: {
borderWidth: 4,
borderTopLeftRadius: 10,
borderBottomRightRadius: 20,
borderColor: 'green',
},
borderRadius3: {
resizeMode: 'cover',
width: 90,
borderWidth: 4,
borderTopLeftRadius: 10,
borderTopRightRadius: 20,
borderBottomRightRadius: 30,
borderBottomLeftRadius: 40,
borderColor: 'red',
},
borderRadius4: {
resizeMode: 'stretch',
width: 90,
borderWidth: 4,
borderTopLeftRadius: 10,
borderTopRightRadius: 20,
borderBottomRightRadius: 30,
borderBottomLeftRadius: 40,
borderColor: 'red',
backgroundColor: 'yellow',
},
borderRadius5: {
resizeMode: 'contain',
width: 90,
borderWidth: 4,
borderTopLeftRadius: 10,
borderTopRightRadius: 20,
borderBottomRightRadius: 30,
borderBottomLeftRadius: 40,
borderColor: 'red',
backgroundColor: 'yellow',
},
});
exports.displayName = (undefined: ?string);
exports.framework = 'React';
exports.title = 'Image';
exports.category = 'Basic';
exports.description =
'Base component for displaying different types of images.';
exports.examples = [
{
title: 'Plain Network Image with `source` prop.',
description: ('If the `source` prop `uri` property is prefixed with ' +
'"http", then it will be downloaded from the network.': string),
render: function (): React.Node {
return <Image source={fullImage} style={styles.base} />;
},
},
{
title: 'Plain Network Image with `src` prop.',
description: ('If the `src` prop is defined with ' +
'"http", then it will be downloaded from the network.': string),
render: function (): React.Node {
return <Image src={fullImage.uri} style={styles.base} />;
},
},
{
title: 'Multiple Image Source using the `srcSet` prop.',
description:
('A list of comma seperated uris along with scale are provided in `srcSet`.' +
'An appropriate value will be used based on the scale of the device.': string),
render: function (): React.Node {
return (
<Image
width={64}
height={64}
srcSet={`${IMAGE2} 4x, ${IMAGE1} 2x`}
style={styles.base}
/>
);
},
},
{
title: 'Plain Blob Image',
description: ('If the `source` prop `uri` property is an object URL, ' +
'then it will be resolved using `BlobProvider` (Android) or `RCTBlobManager` (iOS).': string),
render: function (): React.Node {
return <BlobImageExample urls={[IMAGE1, IMAGE2]} />;
},
},
{
title: 'Plain Static Image',
description:
('Static assets should be placed in the source code tree, and ' +
'required in the same way as JavaScript modules.': string),
render: function (): React.Node {
return (
<View style={styles.horizontal}>
<Image
source={require('../../assets/uie_thumb_normal.png')}
style={styles.icon}
/>
<Image
source={require('../../assets/uie_thumb_selected.png')}
style={styles.icon}
/>
<Image
source={require('../../assets/uie_comment_normal.png')}
style={styles.icon}
/>
<Image
source={require('../../assets/uie_comment_highlighted.png')}
style={styles.icon}
/>
</View>
);
},
},
{
title: 'Image Loading Events',
render: function (): React.Node {
return (
<NetworkImageCallbackExample
source={{
uri: `${IMAGE1}?r=1&t=${Date.now()}`,
}}
prefetchedSource={{uri: IMAGE_PREFETCH_URL}}
/>
);
},
},
{
title: 'Error Handler',
render: function (): React.Node {
return (
<NetworkImageExample
source={{
uri: IMAGE1 + '_TYPO',
}}
/>
);
},
},
{
title: 'Image Download Progress',
render: function (): React.Node {
return (
<NetworkImageExample
source={{
uri: `${IMAGE1}?r=1`,
}}
/>
);
},
},
{
title: 'defaultSource',
description: 'Show a placeholder image when a network image is loading',
render: function (): React.Node {
return (
<Image
defaultSource={require('../../assets/bunny.png')}
source={{
// Note: Use a large image and bust cache so we can in fact
// visualize the `defaultSource` image.
uri: fullImage.uri + '?cacheBust=notinCache' + Date.now(),
}}
style={styles.base}
/>
);
},
platform: 'ios',
},
{
title: 'Cache Policy',
description:
('First image has never been loaded before and is instructed not to load unless in cache.' +
'Placeholder image from above will stay. Second image is the same but forced to load regardless of' +
' local cache state.': string),
render: function (): React.Node {
return (
<View style={styles.horizontal}>
<Image
defaultSource={require('../../assets/bunny.png')}
source={{
uri: smallImage.uri + '?cacheBust=notinCache' + Date.now(),
cache: 'only-if-cached',
}}
style={styles.base}
/>
<Image
defaultSource={require('../../assets/bunny.png')}
source={{
uri: smallImage.uri + '?cacheBust=notinCache' + Date.now(),
cache: 'reload',
}}
style={styles.base}
/>
</View>
);
},
platform: 'ios',
},
{
title: 'Borders',
render: function (): React.Node {
return (
<View style={styles.horizontal}>
<Image
source={smallImage}
style={[styles.base, styles.background, styles.customBorderColor]}
/>
</View>
);
},
},
{
title: 'Border Radius',
render: function (): React.Node {
return (
<View style={styles.horizontal}>
<Image
style={[styles.base, styles.imageWithBorderRadius]}
source={fullImage}
/>
<Image
style={[styles.base, styles.borderRadius1]}
source={fullImage}
/>
<Image
style={[styles.base, styles.borderTopLeftRadius]}
source={fullImage}
/>
<Image
style={[styles.base, styles.borderRadius2]}
source={fullImage}
/>
<Image
style={[styles.base, styles.borderRadius3]}
source={fullImage}
/>
<Image
style={[styles.base, styles.borderRadius4]}
source={fullImage}
/>
<Image
style={[styles.base, styles.borderRadius5]}
source={fullImage}
/>
</View>
);
},
},
{
title: 'Background Color',
render: function (): React.Node {
return (
<View style={styles.horizontal}>
<Image source={smallImage} style={styles.base} />
<Image
style={[styles.base, styles.backgroundColor1]}
source={smallImage}
/>
<Image
style={[styles.base, styles.backgroundColor2]}
source={smallImage}
/>
<Image
style={[styles.base, styles.backgroundColor3]}
source={smallImage}
/>
</View>
);
},
},
{
title: 'Opacity',
render: function (): React.Node {
return (
<View style={styles.horizontal}>
<Image style={[styles.base, styles.opacity1]} source={fullImage} />
<Image style={[styles.base, styles.opacity2]} source={fullImage} />
<Image style={[styles.base, styles.opacity3]} source={fullImage} />
<Image style={[styles.base, styles.opacity4]} source={fullImage} />
<Image style={[styles.base, styles.opacity5]} source={fullImage} />
<Image style={[styles.base, styles.opacity6]} source={fullImage} />
</View>
);
},
},
{
title: 'Nesting content inside <Image> component',
render: function (): React.Node {
return (
<View style={styles.base}>
<Image
style={{...StyleSheet.absoluteFillObject}}
source={fullImage}
/>
<Text style={styles.nestedText}>React</Text>
</View>
);
},
},
{
title: 'Nesting content inside <ImageBackground> component',
render: function (): React.Node {
return (
<ImageBackground
style={styles.transparentImageBackground}
source={fullImage}>
<Text style={styles.nestedText}>React</Text>
</ImageBackground>
);
},
},
{
title: 'Tint Color',
description: ('The `tintColor` prop changes all the non-alpha ' +
'pixels to the tint color.': string),
render: function (): React.Node {
return (
<View>
<View style={styles.horizontal}>
<Image
source={require('../../assets/uie_thumb_normal.png')}
style={[
styles.icon,
styles.imageWithBorderRadius,
styles.tintColor1,
]}
tintColor={'#5ac8fa'}
/>
<Image
source={require('../../assets/uie_thumb_normal.png')}
style={[styles.icon, styles.imageWithBorderRadius]}
tintColor={'#4cd964'}
/>
<Image
source={require('../../assets/uie_thumb_normal.png')}
style={[styles.icon, styles.imageWithBorderRadius]}
tintColor={'#ff2d55'}
/>
<Image
source={require('../../assets/uie_thumb_normal.png')}
style={[styles.icon, styles.imageWithBorderRadius]}
tintColor={'#8e8e93'}
/>
</View>
<Text style={styles.sectionText}>
It also works using the `tintColor` style prop
</Text>
<View style={styles.horizontal}>
<Image
source={require('../../assets/uie_thumb_normal.png')}
style={[
styles.icon,
styles.imageWithBorderRadius,
styles.tintColor2,
]}
/>
<Image
source={require('../../assets/uie_thumb_normal.png')}
style={[
styles.icon,
styles.imageWithBorderRadius,
styles.tintColor3,
]}
/>
<Image
source={require('../../assets/uie_thumb_normal.png')}
style={[
styles.icon,
styles.imageWithBorderRadius,
styles.tintColor1,
]}
/>
<Image
source={require('../../assets/uie_thumb_normal.png')}
style={[
styles.icon,
styles.imageWithBorderRadius,
styles.tintColor4,
]}
/>
</View>
<Text style={styles.sectionText}>
The `tintColor` prop has precedence over the `tintColor` style prop
</Text>
<View style={styles.horizontal}>
<Image
source={require('../../assets/uie_thumb_normal.png')}
style={[
styles.icon,
styles.imageWithBorderRadius,
styles.tintColor2,
]}
tintColor={'#5ac8fa'}
/>
<Image
source={require('../../assets/uie_thumb_normal.png')}
style={[
styles.icon,
styles.imageWithBorderRadius,
styles.tintColor3,
]}
tintColor={'#5ac8fa'}
/>
<Image
source={require('../../assets/uie_thumb_normal.png')}
style={[
styles.icon,
styles.imageWithBorderRadius,
styles.tintColor1,
]}
tintColor={'#5ac8fa'}
/>
<Image
source={require('../../assets/uie_thumb_normal.png')}
style={[
styles.icon,
styles.imageWithBorderRadius,
styles.tintColor4,
]}
tintColor={'#5ac8fa'}
/>
</View>
<Text style={styles.sectionText}>
It also works with downloaded images:
</Text>
<View style={styles.horizontal}>
<Image
source={smallImage}
style={[
styles.base,
styles.imageWithBorderRadius,
styles.tintColor2,
]}
/>
<Image
source={smallImage}
style={[
styles.base,
styles.imageWithBorderRadius,
styles.tintColor3,
]}
/>
<Image
source={smallImage}
style={[
styles.base,
styles.imageWithBorderRadius,
styles.tintColor1,
]}
/>
<Image
source={smallImage}
style={[
styles.base,
styles.imageWithBorderRadius,
styles.tintColor4,
]}
/>
</View>
</View>
);
},
},
{
title: 'Object Fit',
description: ('The `objectFit` style prop controls how the image is ' +
'rendered within the frame.': string),
render: function (): React.Node {
return (
<View>
{[smallImage, fullImage].map((image, index) => {
return (
<View key={index}>
<View style={styles.horizontal}>
<View>
<Text style={styles.resizeModeText}>Contain</Text>
<Image
style={[styles.resizeMode, styles.objectFitContain]}
source={image}
/>
</View>
<View style={styles.leftMargin}>
<Text style={styles.resizeModeText}>Cover</Text>
<Image
style={[styles.resizeMode, styles.objectFitCover]}
source={image}
/>
</View>
</View>
<View style={styles.horizontal}>
<View>
<Text style={styles.resizeModeText}>Fill</Text>
<Image
style={[styles.resizeMode, styles.objectFitFill]}
source={image}
/>
</View>
<View style={styles.leftMargin}>
<Text style={styles.resizeModeText}>Scale Down</Text>
<Image
style={[styles.resizeMode, styles.objectFitScaleDown]}
source={image}
/>
</View>
</View>
</View>
);
})}
</View>
);
},
},
{
title: 'Resize Mode',
description: ('The `resizeMode` style prop controls how the image is ' +
'rendered within the frame.': string),
render: function (): React.Node {
return (
<View>
{[smallImage, fullImage].map((image, index) => {
return (
<View key={index}>
<View style={styles.horizontal}>
<View>
<Text style={styles.resizeModeText}>Contain</Text>
<Image
style={styles.resizeMode}
resizeMode="contain"
source={image}
/>
</View>
<View style={styles.leftMargin}>
<Text style={styles.resizeModeText}>Cover</Text>
<Image
style={styles.resizeMode}
resizeMode="cover"
source={image}
/>
</View>
</View>
<View style={styles.horizontal}>
<View>
<Text style={styles.resizeModeText}>Stretch</Text>
<Image
style={styles.resizeMode}
resizeMode="stretch"
source={image}
/>
</View>
<View style={styles.leftMargin}>
<Text style={styles.resizeModeText}>Repeat</Text>
<Image
style={styles.resizeMode}
resizeMode="repeat"
source={image}
/>
</View>
<View style={styles.leftMargin}>
<Text style={styles.resizeModeText}>Center</Text>
<Image
style={styles.resizeMode}
resizeMode="center"
source={image}
/>
</View>
</View>
</View>
);
})}
</View>
);
},
},
{
title: 'Animated GIF',
render: function (): React.Node {
return (
<Image
style={styles.gif}
source={require('../../assets/tumblr_mfqekpMktw1rn90umo1_500.gif')}
/>
);
},
platform: 'ios',
},
{
title: 'Base64 image',
render: function (): React.Node {
return (
<Image style={styles.base64} source={{uri: base64Icon, scale: 3}} />
);
},
platform: 'ios',
},
{
title: 'Cap Insets',
description:
('When the image is resized, the corners of the size specified ' +
'by capInsets will stay a fixed size, but the center content and ' +
'borders of the image will be stretched. This is useful for creating ' +
'resizable rounded buttons, shadows, and other resizable assets.': string),
render: function (): React.Node {
return <ImageCapInsetsExample />;
},
platform: 'ios',
},
{
title: 'Image Size',
render: function (): React.Node {
return <ImageSizeExample source={fullImage} />;
},
},
{
title: 'MultipleSourcesExample',
description:
('The `source` prop allows passing in an array of uris, so that native to choose which image ' +
'to diplay based on the size of the of the target image': string),
render: function (): React.Node {
return <MultipleSourcesExample />;
},
},
{
title: 'Legacy local image',
description: ('Images shipped with the native bundle, but not managed ' +
'by the JS packager': string),
render: function (): React.Node {
return <Image source={{uri: 'legacy_image', width: 120, height: 120}} />;
},
},
{
title: 'Bundled images',
description: 'Images shipped in a separate native bundle',
render: function (): React.Node {
return (
<View style={styles.flexRow}>
<Image
source={{
uri: 'ImageInBundle',
bundle: 'RNTesterBundle',
width: 100,
height: 100,
}}
style={styles.imageInBundle}
/>
<Image
source={{
uri: 'ImageInAssetCatalog',
bundle: 'RNTesterBundle',
width: 100,
height: 100,
}}
style={styles.imageInAssetCatalog}
/>
</View>
);
},
platform: 'ios',
},
{
title: 'Blur Radius',
render: function (): React.Node {
return (
<View style={styles.horizontal}>
<Image style={styles.base} source={fullImage} blurRadius={0} />
<Image style={styles.base} source={fullImage} blurRadius={5} />
<Image style={styles.base} source={fullImage} blurRadius={10} />
<Image style={styles.base} source={fullImage} blurRadius={15} />
<Image style={styles.base} source={fullImage} blurRadius={20} />
<Image style={styles.base} source={fullImage} blurRadius={25} />
</View>
);
},
},
{
title: 'Accessibility',
description:
('If the `accessible` (boolean) prop is set to True, the image will be indicated as an accessbility element.': string),
render: function (): React.Node {
return <Image accessible source={fullImage} style={styles.base} />;
},
},
{
title: 'Accessibility Label',
description:
('When an element is marked as accessibile (using the accessibility prop), it is good practice to set an accessibilityLabel on the image to provide a description of the element to people who use VoiceOver. VoiceOver will read this string when people select this element.': string),
render: function (): React.Node {
return (
<Image
accessible
accessibilityLabel="Picture of people standing around a table"
source={fullImage}
style={styles.base}
/>
);
},
},
{
title: 'Accessibility Label via alt prop',
description:
'Using the alt prop markes an element as being accessibile, and passes the alt text to accessibilityLabel',
render: function (): React.Node {
return (
<Image
alt="Picture of people standing around a table"
source={fullImage}
style={styles.base}
/>
);
},
},
{
title: 'Fade Duration',
description:
('The time (in miliseconds) that an image will fade in for when it appears (default = 300).': string),
render: function (): React.Node {
return <FadeDurationExample />;
},
platform: 'android',
},
{
title: 'Loading Indicator Source',
description:
('This prop is used to set the resource that will be used as the loading indicator for the image (displayed until the image is ready to be displayed).': string),
render: function (): React.Node {
return <LoadingIndicatorSourceExample />;
},
},
{
title: 'On Layout',
description:
('This prop is used to set the handler function to be called when the image is mounted or its layout changes. The function receives an event with `{nativeEvent: {layout: {x, y, width, height}}}`': string),
render: function (): React.Node {
return <OnLayoutExample />;
},
},
{
title: 'On Partial Load',
description:
('This prop is used to set the handler function to be called when the partial load of the image is complete. This is meant for progressive JPEG loads.': string),
render: function (): React.Node {
return <OnPartialLoadExample />;
},
platform: 'ios',
},
{
title: 'Vector Drawable',
description:
'Demonstrating an example of loading a vector drawable asset by name',
render: function (): React.Node {
return <VectorDrawableExample />;
},
platform: 'android',
},
];