Admin 发布的文章

  1. 新建react native项目

    npx create-expo-app -t expo-template-blank-typescript
  2. 根据教程配置https://github.com/expo/config-plugins/tree/main/packages/ffmpeg-kit-react-native

    1. 安装依赖

      yarn add ffmpeg-kit-react-native @config-plugins/ffmpeg-kit-react-native expo-build-properties

      github教程里没说要装expo-build-properties,导致后续步骤报错

    2. app.json里添加

      "plugins": [
        [
          "@config-plugins/ffmpeg-kit-react-native",
          {
            "package": "full-gpl"
          }
        ]
      ]

      关于full-gpl含义:https://github.com/tanersener/mobile-ffmpeg/releases

  3. 根据上述生成ios和android文件夹

    expo prebuild

    ios/Podfile里的iOS版本太旧会导致编译失败,可根据ffmpeg sdk的官方要求更改版本号:https://www.npmjs.com/package/ffmpeg-kit-react-native

  4. App.tsx编写如下代码

    import { StatusBar } from 'expo-status-bar';
    import { StyleSheet, Text, View } from 'react-native';
    import { FFmpegKit, ReturnCode } from 'ffmpeg-kit-react-native'
    import { useEffect } from 'react';
    import * as FileSystem from 'expo-file-system'
    
    export default function App() {
      useEffect(()=>{
        FFmpegKit.execute(`-i https://static-b905bdbb-5254-4483-af4c-16e5bf477a2e.bspapp.com/mpd/wolf.mpd -c copy ${FileSystem.documentDirectory}/${Math.random()}.mp4`)
        .then(async (session) => {
          const returnCode = await session.getReturnCode()
          if (ReturnCode.isSuccess(returnCode)) {
            console.log('SUCCESS')
          } else if (ReturnCode.isCancel(returnCode)) {
            console.log('CANCEL')
          } else {
            console.log('ERROR')
          }
        })
      }, [])
    
      return (
        <View style={styles.container}>
          <Text>Open up App.tsx to start working on your app!</Text>
          <StatusBar style="auto" />
        </View>
      );
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        backgroundColor: '#fff',
        alignItems: 'center',
        justifyContent: 'center',
      },
    });
  5. 云端编译并在iOS虚拟机上运行

    yarn ios

    看到SUCCESS的log说明ffmpeg运行成功

import BigNumber from "bignumber.js"
export function bigNumberFloor(num: string|number|BigNumber, decimals: number): string {
  return new BigNumber(num).times(10**decimals).idiv(1).div(10**decimals).toFormat()
}
export function bigNumberCeil(num: string|number|BigNumber, decimals: number): BigNumber {
  return new BigNumber(num).times(10**decimals).integerValue(BigNumber.ROUND_CEIL).div(10**decimals)
}

React Native Webview组件没有自带的方式可以监听网页内部资源的加载(jpg, png, js, css, mp3等),曲线实现方式:用js注入定时器到网页,网页里获取节点的src属性并postMessage给native,以img节点为例:

import { WebView } from 'react-native-webview'
let srcs:string[] = []
export default function WebviewScreen() {
  return (
    <WebView 
      source={{ uri: 'https://www.bing.com/images' }}
      useWebKit={true}
      allowsBackForwardNavigationGestures
      allowsInlineMediaPlayback
      injectedJavaScriptBeforeContentLoaded = {`
        setInterval(()=>{
          const nodes = document.querySelectorAll('img')
          for (const node of nodes) {
            window.ReactNativeWebView.postMessage(node.src)
          }
        }, 2000)
      `}
      onMessage = {msg=>{
        const src = msg.nativeEvent?.data?.trim()
        if (src && -1===srcs.indexOf(src)) {
          console.log('onMessage', new Date(), src)
          srcs.push(src)
        }
      }}
    />
  );
}

也可以用于video等其它带有src的节点
如果是video,src可能在它的子节点srouce里,而且可能有多个,所以需要这么写:

setInterval(() => {
  const videoElems = document.querySelectorAll('video')
  for (const videoElem of videoElems || []) {
    window.ReactNativeWebView?.postMessage(videoElem.src)
    for (const courceElem of videoElem.getElementsByTagName('source') || []) {
      window.ReactNativeWebView?.postMessage(courceElem.src)
    }
  }
}, 1000)

缺点:

  1. 只能定时不能监听
  2. 如果网页里用到跨域的iframe,则iframe里的resource无法被获取

  1. 很多时候,input只关心最后状态,不需要用useState
    请输入图片描述
  2. 有时候并不需要用useEffect去改变值
    请输入图片描述
  3. useMemo防止object在重新渲染时再内部值不变时被更换内存地址,变成新“值”
    请输入图片描述
  4. 适时终止过时的api请求
    请输入图片描述