video.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. /*
  2. * Tencent is pleased to support the open source community by making vap available.
  3. *
  4. * Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
  5. *
  6. * Licensed under the MIT License (the "License"); you may not use this file except in
  7. * compliance with the License. You may obtain a copy of the License at
  8. *
  9. * http://opensource.org/licenses/MIT
  10. *
  11. * Unless required by applicable law or agreed to in writing, software distributed under the License is
  12. * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  13. * either express or implied. See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. export default class VapVideo {
  17. constructor(options) {
  18. if (!options.container || !options.src) {
  19. return console.warn('[Alpha video]: options container and src cannot be empty!')
  20. }
  21. this.options = Object.assign(
  22. {
  23. // 视频url
  24. src: '',
  25. // 循环播放
  26. loop: false,
  27. fps: 20,
  28. // 视频宽度
  29. width: 375,
  30. // 视频高度
  31. height: 375,
  32. // 容器
  33. container: null,
  34. config: ''
  35. },
  36. options
  37. )
  38. this.fps = 20
  39. this.requestAnim = this.requestAnimFunc(this.fps)
  40. this.container = this.options.container
  41. if (!this.options.src || !this.options.config || !this.options.container) {
  42. console.error('参数出错:src(视频地址)、config(配置文件地址)、container(dom容器)')
  43. } else {
  44. // 创建video
  45. this.initVideo()
  46. }
  47. }
  48. initVideo() {
  49. const options = this.options
  50. // 创建video
  51. const video = (this.video = document.createElement('video'))
  52. video.crossOrigin = 'anonymous'
  53. video.autoplay = false
  54. // video.muted = true
  55. // video.volume = 0
  56. video.style.display = 'none'
  57. video.src = options.src
  58. video.loop = !!options.loop
  59. // 这里要插在body上,避免container移动带来无法播放的问题
  60. document.body.appendChild(this.video)
  61. // 绑定事件
  62. this.events = {}
  63. ;['playing', 'pause', 'ended', 'error'].forEach(item => {
  64. this.on(item, this['on' + item].bind(this))
  65. })
  66. video.load()
  67. }
  68. drawFrame() {
  69. this._drawFrame = this._drawFrame || this.drawFrame.bind(this)
  70. this.animId = this.requestAnim(this._drawFrame)
  71. }
  72. play() {
  73. const prom = this.video && this.video.play()
  74. if (prom && prom.then) {
  75. prom.catch(e => {
  76. if (!this.video) {
  77. return
  78. }
  79. this.video.muted = true
  80. this.video.volume = 0
  81. this.video.play().catch(e => {
  82. ;(this.events.error || []).forEach(item => {
  83. item(e)
  84. })
  85. })
  86. })
  87. }
  88. }
  89. requestAnimFunc() {
  90. const me = this
  91. if (window.requestAnimationFrame) {
  92. let index = -1
  93. return function(cb) {
  94. index++
  95. return requestAnimationFrame(() => {
  96. if (!(index % (60 / me.fps))) {
  97. return cb()
  98. }
  99. me.animId = me.requestAnim(cb)
  100. })
  101. }
  102. }
  103. return function(cb) {
  104. return setTimeout(cb, 1000 / me.fps)
  105. }
  106. }
  107. cancelRequestAnimation() {
  108. if (window.cancelAnimationFrame) {
  109. cancelAnimationFrame(this.animId)
  110. }
  111. clearTimeout(this.animId)
  112. }
  113. destroy() {
  114. if (this.video) {
  115. this.video.parentNode && this.video.parentNode.removeChild(this.video)
  116. this.video = null
  117. }
  118. this.cancelRequestAnimation(this.animId)
  119. }
  120. clear() {
  121. this.destroy()
  122. }
  123. on(event, callback) {
  124. const cbs = this.events[event] || []
  125. cbs.push(callback)
  126. this.events[event] = cbs
  127. this.video.addEventListener(event, callback)
  128. return this
  129. }
  130. onplaying() {
  131. if (!this.firstPlaying) {
  132. this.firstPlaying = true
  133. this.drawFrame()
  134. }
  135. }
  136. onpause() {}
  137. onended() {
  138. this.destroy()
  139. }
  140. onerror(err) {
  141. console.error('[Alpha video]: play error: ', err)
  142. this.destroy()
  143. }
  144. }