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

以下组件与 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