index.tsx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import { defineComponent, PropType } from 'vue';
  2. import styles from './index.module.less';
  3. import Plyr from 'plyr';
  4. import 'plyr/dist/plyr.css';
  5. import { Button, Icon, Loading, Toast } from 'vant';
  6. import TCPlayer from 'tcplayer.js';
  7. import 'tcplayer.js/dist/tcplayer.css';
  8. // import iconVideoPlay from '@/common/images/icon_video_play.png';
  9. // import { browser } from '@/helpers/utils';
  10. export default defineComponent({
  11. name: 'o-video',
  12. props: {
  13. setting: {
  14. type: Object,
  15. default: () => {}
  16. },
  17. controls: {
  18. type: Boolean,
  19. default: true
  20. },
  21. height: String,
  22. src: {
  23. type: String,
  24. default: ''
  25. },
  26. poster: {
  27. type: String,
  28. default: ''
  29. },
  30. styleValue: {
  31. type: Object,
  32. default: () => ({})
  33. },
  34. preload: {
  35. type: String as PropType<'auto' | 'metadata' | 'none'>,
  36. default: 'auto'
  37. },
  38. currentTime: {
  39. type: Boolean,
  40. default: true
  41. },
  42. playsinline: {
  43. type: Boolean,
  44. default: true
  45. },
  46. onPlay: {
  47. type: Function,
  48. default: () => {}
  49. }
  50. },
  51. emits: ['exitfullscreen'],
  52. data() {
  53. return {
  54. videoID: 'video' + Date.now() + Math.floor(Math.random() * 100),
  55. player: null as any,
  56. loading: true // 首次进入加载中
  57. };
  58. },
  59. mounted() {
  60. this._init();
  61. },
  62. methods: {
  63. _init() {
  64. // controls: [
  65. // 'play-large' , // 中间的大播放按钮
  66. // 'restart' , // 重新开始播放
  67. // 'rewind' , // 按寻道时间倒带(默认 10 秒)
  68. // 'play' , // 播放/暂停播放
  69. // 'fast-forward' , // 快进查找时间(默认 10 秒)
  70. // 'progress' , // 播放和缓冲的进度条和滑动条
  71. // 'current-time' , // 播放的当前时间
  72. // ' duration' , // 媒体的完整持续时间
  73. // 'mute' , // 切换静音
  74. // 'volume', // 音量控制
  75. // 'captions' , // 切换字幕
  76. // 'settings' , // 设置菜单
  77. // 'pip' , // 画中画(当前仅 Safari)
  78. // 'airplay' , // Airplay(当前仅 Safari)
  79. // 'download ' , // 显示一个下载按钮,其中包含指向当前源或您在选项中指定的自定义 URL 的链接
  80. // 'fullscreen' , // 切换全屏
  81. // ] ;
  82. const Button = TCPlayer.getComponent('Button');
  83. const BigPlayButton = TCPlayer.getComponent('BigPlayButton');
  84. BigPlayButton.prototype.createEl = function () {
  85. const el = Button.prototype.createEl.call(this);
  86. const _html =
  87. '<button><svg width="41px"height="41px"viewBox="0 0 41 41"version="1.1"xmlns="http://www.w3.org/2000/svg"xmlns:xlink="http://www.w3.org/1999/xlink"><g stroke="none"stroke-width="1"fill="none"fill-rule="evenodd"><g transform="translate(-167.000000, -155.000000)"><g transform="translate(0.000000, 85.000000)"><g transform="translate(158.000000, 70.000000)"><g transform="translate(9.000000, 0.000000)"><circle id="椭圆形"stroke="#FFFFFF"fill-opacity="0.1"fill="#D8D8D8"cx="20.5"cy="20.5"r="20"></circle><path d="M14.5483871,27.6859997 L14.5483871,13.4342349 C14.5480523,12.8729571 14.8729597,12.356555 15.3949624,12.0887034 C15.9169651,11.8208518 16.5522696,11.8445472 17.0503046,12.1504437 L28.6530473,19.2778563 C29.1119763,19.5602271 29.3887725,20.0426422 29.3887725,20.5601173 C29.3887725,21.0775924 29.1119763,21.5600075 28.6530473,21.8423783 L17.0503046,28.9697909 C16.5522696,29.2756874 15.9169651,29.2993828 15.3949624,29.0315312 C14.8729597,28.7636796 14.5480523,28.2472775 14.5483871,27.6859997 Z"id="路径"fill="#FFFFFF"fill-rule="nonzero"></path></g></g></g></g></g></svg></button>';
  88. el.appendChild(
  89. TCPlayer.dom.createEl('div', {
  90. className: 'vjs-button-icon',
  91. innerHTML: _html
  92. })
  93. );
  94. return el;
  95. };
  96. this.player = TCPlayer(this.videoID, {
  97. appID: '',
  98. controls: this.controls
  99. }); // player-container-id 为播放器容器 ID,必须与 html 中一致
  100. if (this.player) {
  101. this.player.src(this.src); // url 播放地址
  102. this.player.poster(this.poster || '');
  103. if (this.preload === 'none') {
  104. this.loading = false;
  105. }
  106. this.player.on('loadstart', () => {
  107. this.loading = false;
  108. this.domPlayVisibility(false);
  109. });
  110. this.player.on('play', () => {
  111. this.onPlay && this.onPlay(this.player);
  112. });
  113. this.player.on('fullscreenchange', () => {
  114. if (this.player.isFullscreen()) {
  115. console.log('fullscreen');
  116. const i = document.createElement('i');
  117. i.id = 'fullscreen-back';
  118. i.className = 'van-icon van-icon-arrow-left video-back';
  119. i.addEventListener('click', () => {
  120. this.player.exitFullscreen();
  121. });
  122. // console.log(document.getElementsByClassName('video-js'))
  123. document.getElementsByClassName('video-js')[0].appendChild(i);
  124. } else {
  125. console.log('exitfullscreen');
  126. const i = document.getElementById('fullscreen-back');
  127. i && i.remove();
  128. }
  129. });
  130. }
  131. },
  132. // 操作功能
  133. domPlayVisibility(hide = true) {
  134. const controls = document.querySelector('.vjs-big-play-button');
  135. const controls2 = document.querySelector('.vjs-control-bar');
  136. if (hide) {
  137. controls?.setAttribute('style', 'display:none');
  138. controls2?.setAttribute('style', 'display:none');
  139. } else {
  140. controls?.removeAttribute('style');
  141. setTimeout(() => {
  142. controls2?.removeAttribute('style');
  143. }, 200);
  144. }
  145. },
  146. onReplay() {
  147. this.player.currentTime(0);
  148. this.player.play();
  149. this.domPlayVisibility(false);
  150. },
  151. onStop() {
  152. this.player.currentTime(0);
  153. this.player.pause();
  154. }
  155. },
  156. unmounted() {
  157. this.player?.pause();
  158. this.player?.src('');
  159. this.player?.dispose();
  160. },
  161. render() {
  162. return (
  163. <div class={styles['video-container']}>
  164. <video
  165. ref="video"
  166. id={this.videoID}
  167. class={styles['video']}
  168. src={this.src}
  169. playsinline={this.playsinline}
  170. poster={this.poster}
  171. preload={this.preload}
  172. style={{ ...this.styleValue }}></video>
  173. {/* </div> */}
  174. {/* 加载视频使用 */}
  175. {/* {this.loading && (
  176. <div
  177. class={styles.loadingVideo}
  178. style={{
  179. height: this.height || '210px'
  180. }}>
  181. <Loading
  182. size={36}
  183. color="#FF8057"
  184. vertical
  185. style={{ height: '100%', justifyContent: 'center' }}>
  186. 加载中...
  187. </Loading>
  188. </div>
  189. )} */}
  190. </div>
  191. );
  192. }
  193. });