React Native Expo如何给视频添加水印并捕获分享?
解决方案:React Native Expo 视频加水印并分享
问题根源
react-native-view-shot 仅能捕获React Native JS层渲染的视图内容,而视频组件(如expo-av的Video)属于原生层渲染,无法被快照捕获,因此会出现黑图。针对视频的水印分享需求,可根据实际场景选择以下方案:
方案1:分享带水印的视频静态截图
若需求是分享视频当前帧的截图(带水印),可通过「捕获视频帧 + 合成水印」实现:
步骤1:安装依赖
expo install expo-av expo-image-manipulator react-native-view-shot
步骤2:组件代码实现
import React, { useRef } from 'react'; import { View, Text, Image, Share, Button } from 'react-native'; import { Video } from 'expo-av'; import { captureRef } from 'react-native-view-shot'; import { manipulateAsync, SaveFormat } from 'expo-image-manipulator'; export default function PostShareComponent({ post }) { const viewRef = useRef(null); const videoRef = useRef(null); const watermarkRef = useRef(null); const share = async () => { if (post.type === 'photo') { // 保留原有照片处理逻辑 const viewShot = await captureRef(viewRef, { format: 'png', quality: 0.8, }); await Share.share({ url: viewShot }); } else if (post.type === 'video') { // 1. 捕获视频当前帧截图 const videoSnapshot = await videoRef.current.takeSnapshotAsync({ format: 'png', quality: 0.8, }); // 2. 捕获水印文字的视图快照 const watermarkSnapshot = await captureRef(watermarkRef, { format: 'png', quality: 1.0, }); // 3. 合成视频截图与水印(可根据实际截图尺寸调整坐标) const finalImage = await manipulateAsync( videoSnapshot, [ { overlay: { uri: watermarkSnapshot, position: { x: 600 - 220, // 截图宽度 - 水印宽度 - 右边距 y: 800 - 40, // 截图高度 - 水印高度 - 下边距 }, }, }, ], { format: SaveFormat.PNG, quality: 0.8 } ); // 4. 分享处理后的图片 await Share.share({ url: finalImage.uri }); } }; return ( <View style={{ flex: 1 }}> {/* 主内容视图 */} <View ref={viewRef} style={{ height: '100%', width: '100%', backgroundColor: 'black' }} > {post.type === 'video' ? ( <Video ref={videoRef} source={{ uri: post.url }} rate={1.0} volume={1.0} isMuted={false} resizeMode="cover" shouldPlay isLooping style={{ width: '100%', height: '100%' }} /> ) : ( <Image source={{ uri: post.url }} style={{ width: '100%', height: '100%' }} /> )} </View> {/* 单独的水印视图(用于捕获快照) */} <View ref={watermarkRef} style={{ position: 'absolute', bottom: 10, right: 10, backgroundColor: 'transparent' }} > <Text style={{ color: 'white', fontSize: 20 }}> SocialName.com/username </Text> </View> <Button title="分享" onPress={share} /> </View> ); }
方案2:分享带水印的完整视频文件
若需求是分享带水印的视频本身,需使用expo-ffmpeg对视频文件进行水印添加处理:
步骤1:安装依赖
expo install expo-ffmpeg expo-file-system expo-av
步骤2:视频加水印与分享实现
import React, { useRef } from 'react'; import { View, Text, Image, Share, Button } from 'react-native'; import { Video } from 'expo-av'; import * as FileSystem from 'expo-file-system'; import * as FFmpeg from 'expo-ffmpeg'; export default function PostShareComponent({ post }) { const videoRef = useRef(null); const shareVideoWithWatermark = async () => { if (post.type !== 'video') return; // 输入视频路径与输出路径 const inputUri = post.url; const outputUri = `${FileSystem.documentDirectory}watermarked_video.mp4`; // FFmpeg命令:添加右下角白色文字水印(字号20,边距10) const ffmpegCommand = `-i ${inputUri} -vf "drawtext=text='SocialName.com/username':fontsize=20:fontcolor=white:x=w-tw-10:y=h-th-10" -c:a copy ${outputUri}`; // 执行视频处理命令 const result = await FFmpeg.executeAsync(ffmpegCommand); if (result.returnCode === 0) { // 分享处理后的视频 await Share.share({ url: outputUri }); } else { console.error('视频加水印失败:', result.stderr); } }; return ( <View style={{ flex: 1 }}> {post.type === 'video' ? ( <Video ref={videoRef} source={{ uri: post.url }} rate={1.0} volume={1.0} isMuted={false} resizeMode="cover" shouldPlay isLooping style={{ width: '100%', height: '100%' }} /> ) : ( <Image source={{ uri: post.url }} style={{ width: '100%', height: '100%' }} /> )} <Button title="分享带水印视频" onPress={shareVideoWithWatermark} /> </View> ); }
注意事项
- 视频处理耗时较长,建议添加加载状态提示
- Android平台需确保文件系统权限配置正确
- FFmpeg命令中
x=w-tw-10表示水印右边距10,y=h-th-10表示下边距10(w为视频宽度,tw为文字宽度,h为视频高度,th为文字高度)
内容的提问来源于stack exchange,提问作者stormy TV
相关产品推荐
相关产品推荐

