video3.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. <style>
  2. .__cov-video-container {
  3. position: relative;
  4. width: 100%;
  5. background-color: #000;
  6. }
  7. .__cov-video {
  8. width: 100%;
  9. height: 100%;
  10. vertical-align: bottom;
  11. }
  12. .__cov-contrl-content {
  13. position: absolute;
  14. display: flex;
  15. left: 0;
  16. bottom: 0;
  17. background-color: rgba(0, 0, 0, 0.41);
  18. height: 2rem;
  19. width: 100%;
  20. z-index: 2147483647;
  21. }
  22. .__cov-contrl-play-btn {
  23. position: relative;
  24. height: 100%;
  25. background: none;
  26. border: none;
  27. height: 2rem;
  28. width: 4rem;
  29. outline: none;
  30. vertical-align: top;
  31. }
  32. .__cov-contrl-play-btn:hover {
  33. background-color: rgba(255, 255, 255, 0.27);
  34. }
  35. .__cov-contrl-play-btn-icon {
  36. position: absolute;
  37. height: 1rem;
  38. width: 1rem;
  39. top: 50%;
  40. left: 50%;
  41. margin-top: -0.5rem;
  42. margin-left: -0.5rem;
  43. }
  44. .__cov-contrl-vol-btn-icon {
  45. position: absolute;
  46. height: 1.1rem;
  47. width: 1.1rem;
  48. top: 50%;
  49. left: 50%;
  50. margin-top: -0.55rem;
  51. margin-left: -0.55rem;
  52. }
  53. .__cov-contrl-vol-slider {
  54. position: relative;
  55. display: inline-block;
  56. height: 100%;
  57. width: 6rem;
  58. height: 2rem;
  59. overflow: hidden;
  60. transition: all 0.2s ease-in;
  61. }
  62. .__cov-contrl-vol-rail {
  63. position: absolute;
  64. top: 50%;
  65. width: 6rem;
  66. height: 0.1rem;
  67. margin-top: -0.05rem;
  68. background: #fff;
  69. }
  70. .__cov-contrl-vol-inner {
  71. position: absolute;
  72. display: inline-block;
  73. left: 0;
  74. top: 50%;
  75. background: #fff;
  76. width: 0.5rem;
  77. height: 0.5rem;
  78. border-radius: 50%;
  79. margin-top: -0.25rem;
  80. z-index: 2;
  81. cursor: pointer;
  82. }
  83. .__cov-contrl-vol-box {
  84. display: flex;
  85. }
  86. .__cov-contrl-video-slider {
  87. position: relative;
  88. display: inline-block;
  89. height: 100%;
  90. width: 100%;
  91. overflow: hidden;
  92. margin: 0 0.5rem;
  93. transition: all 0.2s ease-in;
  94. }
  95. .__cov-contrl-video-slider.no-scrub {
  96. pointer-events: none;
  97. }
  98. .__cov-contrl-video-rail {
  99. position: absolute;
  100. top: 50%;
  101. width: 100%;
  102. height: 0.1rem;
  103. margin-top: -0.05rem;
  104. background: rgba(255, 255, 255, 0.5);
  105. overflow: hidden;
  106. }
  107. .__cov-contrl-video-rail-inner {
  108. position: absolute;
  109. top: 0;
  110. left: 0;
  111. width: 100%;
  112. height: 0.1rem;
  113. background: rgb(255, 255, 255);
  114. transition: transform 0.2s;
  115. }
  116. .__cov-contrl-video-inner {
  117. position: absolute;
  118. display: inline-block;
  119. left: 0;
  120. top: 50%;
  121. background: #fff;
  122. width: 0.5rem;
  123. height: 0.5rem;
  124. border-radius: 50%;
  125. margin-top: -0.25rem;
  126. z-index: 2;
  127. cursor: pointer;
  128. transition: all 16ms;
  129. }
  130. .__cov-contrl-video-time {
  131. padding: 0 1rem;
  132. }
  133. .__cov-contrl-video-time-text {
  134. color: #fff;
  135. line-height: 2rem;
  136. font-size: 0.8rem;
  137. }
  138. ::-webkit-media-controls {
  139. display: none !important;
  140. }
  141. video::-webkit-media-controls {
  142. display: none !important;
  143. }
  144. video::-webkit-media-controls-enclosure {
  145. display: none !important;
  146. }
  147. .fade-transition {
  148. transition: opacity 0.3s ease;
  149. }
  150. .fade-enter {
  151. opacity: 1;
  152. }
  153. .fade-leave {
  154. opacity: 0;
  155. }
  156. .hide-cursor {
  157. cursor: none;
  158. }
  159. @media all and (max-width: 768px) {
  160. .__cov-contrl-vol-slider {
  161. width: 3rem;
  162. }
  163. .__cov-contrl-video-time {
  164. padding: 0 0.2rem;
  165. }
  166. .__cov-contrl-vol-box .__cov-contrl-play-btn {
  167. width: 2rem;
  168. }
  169. }
  170. </style>
  171. <template>
  172. <div id="app">
  173. <div class="container">
  174. <div class="__cov-video-container" @mouseenter="mouseEnterVideo" @mouseleave="mouseLeaveVideo">
  175. <video :class="{'hide-cursor': !state.contrlShow}" class="__cov-video" :poster="options.poster">
  176. <source v-for="source in sources" :src="source.src" :type="source.type" :key="source.src" />
  177. </video>
  178. <div class="__cov-contrl-content" transition="fade" v-show="state.contrlShow">
  179. <button class="__cov-contrl-play-btn" @click="play">
  180. <svg
  181. class="__cov-contrl-play-btn-icon"
  182. v-show="!state.playing"
  183. viewBox="0 0 47 57"
  184. version="1.1"
  185. xmlns="http://www.w3.org/2000/svg"
  186. >
  187. <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
  188. <title>Triangle 1</title>
  189. <desc>Created with Sketch.</desc>
  190. <defs></defs>
  191. <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
  192. <polygon id="Triangle-1" stroke="#FFFFFF" fill="#FFFFFF" points="1 56 1 1 47 28.5"></polygon>
  193. </g>
  194. </svg>
  195. <svg
  196. class="__cov-contrl-play-btn-icon"
  197. v-show="state.playing"
  198. viewBox="0 0 15 22"
  199. version="1.1"
  200. xmlns="http://www.w3.org/2000/svg"
  201. >
  202. <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
  203. <title>Combined Shape</title>
  204. <desc>Created with Sketch.</desc>
  205. <defs>
  206. <path
  207. d="M0,0.979149244 L5,0.979149244 L5,22 L0,22 L0,0.979149244 Z M10,0.979149244 L15,0.979149244 L15,22 L10,22 L10,0.979149244 Z"
  208. id="path-1"
  209. ></path>
  210. <mask
  211. id="mask-2"
  212. maskContentUnits="userSpaceOnUse"
  213. maskUnits="objectBoundingBox"
  214. x="0"
  215. y="0"
  216. width="15"
  217. height="21.0208508"
  218. fill="white"
  219. >
  220. <use xlink:href="#path-1"></use>
  221. </mask>
  222. </defs>
  223. <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
  224. <use
  225. id="Combined-Shape"
  226. stroke="#FFFFFF"
  227. mask="url(#mask-2)"
  228. stroke-width="2"
  229. fill="#FFFFFF"
  230. xlink:href="#path-1"
  231. ></use>
  232. </g>
  233. </svg>
  234. </button>
  235. <div
  236. class="__cov-contrl-video-slider"
  237. @click="slideClick"
  238. @mousedown="videoMove"
  239. :class="{'no-scrub': options.noScrub}"
  240. >
  241. <div class="__cov-contrl-video-inner" :style="{transform: `translate3d(${video.pos.current}px, 0, 0)`}"></div>
  242. <div class="__cov-contrl-video-rail">
  243. <div
  244. class="__cov-contrl-video-rail-inner"
  245. :style="{transform: 'translate3d(' + video.loaded + '%, 0, 0)'}"
  246. ></div>
  247. </div>
  248. </div>
  249. <div class="__cov-contrl-video-time">
  250. <span class="__cov-contrl-video-time-text">{{ video.displayTime }}</span>
  251. </div>
  252. <div class="__cov-contrl-vol-box">
  253. <button class="__cov-contrl-play-btn" @click="volMuted">
  254. <svg
  255. class="__cov-contrl-vol-btn-icon"
  256. viewBox="0 0 41 44"
  257. version="1.1"
  258. xmlns="http://www.w3.org/2000/svg"
  259. xmlns:xlink="http://www.w3.org/1999/xlink"
  260. >
  261. <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
  262. <title>vol</title>
  263. <desc>Created with Sketch.</desc>
  264. <defs>
  265. <path
  266. d="M8.61522369,12 L20,0.615223689 L20,37.3847763 L8.61522369,26 L1.99201702,26 C0.891856397,26 0,25.1029399 0,23.9941413 L0,14.0058587 C0,12.8980535 0.900176167,12 1.99201702,12 L8.61522369,12 L8.61522369,12 Z"
  267. id="cov-vol"
  268. ></path>
  269. </defs>
  270. <g id="Page-1" stroke="none" stroke-width="2" fill="none" fill-rule="evenodd">
  271. <g id="vol" transform="translate(2.000000, 3.000000)">
  272. <g id="cov-vol-icon">
  273. <g id="Combined-Shape-Clipped">
  274. <path
  275. v-show="volume.percent > 1 && !volume.muted"
  276. d="M25,29.5538997 C28.4589093,27.6757536 31.2629093,23.2984641 31.2629093,19.7769499 C31.2629093,16.2554357 28.4589093,11.8781461 25,10"
  277. id="vol-range-2"
  278. stroke="#FFFFFF"
  279. ></path>
  280. <path
  281. v-show="volume.percent > 70 && !volume.muted"
  282. d="M28,35.5538997 C33.5816016,32.5231573 38.1063837,25.4595762 38.1063837,19.7769499 C38.1063837,14.0943235 33.5816016,7.03074247 28,4"
  283. id="vol-range-2"
  284. stroke="#FFFFFF"
  285. ></path>
  286. <mask id="mask-2" fill="white">
  287. <use xlink:href="#cov-vol"></use>
  288. </mask>
  289. <use id="vol-path" stroke="#FFFFFF" stroke-width="3" xlink:href="#cov-vol"></use>
  290. <g id="Combined-Shape" mask="url(#mask-2)" stroke="#FFFFFF" stroke-width="2" fill="#FFFFFF">
  291. <path
  292. d="M8.61522369,12 L20,0.615223689 L20,37.3847763 L8.61522369,26 L1.99201702,26 C0.891856397,26 0,25.1029399 0,23.9941413 L0,14.0058587 C0,12.8980535 0.900176167,12 1.99201702,12 L8.61522369,12 L8.61522369,12 Z"
  293. id="cov-vol"
  294. ></path>
  295. </g>
  296. </g>
  297. </g>
  298. </g>
  299. </g>
  300. </svg>
  301. </button>
  302. <div class="__cov-contrl-vol-slider" @click="volSlideClick" @mousedown="volMove">
  303. <div class="__cov-contrl-vol-inner" :style="{transform: `translate3d(${volume.pos.current}px, 0, 0)`}"></div>
  304. <div class="__cov-contrl-vol-rail"></div>
  305. </div>
  306. </div>
  307. <button class="__cov-contrl-play-btn" @click="fullScreen">
  308. <svg
  309. class="__cov-contrl-vol-btn-icon"
  310. viewBox="0 0 33 33"
  311. version="1.1"
  312. xmlns="http://www.w3.org/2000/svg"
  313. xmlns:xlink="http://www.w3.org/1999/xlink"
  314. >
  315. <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
  316. <defs></defs>
  317. <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
  318. <path
  319. d="M31.1682064,22 L31.1682064,31.0073537 L22,31.0073537 M22,1 L31.0073537,1 L31.0073537,10.1682064 M1,10.0073537 L1,1 L10.1682064,1 M10.0073537,31.1682064 L1,31.1682064 L1,22"
  320. id="Combined-Shape"
  321. stroke="#FFFFFF"
  322. stroke-width="2"
  323. ></path>
  324. </g>
  325. </svg>
  326. </button>
  327. </div>
  328. </div>
  329. </div>
  330. </div>
  331. </template>
  332. <script>
  333. const getMousePosition = function(e, type = 'x') {
  334. if (type === 'x') {
  335. return e.pageX;
  336. }
  337. return e.pageY;
  338. };
  339. const pad = val => {
  340. val = Math.floor(val);
  341. if (val < 10) {
  342. return '0' + val;
  343. }
  344. return val + '';
  345. };
  346. const timeParse = sec => {
  347. let min = 0;
  348. min = Math.floor(sec / 60);
  349. sec = sec - min * 60;
  350. return pad(min) + ':' + pad(sec);
  351. };
  352. export default {
  353. props: {
  354. sources: Array,
  355. options: {
  356. type: Object,
  357. default() {
  358. return {
  359. autoplay: false,
  360. volume: 0.9,
  361. poster: '',
  362. noScrub: false
  363. };
  364. }
  365. }
  366. },
  367. data() {
  368. return {
  369. $video: null,
  370. video: {
  371. $videoSlider: null,
  372. len: 0,
  373. current: 0,
  374. loaded: 0,
  375. moving: false,
  376. displayTime: '00:00',
  377. pos: {
  378. start: 0,
  379. width: 0,
  380. innerWidth: 0,
  381. current: 0
  382. }
  383. },
  384. volume: {
  385. $volBox: null,
  386. muted: false,
  387. percent: 60,
  388. moving: false,
  389. pos: {
  390. start: 0,
  391. width: 0,
  392. innerWidth: 0,
  393. current: 0
  394. }
  395. },
  396. player: {
  397. $player: null,
  398. pos: null
  399. },
  400. tmp: {
  401. contrlHideTimer: null
  402. },
  403. state: {
  404. contrlShow: true,
  405. vol: 0.5,
  406. currentTime: 0,
  407. fullScreen: false,
  408. playing: false
  409. }
  410. };
  411. },
  412. ready() {
  413. this.init();
  414. },
  415. mounted() {
  416. this.init();
  417. },
  418. beforeDestroy() {
  419. document.body.removeEventListener('mousemove', this.mouseMoveAction);
  420. document.body.removeEventListener('mouseup', this.mouseUpAction);
  421. },
  422. methods: {
  423. init() {
  424. this.$video = this.$el.getElementsByTagName('video')[0];
  425. this.initCore();
  426. if (this.options.autoplay) {
  427. this.play();
  428. }
  429. document.body.addEventListener('mousemove', this.mouseMoveAction, false);
  430. document.body.addEventListener('mouseup', this.mouseUpAction, false);
  431. },
  432. initCore() {
  433. this.initVol();
  434. this.initVideo();
  435. this.initPlayer();
  436. const vol = this.options.volume || 0.9;
  437. this.setVol(vol);
  438. },
  439. initPlayer() {
  440. const $player = this.$el.getElementsByClassName('__cov-video-container')[0];
  441. this.player.pos = $player.getBoundingClientRect();
  442. this.player.$player = $player;
  443. },
  444. initVol() {
  445. const $volBox = this.$el.getElementsByClassName('__cov-contrl-vol-slider')[0];
  446. const $volInner = $volBox.getElementsByClassName('__cov-contrl-vol-inner')[0];
  447. this.volume.$volBox = $volBox;
  448. this.volume.pos.innerWidth = $volInner.getBoundingClientRect().width;
  449. this.volume.pos.start = $volBox.getBoundingClientRect().left;
  450. this.volume.pos.width = $volBox.getBoundingClientRect().width - this.volume.pos.innerWidth;
  451. },
  452. initVideo() {
  453. const $videoSlider = this.$el.getElementsByClassName('__cov-contrl-video-slider')[0];
  454. const $videoInner = $videoSlider.getElementsByClassName('__cov-contrl-video-inner')[0];
  455. this.$videoSlider = $videoSlider;
  456. this.video.pos.start = $videoSlider.getBoundingClientRect().left;
  457. this.video.pos.innerWidth = $videoInner.getBoundingClientRect().width;
  458. this.video.pos.width = $videoSlider.getBoundingClientRect().width - this.video.pos.innerWidth;
  459. this.getTime();
  460. },
  461. mouseEnterVideo() {
  462. if (this.tmp.contrlHideTimer) {
  463. clearTimeout(this.tmp.contrlHideTimer);
  464. this.tmp.contrlHideTimer = null;
  465. }
  466. this.state.contrlShow = true;
  467. },
  468. mouseLeaveVideo(e) {
  469. if (this.tmp.contrlHideTimer) {
  470. clearTimeout(this.tmp.contrlHideTimer);
  471. }
  472. this.tmp.contrlHideTimer = setTimeout(() => {
  473. this.state.contrlShow = false;
  474. this.tmp.contrlHideTimer = null;
  475. }, 2000);
  476. },
  477. toggleContrlShow() {
  478. this.state.contrlShow = !this.state.contrlShow;
  479. },
  480. getTime() {
  481. this.$video.addEventListener('durationchange', e => {
  482. console.log(e);
  483. });
  484. this.$video.addEventListener('progress', e => {
  485. this.video.loaded = (-1 + this.$video.buffered.end(0) / this.$video.duration) * 100;
  486. });
  487. this.video.len = this.$video.duration;
  488. },
  489. setVideoByTime(percent) {
  490. this.$video.currentTime = Math.floor(percent * this.video.len);
  491. },
  492. play() {
  493. this.state.playing = !this.state.playing;
  494. if (this.$video) {
  495. if (this.state.playing) {
  496. this.$video.play();
  497. this.mouseLeaveVideo();
  498. this.$video.addEventListener('timeupdate', this.timeline);
  499. this.$video.addEventListener('ended', e => {
  500. this.state.playing = false;
  501. this.video.pos.current = 0;
  502. this.$video.currentTime = 0;
  503. });
  504. } else {
  505. this.$video.pause();
  506. }
  507. }
  508. },
  509. timeline() {
  510. const percent = this.$video.currentTime / this.$video.duration;
  511. this.video.pos.current = (this.video.pos.width * percent).toFixed(3);
  512. this.video.displayTime = timeParse(this.$video.duration - this.$video.currentTime);
  513. if (percent === 1) {
  514. this.$emit('video-ended', true);
  515. }
  516. },
  517. volMove(e) {
  518. this.initVol();
  519. this.volume.moving = true;
  520. },
  521. videoMove(e) {
  522. this.initVideo();
  523. this.video.moving = true;
  524. },
  525. slideClick(e) {
  526. this.videoSlideMove(e);
  527. },
  528. volSlideClick(e) {
  529. this.volSlideMove(e);
  530. },
  531. volMuted() {
  532. this.$video.muted = !this.$video.muted;
  533. this.volume.muted = this.$video.muted;
  534. },
  535. setVol(val) {
  536. if (this.$video) {
  537. this.volume.pos.current = val * this.volume.pos.width;
  538. this.volume.percent = val * 100;
  539. this.$video.volume = val;
  540. }
  541. },
  542. fullScreen() {
  543. if (!this.state.fullScreen) {
  544. this.state.fullScreen = true;
  545. this.$video.webkitRequestFullScreen();
  546. } else {
  547. this.state.fullScreen = false;
  548. document.webkitCancelFullScreen();
  549. }
  550. setTimeout(this.initVideo, 200);
  551. },
  552. mouseMoveAction(e) {
  553. if (this.volume.moving) {
  554. this.volSlideMove(e);
  555. }
  556. if (this.video.moving) {
  557. this.videoSlideMove(e);
  558. }
  559. this.contrlHider(e);
  560. },
  561. contrlHider(e) {
  562. const x = getMousePosition(e, 'x');
  563. const y = getMousePosition(e, 'y');
  564. if (!this.player.pos) return;
  565. if (x > this.player.pos.left && x < this.player.pos.left + this.player.pos.width) {
  566. if (y > this.player.pos.top + this.player.pos.height * 0.6 && y < this.player.pos.top + this.player.pos.height) {
  567. return this.mouseEnterVideo();
  568. }
  569. }
  570. return this.mouseLeaveVideo();
  571. },
  572. volSlideMove(e) {
  573. const x = getMousePosition(e) - this.volume.pos.start;
  574. if (x > 0 && x < this.volume.pos.width) {
  575. this.setVol(x / this.volume.pos.width);
  576. }
  577. },
  578. videoSlideMove(e) {
  579. const x = getMousePosition(e) - this.video.pos.start;
  580. if (x > 0 && x < this.video.pos.width) {
  581. this.video.pos.current = x;
  582. this.setVideoByTime(x / this.video.pos.width);
  583. }
  584. },
  585. mouseUpAction(e) {
  586. this.volume.moving = false;
  587. this.video.moving = false;
  588. }
  589. }
  590. };
  591. </script>