index.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  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 { Loading } from 'vant';
  6. import { browser } from '@/helpers/utils';
  7. export default defineComponent({
  8. name: 'm-video',
  9. props: {
  10. setting: {
  11. type: Object,
  12. default: () => ({})
  13. },
  14. controls: Boolean,
  15. height: String,
  16. src: {
  17. type: String,
  18. default: ''
  19. },
  20. poster: {
  21. type: String,
  22. default: ''
  23. },
  24. styleValue: {
  25. type: Object,
  26. default: () => ({})
  27. },
  28. preload: {
  29. type: String as PropType<'auto' | 'metadata' | 'none'>,
  30. default: 'auto'
  31. },
  32. currentTime: {
  33. type: Boolean,
  34. default: true
  35. },
  36. playsinline: {
  37. type: Boolean,
  38. default: true
  39. },
  40. preLoading: {
  41. type: Boolean,
  42. default: true
  43. }
  44. // onPlay: {
  45. // type: Function,
  46. // default: () => {}
  47. // }
  48. },
  49. emits: ['exitfullscreen', 'play', 'ready', 'enterfullscreen'],
  50. data() {
  51. return {
  52. player: null as any,
  53. loading: true // 首次进入加载中
  54. };
  55. },
  56. mounted() {
  57. this.loading = this.preLoading;
  58. this._init();
  59. },
  60. methods: {
  61. _init() {
  62. const controls = [
  63. 'play-large',
  64. 'play',
  65. 'progress',
  66. 'captions',
  67. 'fullscreen'
  68. ];
  69. // if (browser().isApp) {
  70. // controls.push('fullscreen');
  71. // }
  72. if (this.currentTime) {
  73. controls.push('current-time');
  74. }
  75. const params: any = {
  76. controls: controls,
  77. ...this.setting,
  78. invertTime: false
  79. };
  80. if (browser().iPhone) {
  81. params.fullscreen = {
  82. enabled: true,
  83. fallback: 'force',
  84. iosNative: true
  85. };
  86. }
  87. this.player = new Plyr((this as any).$refs.video, params);
  88. // fullscreen: {
  89. // enabled: true,
  90. // fallback: 'force',
  91. // iosNative: true
  92. // }
  93. this.player.elements.container
  94. ? (this.player.elements.container.style.height = this.height || '210px')
  95. : null;
  96. if (this.preload === 'none') {
  97. this.loading = false;
  98. }
  99. this.player.on('loadedmetadata', () => {
  100. this.loading = false;
  101. this.domPlayVisibility(false);
  102. });
  103. this.player.on('loadeddata', () => {
  104. this.$emit('ready', this.player);
  105. });
  106. this.player.on('play', () => {
  107. this.$emit('play', this.player);
  108. });
  109. this.player.on('enterfullscreen', () => {
  110. // console.log('fullscreen', this.player.elements);
  111. // const fragment = document.createDocumentFragment();
  112. // const i = document.createElement('i');
  113. // i.id = 'fullscreen-back';
  114. // i.className = 'van-icon van-icon-arrow-left video-back';
  115. // i.addEventListener('click', () => {
  116. // this.player.fullscreen.exit();
  117. // });
  118. // console.log(document.getElementsByClassName('plyr'), i);
  119. // fragment.appendChild(i);
  120. // // const parentNode = document.getElementsByClassName('plyr')[0];
  121. // // parentNode.insertBefore(fragment, parentNode.firstChild);
  122. // this.player.elements.container.appendChild(fragment);
  123. this.$emit('enterfullscreen');
  124. });
  125. this.player.on('exitfullscreen', () => {
  126. console.log('exitfullscreen');
  127. // const i = document.getElementById('fullscreen-back');
  128. // i && i.remove();
  129. this.$emit('exitfullscreen');
  130. });
  131. },
  132. // 操作功能
  133. domPlayVisibility(hide = true) {
  134. const controls = document.querySelector('.plyr__controls');
  135. const controls2 = document.querySelector('.plyr__control--overlaid');
  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. onStop() {
  147. this.player.stop();
  148. },
  149. onExitScreen() {
  150. this.player.fullscreen.active && this.player.fullscreen.exit();
  151. },
  152. onPlay() {
  153. this.player?.play();
  154. }
  155. },
  156. unmounted() {
  157. this.player?.destroy();
  158. },
  159. render() {
  160. return (
  161. <div class={styles['video-container']}>
  162. <video
  163. ref="video"
  164. class={styles['video']}
  165. src={this.src}
  166. playsinline={this.playsinline}
  167. poster={this.poster}
  168. preload={this.preload}
  169. style={{ ...this.styleValue }}></video>
  170. {/* </div> */}
  171. {/* 加载视频使用 */}
  172. {this.loading && (
  173. <div
  174. class={styles.loadingVideo}
  175. style={{
  176. height: this.height || '210px'
  177. }}>
  178. <Loading
  179. size={36}
  180. color="#FF8057"
  181. vertical
  182. style={{ height: '100%', justifyContent: 'center' }}>
  183. 加载中...
  184. </Loading>
  185. </div>
  186. )}
  187. </div>
  188. );
  189. }
  190. });