CusVideo.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. import React, { Component } from "react";
  2. import {
  3. Platform,
  4. Text,
  5. View,
  6. ImageBackground,
  7. Button,
  8. Image,
  9. TouchableOpacity,
  10. PanResponder,
  11. StyleSheet
  12. } from "react-native";
  13. import { createStackNavigator, createAppContainer } from "react-navigation";
  14. import Video from "react-native-video";
  15. import SeekBar from "../components/SeekBar";
  16. const instructions = Platform.select({
  17. ios: "Press Cmd+R to reload,\n" + "Cmd+D or shake for dev menu",
  18. android:
  19. "Double tap R on your keyboard to reload,\n" +
  20. "Shake or press menu button for dev menu"
  21. });
  22. type Props = {};
  23. export default class CusVideo extends React.Component {
  24. constructor(props) {
  25. super(props);
  26. this.pressStatus = false;
  27. this.progress = 0;
  28. this.time_hideController;
  29. }
  30. config = {
  31. changeX: 0,
  32. changeY: 0,
  33. xDiff: 0,
  34. yDiff: 0
  35. };
  36. state = {
  37. player_status_icon: require("../images/video/start.png"),
  38. rate: 1,
  39. volume: 1,
  40. muted: false,
  41. resizeMode: "stretch",
  42. duration: 0.0,
  43. currentTime: 0.0,
  44. paused: true,
  45. onBuffer: true,
  46. wheel: false,
  47. isFull: false,
  48. needback: this.props.needback,
  49. show_controller: true,
  50. show_loading: true,
  51. loading_arr: [
  52. require("../images/video/loading1.png"),
  53. require("../images/video/loading2.png"),
  54. require("../images/video/loading3.png"),
  55. require("../images/video/loading4.png"),
  56. require("../images/video/loading5.png"),
  57. require("../images/video/loading6.png"),
  58. require("../images/video/loading7.png"),
  59. require("../images/video/loading8.png"),
  60. require("../images/video/loading9.png"),
  61. require("../images/video/loading10.png"),
  62. require("../images/video/loading11.png"),
  63. require("../images/video/loading12.png"),
  64. require("../images/video/loading13.png"),
  65. require("../images/video/loading14.png"),
  66. require("../images/video/loading15.png"),
  67. require("../images/video/loading16.png"),
  68. require("../images/video/loading17.png"),
  69. require("../images/video/loading18.png"),
  70. require("../images/video/loading19.png"),
  71. require("../images/video/loading20.png")
  72. ]
  73. };
  74. render() {
  75. if (!this.props.show) {
  76. return null;
  77. }
  78. return (
  79. <View style={[this.props.style, { overflow: "hidden" }]}>
  80. <Video
  81. {...this.videotouch.panHandlers}
  82. style={{ flex: 1 }}
  83. source={{
  84. uri: this.props.uri
  85. }}
  86. ref={ref => {
  87. this.player = ref;
  88. }}
  89. // poster={this.props.poster}
  90. resizeMode={[this.state.resizeMode]}
  91. // posterResizeMode={this.state.resizeMode}
  92. onBuffer={this.onBuffer.bind(this)}
  93. rate={this.state.rate} //播放速率
  94. paused={this.state.paused} //暂停
  95. volume={this.state.volume} //调节音量
  96. muted={this.state.muted} //控制音频是否静音
  97. resizeMode={this.state.resizeMode} //缩放模式
  98. onLoadStart={this.loadStart} // 当视频开始加载时的回调函数
  99. onLoad={this.onLoad} //加载媒体并准备播放时调用的回调函数。
  100. onProgress={this.onProgress} //视频播放过程中每个间隔进度单位调用的回调函数
  101. onAudioBecomingNoisy={this.onAudioBecomingNoisy} //音频变得嘈杂时的回调 - 应暂停视频
  102. onAudioFocusChanged={this.onAudioFocusChanged} //音频焦点丢失时的回调 - 如果焦点丢失则暂停
  103. repeat={this.state.wheel} //确定在到达结尾时是否重复播放视频。
  104. onError={this.onError.bind(this)} // 当视频不能加载,或出错后的回调函数
  105. onEnd={this.onEnd.bind(this)} //视频播放结束时的回调函数
  106. playInBackground={false} // 当app转到后台运行的时候,播放是否暂停
  107. playWhenInactive={true} // [iOS] Video continues to play when control or notification center are shown. 仅适用于IOS
  108. />
  109. <View
  110. style={{
  111. position: "absolute",
  112. width: "100%",
  113. height: "100%",
  114. alignItems: "center",
  115. justifyContent: "center"
  116. }}
  117. >
  118. <Loading
  119. loading_arr={this.state.loading_arr}
  120. show={this.state.show_loading}
  121. />
  122. </View>
  123. <TopController
  124. ref={view => (this.topcontroller = view)}
  125. needback={this.state.needback}
  126. videoback={this.videoBackClick.bind(this)}
  127. />
  128. <BottomController
  129. ref={view => (this.bottomcontroller = view)}
  130. show={this.state.show_controller}
  131. touch_up_callback={this.touch_up_callback.bind(this)}
  132. full_click={this.presentFullscreenPlayer.bind(this)}
  133. play_click={this.play.bind(this)}
  134. duration={this.state.duration}
  135. currentTime={this.state.currentTime}
  136. player_status_icon={this.state.player_status_icon}
  137. />
  138. </View>
  139. );
  140. }
  141. componentWillMount() {
  142. this.videotouch = PanResponder.create({
  143. onStartShouldSetPanResponder: (evt, gestureState) => true,
  144. onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
  145. onMoveShouldSetPanResponder: (evt, gestureState) => true,
  146. onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
  147. onPanResponderGrant: (evt, gestureState) => {
  148. this.pressStatus = true;
  149. this.config.changeY = evt.nativeEvent.pageY;
  150. this.config.changeX = evt.nativeEvent.pageX;
  151. if (this.state.show_controller) {
  152. this.hideController();
  153. } else {
  154. this.showController();
  155. }
  156. },
  157. onPanResponderStart: (evt, gestureState) => {
  158. this.pressStatus = true;
  159. },
  160. onPanResponderMove: (evt, gestureState) => {
  161. this.config.yDiff = evt.nativeEvent.pageY - this.config.changeY;
  162. this.config.xDiff = evt.nativeEvent.pageX - this.config.changeX;
  163. this.config.changeY = evt.nativeEvent.pageY;
  164. this.config.changeX = evt.nativeEvent.pageX;
  165. },
  166. onPanResponderEnd: (evt, gestureState) => {
  167. this.pressStatus = true;
  168. },
  169. onPanResponderTerminationRequest: (evt, gestureState) => true,
  170. onPanResponderRelease: (evt, gestureState) => {
  171. if (this.pressStatus) {
  172. this.props.onPress && this.props.onPress();
  173. }
  174. },
  175. onPanResponderTerminate: (evt, gestureState) => {
  176. // 另一个组件已经成为了新的响应者,所以当前手势将被取消。
  177. },
  178. onShouldBlockNativeResponder: (evt, gestureState) => {
  179. // 返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者
  180. // 默认返回true。目前暂时只支持android。
  181. //基于业务交互场景,如果这里使用js事件处理,会导致容器不能左右滑动。所以设置成false.
  182. return false;
  183. }
  184. });
  185. }
  186. componentWillUnmount() {
  187. clearTimeout(this.time_hideController);
  188. }
  189. loadStart() {
  190. // alert("loadStart");
  191. }
  192. onBuffer({ isBuffering }: { isBuffering: boolean }) {
  193. //true为正在加载,false为没有加载,此处给loading提示
  194. this.setState({
  195. onBuffer: isBuffering,
  196. show_loading: isBuffering
  197. });
  198. }
  199. isPlaying() {
  200. if (this.state.onBuffer == false && this.state.paused == false) {
  201. return true;
  202. }
  203. return false;
  204. }
  205. onLoad = data => {
  206. //获取的是秒数
  207. this.setState({ duration: data.duration });
  208. // this.bottomcontroller.setMax(data.duration);
  209. };
  210. onProgress = data => {
  211. this.setState({
  212. currentTime: data.currentTime
  213. });
  214. if (this.state.show_controller && this.props.show) {
  215. this.bottomcontroller.setProgress(this.state.currentTime);
  216. this.bottomcontroller.setMax(this.state.duration);
  217. }
  218. };
  219. onError() {
  220. if (this.props.onEnd == undefined) {
  221. } else {
  222. this.props.onError();
  223. }
  224. }
  225. onEnd() {
  226. if (this.props.onEnd == undefined) {
  227. } else {
  228. this.props.onEnd();
  229. }
  230. }
  231. play() {
  232. //controller的play点击无法换图...
  233. if (this.state.paused) {
  234. this.start();
  235. } else {
  236. this.pause();
  237. }
  238. }
  239. pause() {
  240. this.setState({
  241. paused: true,
  242. player_status_icon: require("../images/video/start.png")
  243. });
  244. this.player_icon_index = 1;
  245. }
  246. start() {
  247. this.setState({
  248. paused: false,
  249. player_status_icon: require("../images/video/pause.png")
  250. });
  251. this.player_icon_index = 0;
  252. if (this.state.show_controller) {
  253. this.time_hideController = setTimeout(() => {
  254. this.setState({
  255. show_controller: false
  256. // needback: false
  257. });
  258. this.bottomcontroller.setBottom(-50);
  259. this.topcontroller.setTop(-50);
  260. }, 5000);
  261. }
  262. }
  263. seekTo(progress) {
  264. this.player.seek(progress);
  265. }
  266. presentFullscreenPlayer() {
  267. if (this.state.isFull) {
  268. this.setState({
  269. isFull: false,
  270. needback: this.props.needback
  271. });
  272. } else {
  273. this.setState({
  274. isFull: true,
  275. needback: true
  276. });
  277. }
  278. this.props.videofullScreenPlayer();
  279. this.bottomcontroller.setProgress(this.state.currentTime);
  280. }
  281. touch_up_callback(progress) {
  282. //抬起之后,获取算出来的progress
  283. this.setState({
  284. currentTime: progress
  285. });
  286. this.seekTo(progress);
  287. }
  288. refreshVideo() {
  289. this.setState({
  290. duration: 0,
  291. currentTime: 0
  292. });
  293. this.bottomcontroller.setProgress(0);
  294. }
  295. videoBackClick() {
  296. // if (this.props.needback != undefined && this.props.needback) {
  297. // }
  298. if (this.state.isFull) {
  299. //全屏状态下,变小屏
  300. this.presentFullscreenPlayer();
  301. } else {
  302. this.props.videoback();
  303. }
  304. }
  305. showController() {
  306. clearTimeout(this.time_hideController);
  307. this.setState({
  308. show_controller: true
  309. // needback: this.props.needback
  310. });
  311. this.bottomcontroller.setBottom(0);
  312. this.bottomcontroller.setProgress(this.state.currentTime);
  313. this.topcontroller.setTop(0);
  314. this.time_hideController = setTimeout(() => {
  315. this.setState({
  316. show_controller: false
  317. // needback: false
  318. });
  319. this.bottomcontroller.setBottom(-50);
  320. this.topcontroller.setTop(-50);
  321. }, 5000);
  322. }
  323. hideController() {
  324. this.setState({
  325. show_controller: false
  326. // needback: false
  327. });
  328. this.bottomcontroller.setBottom(-50);
  329. this.topcontroller.setTop(-50);
  330. }
  331. componentWillUnmount() {
  332. clearTimeout(this.time_hideController);
  333. }
  334. }
  335. /**
  336. * 将秒转换为 分:秒
  337. * s int 秒数
  338. */
  339. function formatTime(s) {
  340. //计算分钟
  341. //算法:将秒数除以60,然后下舍入,既得到分钟数
  342. var h;
  343. h = Math.floor(s / 60);
  344. //计算秒
  345. //算法:取得秒%60的余数,既得到秒数
  346. s = Math.round(s % 60);
  347. //将变量转换为字符串
  348. h += "";
  349. s += "";
  350. //如果只有一位数,前面增加一个0
  351. h = h.length == 1 ? "0" + h : h;
  352. s = s.length == 1 ? "0" + s : s;
  353. return h + ":" + s;
  354. }
  355. class TopController extends Component {
  356. state = {
  357. controller_top: 0
  358. };
  359. render() {
  360. if (this.props.needback == false) {
  361. return null;
  362. }
  363. return (
  364. <View
  365. style={[styles.player_controller, { top: this.state.controller_top }]}
  366. >
  367. <View
  368. style={{
  369. flex: 1,
  370. alignItems: "center"
  371. }}
  372. >
  373. <TouchableOpacity
  374. //点击事件放在这个组件里才管用
  375. style={{
  376. justifyContent: "center",
  377. width: "100%",
  378. height: "100%"
  379. }}
  380. onPress={() => this.props.videoback()}
  381. >
  382. <Image
  383. style={[styles.player_pause_icon]}
  384. source={require("../images/video/back.png")}
  385. />
  386. </TouchableOpacity>
  387. </View>
  388. <View style={{ flex: 2 }} />
  389. <View
  390. style={{
  391. flex: 9,
  392. overflow: "hidden"
  393. }}
  394. />
  395. <View style={{ flex: 2 }} />
  396. <View
  397. style={{
  398. flex: 1,
  399. alignItems: "center"
  400. }}
  401. />
  402. </View>
  403. );
  404. }
  405. setTop(top) {
  406. this.setState({
  407. controller_top: top
  408. });
  409. }
  410. }
  411. class Loading extends Component {
  412. constructor(props) {
  413. super(props);
  414. this.loading_index = 0;
  415. }
  416. state = {
  417. loading_img: this.props.loading_arr[0]
  418. };
  419. render() {
  420. if (this.props.show) {
  421. return (
  422. <Image
  423. source={this.state.loading_img}
  424. style={{ width: 100, height: 20 }}
  425. />
  426. );
  427. } else {
  428. return null;
  429. }
  430. }
  431. componentWillMount() {
  432. this.changeIndex();
  433. }
  434. componentWillUnmount() {
  435. clearTimeout(this.changeindex);
  436. }
  437. changeIndex() {
  438. this.changeindex = setTimeout(() => {
  439. if (this.loading_index > this.props.loading_arr.length - 2) {
  440. this.loading_index = 0;
  441. } else {
  442. this.loading_index = this.loading_index + 1;
  443. }
  444. this.setState({
  445. loading_img: this.props.loading_arr[this.loading_index]
  446. });
  447. this.changeIndex();
  448. }, 1);
  449. }
  450. }
  451. class BottomController extends Component {
  452. state = {
  453. controller_bottom: 0
  454. };
  455. render() {
  456. return (
  457. <View
  458. style={[
  459. styles.player_controller,
  460. { bottom: this.state.controller_bottom }
  461. ]}
  462. >
  463. <View
  464. style={{
  465. flex: 1,
  466. alignItems: "center"
  467. }}
  468. >
  469. {/* 暂停播放按钮 */}
  470. <TouchableOpacity
  471. //点击事件放在这个组件里才管用
  472. style={{
  473. justifyContent: "center",
  474. width: "100%",
  475. height: "100%"
  476. }}
  477. onPress={() => this.props.play_click()}
  478. >
  479. <Image
  480. style={[styles.player_pause_icon]}
  481. source={this.props.player_status_icon}
  482. />
  483. </TouchableOpacity>
  484. </View>
  485. <View style={{ flex: 2 }}>
  486. {/* //左侧当前播放时长 */}
  487. <Text style={[styles.player_time]}>
  488. {formatTime(this.props.currentTime)}
  489. </Text>
  490. </View>
  491. <View
  492. style={{
  493. flex: 9,
  494. overflow: "hidden"
  495. }}
  496. >
  497. {/* //中间进度条 */}
  498. <SeekBar
  499. style={{ flex: 1 }}
  500. ref={view => (this.seekbar = view)}
  501. //必须带此方法,作为滑动之后抬起的回调
  502. touchUpCallBack={this.props.touch_up_callback}
  503. />
  504. </View>
  505. <View style={{ flex: 2 }}>
  506. {/* //右侧总时长 */}
  507. <Text style={[styles.player_time]}>
  508. {formatTime(this.props.duration)}
  509. </Text>
  510. </View>
  511. <View
  512. style={{
  513. flex: 1,
  514. alignItems: "center"
  515. }}
  516. >
  517. <TouchableOpacity
  518. style={{
  519. justifyContent: "center",
  520. width: "100%",
  521. height: "100%"
  522. }}
  523. onPress={() => this.props.full_click()}
  524. >
  525. {/* //放大按钮 */}
  526. <Image
  527. style={[styles.fullscreen_icon]}
  528. source={require("../images/video/fullscreen.png")}
  529. />
  530. </TouchableOpacity>
  531. </View>
  532. </View>
  533. );
  534. }
  535. setMax(duration) {
  536. this.seekbar.setMax(duration);
  537. }
  538. getMax() {
  539. return this.seekbar.getMax();
  540. }
  541. setBottom(bottom) {
  542. this.setState({
  543. controller_bottom: bottom
  544. });
  545. }
  546. setProgress(currentTime) {
  547. this.seekbar.setProgress(currentTime);
  548. }
  549. }
  550. const styles = StyleSheet.create({
  551. player_controller: {
  552. flexDirection: "row",
  553. position: "absolute",
  554. width: "100%",
  555. height: 50,
  556. alignItems: "center",
  557. justifyContent: "center"
  558. },
  559. player_pause_icon: {
  560. width: "80%",
  561. resizeMode: "center",
  562. height: "40%",
  563. justifyContent: "center",
  564. alignItems: "center",
  565. left: 5
  566. },
  567. fullscreen_icon: {
  568. width: "80%",
  569. resizeMode: "center",
  570. height: "30%",
  571. justifyContent: "center",
  572. alignItems: "center"
  573. },
  574. player_time: {
  575. fontSize: 16,
  576. color: "white",
  577. alignItems: "center",
  578. justifyContent: "center",
  579. textAlignVertical: "center",
  580. textAlign: "center"
  581. }
  582. });
  583. /**
  584. 使用方法
  585. <CusVideo
  586. show={this.state.video_show} //是否显示
  587. uri={this.state.video_uri} //播放路径
  588. ref={view => (this.video = view)} //设置ID
  589. needback={false}
  590. videoback={() => alert("videoback")}
  591. videofullScreenPlayer={this.fullScreenPlayer.bind(this)}
  592. style={{
  593. left: this.state.x,
  594. top: this.state.y,
  595. width: this.state.video_width,
  596. height: this.state.video_height,
  597. overflow: "hidden",
  598. position: "absolute"
  599. }}
  600. />
  601. */