React Native Youtube Iframe 在 Android 9.1 phone 下不断闪烁
React Native Youtube Iframe is flickering constantly under an Android 9.1 phone
视频在 Android 下的华为 phone 上闪烁 9. phone 与其他应用播放 youtube 视频和其他 phone 没有问题测试没有这个问题。
https://www.youtube.com/watch?v=iEhmZsQz0HM
- 设备:华为MHA-L29
- OS + 版本:EMUI Android 9.1.0.275
- "react-native": "^0.63.2",
- "react-native-youtube-iframe": "^1.3.0",
- "react-native-webview": "^10.8.3",
- 没有博览会
以下组件与 React Navigation 底部选项卡一起传递:
import React, { FC, useCallback, useContext, useEffect, useState } from 'react'
import { PixelRatio, Platform, StatusBar, StyleSheet, TouchableOpacity, View, ViewStyle } from 'react-native'
import YoutubePlayer from 'react-native-youtube-iframe'
import { colors, MediaContext, paths } from '../../apps'
import SvgPath from '../../components/SvgPath'
import Orientation from 'react-native-orientation-locker'
import useOrientation from '../orientation/useOrientation'
import { useDimensions } from '@react-native-community/hooks'
// Due to React Native issue the Dimension API crash randomly in React Native 0.63
// The workaround is:
// - not to use useWindowsDimensions
// - use instead useDimensions.screen
// https://github.com/facebook/react-native/issues/29451
// https://github.com/wonday/react-native-orientation-locker/issues/71
/**
* Drawer that holds the media when not full screen
*/
const MediaPlayerBox: FC = () => {
/**
* This is just a classical Context API
*/
const {
postMediaUrl,
setPostMediaUrl,
postMediaInfos,
setPostMediaInfos,
postMediaInfosChecked,
setPostMediaInfosChecked,
postMediaButton,
setPostMediaButton,
playerMediaInfos,
setPlayerMediaInfos,
mediaPlayerIsUp,
setMediaPlayerIsUp,
playerFullScreen,
setPlayerFullScreen,
getInfosFromUrl,
onPressPlay,
onPressStop,
playerState,
setPlayerState,
toggleFullScreen,
} = useContext(MediaContext)
/** This is an interface to React Native Orientation Locker */
const {
requestedOrientationLock,
setRequestedOrientationLock,
orientation,
deviceOrientation,
orientationLock,
androidRotationLock,
lockToPortrait,
lockToLandscape,
isLocked,
unlockAllOrientations,
fixDimensions,
} = useOrientation()
/** settle the dimensions according to the screen */
const { width, height } = useDimensions().screen
const ratio16per9 = PixelRatio.roundToNearestPixel(width / (16 / 9))
const [playerViewStyle, setPlayerViewStyle] = useState<ViewStyle | null>(null)
const [playerHeight, setPlayerHeight] = useState(ratio16per9)
const [playerWidth, setPlayerWidth] = useState(width)
/** Change the layout when the player goes fullscreen */
useEffect(() => {
// un mount protection
let mounted = true
// constants
const boxedViewStyle: ViewStyle = {
bottom: 90,
elevation: 5,
position: 'absolute',
right: 0,
left: 0,
shadowColor: colors.shadow,
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
zIndex: 1,
}
const fullscreenViewStyle: ViewStyle = {
position: 'absolute',
top: 0,
right: 0,
left: 0,
bottom: 0,
zIndex: 2,
elevation: 6,
backgroundColor: colors.deepBlack,
}
// This was tested but there is some bugs lefts regarding size and positioning
const fullscreenViewStyleRotated: ViewStyle = {
transform: [{ rotate: '90deg' }],
position: 'absolute',
flex: 1,
right: -width / 2,
top: +width / 2,
width: height,
height: width,
zIndex: 2,
elevation: 6,
backgroundColor: colors.deepBlack,
}
// Do the magic trick to change everything
if (mounted) {
if (playerFullScreen) {
lockToLandscape() // View horizontal
setPlayerViewStyle(fullscreenViewStyle)
setPlayerWidth(PixelRatio.roundToNearestPixel(width / (9 / 16)))
setPlayerHeight(height)
} else {
lockToPortrait() // View
setPlayerViewStyle(boxedViewStyle)
setPlayerWidth(width)
setPlayerHeight(PixelRatio.roundToNearestPixel(width / (16 / 9)))
}
}
return (): void => {
Orientation.lockToPortrait()
mounted = false
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [playerFullScreen, height, width])
/** Listen to state changes of the player */
const onStateChange = useCallback(
(state) => {
setPlayerState(state)
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[],
)
// When closing with the cross
const onClose = (): void => {
onPressStop()
}
if (mediaPlayerIsUp && playerMediaInfos.id)
return (
<View style={[styles.playerView, playerViewStyle]}>
{playerFullScreen && <StatusBar hidden={true} />}
<YoutubePlayer
videoId={playerMediaInfos.id || 'xuCn8ux2gbs'} // The YouTube video ID}
play={mediaPlayerIsUp} // control playback of video with true/false
onChangeState={onStateChange} // detects state change
height={playerHeight}
width={playerWidth}
initialPlayerParams={{
// eslint-disable-next-line @typescript-eslint/camelcase
iv_load_policy: 3,
modestbranding: true,
preventFullScreen: true,
}}
onError={(error): void => {
console.log('ERROR > MediaPlayerBox > YoutubePlayer: ', error)
}}
forceAndroidAutoplay={Platform.OS === 'android'}
/>
<View style={styles.fullScreen}>
<TouchableOpacity activeOpacity={0.8} onPress={toggleFullScreen}>
<SvgPath path={paths.mdiFullscreen} round={false} fillColor={colors.darkGrey} size={40} />
</TouchableOpacity>
</View>
<View style={styles.close}>
<TouchableOpacity activeOpacity={0.8} onPress={onClose}>
<SvgPath path={paths.mdiClose} round={false} fillColor={colors.darkGrey} size={40} />
</TouchableOpacity>
</View>
</View>
)
return null
}
/// Styles ///
const styles = StyleSheet.create({
close: {
position: 'absolute',
right: 10,
top: 10,
zIndex: 7,
},
fullScreen: {
position: 'absolute',
left: 10,
top: 10,
zIndex: 7,
},
playerView: {
bottom: 90,
elevation: 5,
position: 'absolute',
shadowColor: colors.shadow,
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
zIndex: 1,
alignItems: 'center',
},
})
export default MediaPlayerBox
您的视频在我的 Mate 9 上运行良好 phone,我使用的是与您在应用中相同的 react-native-youtube-iframe 和 react-native-webview。您可以在以下位置找到演示视频:here
视频在 Android 下的华为 phone 上闪烁 9. phone 与其他应用播放 youtube 视频和其他 phone 没有问题测试没有这个问题。
https://www.youtube.com/watch?v=iEhmZsQz0HM
- 设备:华为MHA-L29
- OS + 版本:EMUI Android 9.1.0.275
- "react-native": "^0.63.2",
- "react-native-youtube-iframe": "^1.3.0",
- "react-native-webview": "^10.8.3",
- 没有博览会
以下组件与 React Navigation 底部选项卡一起传递:
import React, { FC, useCallback, useContext, useEffect, useState } from 'react'
import { PixelRatio, Platform, StatusBar, StyleSheet, TouchableOpacity, View, ViewStyle } from 'react-native'
import YoutubePlayer from 'react-native-youtube-iframe'
import { colors, MediaContext, paths } from '../../apps'
import SvgPath from '../../components/SvgPath'
import Orientation from 'react-native-orientation-locker'
import useOrientation from '../orientation/useOrientation'
import { useDimensions } from '@react-native-community/hooks'
// Due to React Native issue the Dimension API crash randomly in React Native 0.63
// The workaround is:
// - not to use useWindowsDimensions
// - use instead useDimensions.screen
// https://github.com/facebook/react-native/issues/29451
// https://github.com/wonday/react-native-orientation-locker/issues/71
/**
* Drawer that holds the media when not full screen
*/
const MediaPlayerBox: FC = () => {
/**
* This is just a classical Context API
*/
const {
postMediaUrl,
setPostMediaUrl,
postMediaInfos,
setPostMediaInfos,
postMediaInfosChecked,
setPostMediaInfosChecked,
postMediaButton,
setPostMediaButton,
playerMediaInfos,
setPlayerMediaInfos,
mediaPlayerIsUp,
setMediaPlayerIsUp,
playerFullScreen,
setPlayerFullScreen,
getInfosFromUrl,
onPressPlay,
onPressStop,
playerState,
setPlayerState,
toggleFullScreen,
} = useContext(MediaContext)
/** This is an interface to React Native Orientation Locker */
const {
requestedOrientationLock,
setRequestedOrientationLock,
orientation,
deviceOrientation,
orientationLock,
androidRotationLock,
lockToPortrait,
lockToLandscape,
isLocked,
unlockAllOrientations,
fixDimensions,
} = useOrientation()
/** settle the dimensions according to the screen */
const { width, height } = useDimensions().screen
const ratio16per9 = PixelRatio.roundToNearestPixel(width / (16 / 9))
const [playerViewStyle, setPlayerViewStyle] = useState<ViewStyle | null>(null)
const [playerHeight, setPlayerHeight] = useState(ratio16per9)
const [playerWidth, setPlayerWidth] = useState(width)
/** Change the layout when the player goes fullscreen */
useEffect(() => {
// un mount protection
let mounted = true
// constants
const boxedViewStyle: ViewStyle = {
bottom: 90,
elevation: 5,
position: 'absolute',
right: 0,
left: 0,
shadowColor: colors.shadow,
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
zIndex: 1,
}
const fullscreenViewStyle: ViewStyle = {
position: 'absolute',
top: 0,
right: 0,
left: 0,
bottom: 0,
zIndex: 2,
elevation: 6,
backgroundColor: colors.deepBlack,
}
// This was tested but there is some bugs lefts regarding size and positioning
const fullscreenViewStyleRotated: ViewStyle = {
transform: [{ rotate: '90deg' }],
position: 'absolute',
flex: 1,
right: -width / 2,
top: +width / 2,
width: height,
height: width,
zIndex: 2,
elevation: 6,
backgroundColor: colors.deepBlack,
}
// Do the magic trick to change everything
if (mounted) {
if (playerFullScreen) {
lockToLandscape() // View horizontal
setPlayerViewStyle(fullscreenViewStyle)
setPlayerWidth(PixelRatio.roundToNearestPixel(width / (9 / 16)))
setPlayerHeight(height)
} else {
lockToPortrait() // View
setPlayerViewStyle(boxedViewStyle)
setPlayerWidth(width)
setPlayerHeight(PixelRatio.roundToNearestPixel(width / (16 / 9)))
}
}
return (): void => {
Orientation.lockToPortrait()
mounted = false
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [playerFullScreen, height, width])
/** Listen to state changes of the player */
const onStateChange = useCallback(
(state) => {
setPlayerState(state)
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[],
)
// When closing with the cross
const onClose = (): void => {
onPressStop()
}
if (mediaPlayerIsUp && playerMediaInfos.id)
return (
<View style={[styles.playerView, playerViewStyle]}>
{playerFullScreen && <StatusBar hidden={true} />}
<YoutubePlayer
videoId={playerMediaInfos.id || 'xuCn8ux2gbs'} // The YouTube video ID}
play={mediaPlayerIsUp} // control playback of video with true/false
onChangeState={onStateChange} // detects state change
height={playerHeight}
width={playerWidth}
initialPlayerParams={{
// eslint-disable-next-line @typescript-eslint/camelcase
iv_load_policy: 3,
modestbranding: true,
preventFullScreen: true,
}}
onError={(error): void => {
console.log('ERROR > MediaPlayerBox > YoutubePlayer: ', error)
}}
forceAndroidAutoplay={Platform.OS === 'android'}
/>
<View style={styles.fullScreen}>
<TouchableOpacity activeOpacity={0.8} onPress={toggleFullScreen}>
<SvgPath path={paths.mdiFullscreen} round={false} fillColor={colors.darkGrey} size={40} />
</TouchableOpacity>
</View>
<View style={styles.close}>
<TouchableOpacity activeOpacity={0.8} onPress={onClose}>
<SvgPath path={paths.mdiClose} round={false} fillColor={colors.darkGrey} size={40} />
</TouchableOpacity>
</View>
</View>
)
return null
}
/// Styles ///
const styles = StyleSheet.create({
close: {
position: 'absolute',
right: 10,
top: 10,
zIndex: 7,
},
fullScreen: {
position: 'absolute',
left: 10,
top: 10,
zIndex: 7,
},
playerView: {
bottom: 90,
elevation: 5,
position: 'absolute',
shadowColor: colors.shadow,
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
zIndex: 1,
alignItems: 'center',
},
})
export default MediaPlayerBox
您的视频在我的 Mate 9 上运行良好 phone,我使用的是与您在应用中相同的 react-native-youtube-iframe 和 react-native-webview。您可以在以下位置找到演示视频:here