-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix: Blurred images in attachment view on mobile devices #6442
Changes from all commits
da8b7ee
8543f59
7a72f8b
a0e5b03
45abbae
733a114
db9e2c7
24a5536
cf3b7af
d6b6055
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,12 @@ | ||
import React, {PureComponent} from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import {View, PanResponder} from 'react-native'; | ||
import {View, InteractionManager, PanResponder} from 'react-native'; | ||
import Image from 'react-native-fast-image'; | ||
import ImageZoom from 'react-native-image-pan-zoom'; | ||
import ImageSize from 'react-native-image-size'; | ||
import _ from 'underscore'; | ||
import ImageWithSizeCalculation from '../ImageWithSizeCalculation'; | ||
import styles from '../../styles/styles'; | ||
import * as StyleUtils from '../../styles/styles'; | ||
import * as StyleUtils from '../../styles/StyleUtils'; | ||
import variables from '../../styles/variables'; | ||
import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; | ||
|
||
|
@@ -24,8 +25,11 @@ class ImageView extends PureComponent { | |
super(props); | ||
|
||
this.state = { | ||
imageWidth: 100, | ||
imageHeight: 100, | ||
thumbnailWidth: 100, | ||
thumbnailHeight: 100, | ||
imageWidth: undefined, | ||
imageHeight: undefined, | ||
interactionPromise: undefined, | ||
}; | ||
|
||
// Use the default double click interval from the ImageZoom library | ||
|
@@ -41,6 +45,34 @@ class ImageView extends PureComponent { | |
}); | ||
} | ||
|
||
componentDidMount() { | ||
// Wait till animations are over to prevent stutter in navigation animation | ||
this.state.interactionPromise = InteractionManager.runAfterInteractions(() => this.calculateImageSize()); | ||
} | ||
|
||
componentWillUnmount() { | ||
if (!this.state.interactionPromise) { | ||
return; | ||
} | ||
this.state.interactionPromise.cancel(); | ||
} | ||
|
||
calculateImageSize() { | ||
if (!this.props.url) { | ||
return; | ||
} | ||
ImageSize.getSize(this.props.url).then(({width, height}) => { | ||
let imageWidth = width; | ||
let imageHeight = height; | ||
const aspectRatio = (imageHeight / imageWidth); | ||
if (imageWidth > this.props.windowWidth) { | ||
imageWidth = Math.round(this.props.windowWidth); | ||
imageHeight = Math.round(imageWidth * aspectRatio); | ||
} | ||
this.setState({imageHeight, imageWidth}); | ||
}); | ||
} | ||
|
||
/** | ||
* Updates the amount of active touches on the PanResponder on our ImageZoom overlay View | ||
* | ||
|
@@ -60,6 +92,30 @@ class ImageView extends PureComponent { | |
render() { | ||
// Default windowHeight accounts for the modal header height | ||
const windowHeight = this.props.windowHeight - variables.contentHeaderHeight; | ||
|
||
// Display thumbnail until Image size calculation is complete | ||
if (!this.state.imageWidth || !this.state.imageHeight) { | ||
return ( | ||
<View | ||
style={[ | ||
styles.w100, | ||
styles.h100, | ||
styles.alignItemsCenter, | ||
styles.justifyContentCenter, | ||
styles.overflowHidden, | ||
styles.errorOutline, | ||
]} | ||
> | ||
<Image | ||
source={{uri: this.props.url}} | ||
style={StyleUtils.getWidthAndHeightStyle(this.state.thumbnailWidth, this.state.thumbnailHeight)} | ||
resizeMode={Image.resizeMode.contain} | ||
/> | ||
</View> | ||
); | ||
} | ||
|
||
// Zoom view should be loaded only after measuring actual image dimensions, otherwise it causes blurred images on Android | ||
return ( | ||
<View | ||
style={[ | ||
|
@@ -103,21 +159,14 @@ class ImageView extends PureComponent { | |
this.imageZoomScale = scale; | ||
}} | ||
> | ||
<ImageWithSizeCalculation | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmmm I wonder if we should move all this logic to this component instead... what's the value of keeping both? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh ok ok, no, this is good. |
||
style={StyleUtils.getWidthAndHeightStyle(this.state.imageWidth, this.state.imageHeight)} | ||
url={this.props.url} | ||
onMeasure={({width, height}) => { | ||
let imageWidth = width; | ||
let imageHeight = height; | ||
|
||
if (width > this.props.windowWidth || height > windowHeight) { | ||
const scaleFactor = Math.max(width / this.props.windowWidth, height / windowHeight); | ||
imageHeight = height / scaleFactor; | ||
imageWidth = width / scaleFactor; | ||
} | ||
|
||
this.setState({imageHeight, imageWidth}); | ||
}} | ||
<Image | ||
style={[ | ||
styles.w100, | ||
styles.h100, | ||
this.props.style, | ||
]} | ||
source={{uri: this.props.url}} | ||
resizeMode={Image.resizeMode.contain} | ||
/> | ||
{/** | ||
Create an invisible view on top of the image so we can capture and set the amount of touches before | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aswin-s Do you recall the production steps for the animation bug here? We are looking to remove
InteractionManager.runAfterInteractions
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow this was over 2 years ago. If I remember correctly trying to calculate image size while animating the modal was causing frozen frames on android device. But this component has gone through multiple refactors after this change and even react-native-fast-image was forked to improve load performance. So this optimisation might not be required anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!