dom-video.vue 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. <!-- eslint-disable -->
  2. <template>
  3. <view
  4. v-html="videoHtml"
  5. id="dom-video"
  6. class="dom-video"
  7. :eventDrive="eventDrive"
  8. :change:eventDrive="domVideo.eventHandle"
  9. :videoSrc="videoSrc"
  10. :change:videoSrc="domVideo.srcChange"
  11. :videoProps="videoProps"
  12. :change:videoProps="domVideo.propsChange"
  13. :randomNum="randomNum"
  14. :change:randomNum="domVideo.randomNumChange"
  15. />
  16. </template>
  17. <script>
  18. export default {
  19. props: {
  20. src: {
  21. type: String,
  22. default: '',
  23. },
  24. autoplay: {
  25. type: Boolean,
  26. default: false,
  27. },
  28. loop: {
  29. type: Boolean,
  30. default: false,
  31. },
  32. controls: {
  33. type: Boolean,
  34. default: false,
  35. },
  36. objectFit: {
  37. type: String,
  38. default: 'contain',
  39. },
  40. muted: {
  41. type: Boolean,
  42. default: false,
  43. },
  44. poster: {
  45. type: String,
  46. default: '',
  47. },
  48. },
  49. // 数据状态
  50. data() {
  51. return {
  52. videoHtml: '',
  53. videoSrc: '',
  54. eventDrive: null,
  55. videoProps: {},
  56. randomNum: Math.floor(Math.random() * 100000000 + 1),
  57. };
  58. },
  59. watch: {
  60. // 监听视频资源文件更新
  61. src: {
  62. handler(val) {
  63. if (!val) return;
  64. this.initVideoHtml();
  65. setTimeout(() => {
  66. this.videoSrc = val;
  67. }, 0);
  68. },
  69. immediate: true,
  70. },
  71. // 监听首次加载
  72. autoplay: {
  73. handler(val) {
  74. this.videoProps.autoplay = val;
  75. },
  76. immediate: true,
  77. },
  78. },
  79. // 生命周期
  80. mounted() {
  81. this.initVideoHtml();
  82. },
  83. // 方法
  84. methods: {
  85. // 将video的事件传递给父组件
  86. videoEvent(data) {
  87. // console.log('向父组件传递事件 =>', data)
  88. this.$emit(data);
  89. },
  90. // 初始化视频
  91. initVideoHtml() {
  92. this.videoHtml = `<video
  93. src="${this.src}"
  94. id="dom-html-video_${this.randomNum}"
  95. class="dom-html-video"
  96. ${this.autoplay ? 'autoplay' : ''}
  97. ${this.loop ? 'loop' : ''}
  98. ${this.controls ? 'controls' : ''}
  99. ${this.muted ? 'muted' : ''}
  100. ${this.poster ? 'poster="' + this.poster + '"' : ''}
  101. preload="auto"
  102. playsinline
  103. webkit-playsinline
  104. width="100%"
  105. height="100%"
  106. style="object-fit: ${this.objectFit};padding:0;"
  107. >
  108. <source src="${this.src}" type="video/mp4">
  109. <source src="${this.src}" type="video/ogg">
  110. <source src="${this.src}" type="video/webm">
  111. </video>
  112. `;
  113. // console.log('视频html =>', this.videoHtml)
  114. },
  115. resetEventDrive() {
  116. this.eventDrive = null;
  117. },
  118. // 将service层的事件/数据 => 传递给renderjs层
  119. play() {
  120. this.eventDrive = 'play';
  121. },
  122. pause() {
  123. this.eventDrive = 'pause';
  124. },
  125. stop() {
  126. this.eventDrive = 'stop';
  127. },
  128. },
  129. };
  130. </script>
  131. <script module="domVideo" lang="renderjs">
  132. export default {
  133. data() {
  134. return {
  135. video: null,
  136. num: '',
  137. options: {}
  138. }
  139. },
  140. mounted() {
  141. this.initVideoEvent()
  142. },
  143. methods: {
  144. initVideoEvent() {
  145. setTimeout(() => {
  146. let video = document.getElementById(`dom-html-video_${this.num}`)
  147. this.video = video
  148. // 监听视频事件
  149. video.addEventListener('play', () => {
  150. this.$ownerInstance.callMethod('videoEvent', 'play')
  151. })
  152. video.addEventListener('pause', () => {
  153. this.$ownerInstance.callMethod('videoEvent', 'pause')
  154. })
  155. video.addEventListener('ended', () => {
  156. this.$ownerInstance.callMethod('videoEvent', 'ended')
  157. this.$ownerInstance.callMethod('resetEventDrive')
  158. })
  159. }, 100)
  160. },
  161. eventHandle(eventType) {
  162. if (eventType) {
  163. this.video = document.getElementById(`dom-html-video_${this.num}`)
  164. if (eventType === 'play') {
  165. this.video.play()
  166. } else if (eventType === 'pause') {
  167. this.video.pause()
  168. } else if (eventType === 'stop') {
  169. this.video.stop()
  170. }
  171. }
  172. },
  173. srcChange(val) {
  174. // 实现视频的第一帧作为封面,避免视频展示黑屏
  175. this.initVideoEvent()
  176. setTimeout(() => {
  177. let video = document.getElementById(`dom-html-video_${this.num}`)
  178. video.addEventListener('loadedmetadata', () => {
  179. let { autoplay } = this.options
  180. video.play()
  181. if (!autoplay) {
  182. video.pause()
  183. }
  184. })
  185. }, 0)
  186. },
  187. propsChange(obj) {
  188. this.options = obj
  189. },
  190. randomNumChange(val) {
  191. this.num = val
  192. },
  193. }
  194. }
  195. </script>
  196. <style lang="scss" scoped>
  197. .dom-video {
  198. overflow: hidden;
  199. height: 100%;
  200. padding: 0;
  201. &-height {
  202. height: 100%;
  203. }
  204. }
  205. </style>