eplayer 1.6.7 → 1.6.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/eplayer.js CHANGED
@@ -4,10 +4,18 @@ class Eplayer extends HTMLElement {
4
4
  this.doms = {}
5
5
  this.src = this.getAttribute('src')
6
6
  this.type = this.getAttribute('type')
7
+ this.cover = this.getAttribute('cover')
7
8
  this.live = JSON.parse(this.getAttribute('live'))
8
9
  this.danmaku = null
9
10
  this.subs = []
10
11
 
12
+ // 添加长按右箭头3倍速相关状态
13
+ this.rightKeyHoldTimer = null
14
+ this.rightKeyPressTime = null
15
+ this.originalPlaybackRate = 1
16
+ this.isRightKeyPressed = false
17
+ this.isSpeedModeActive = false
18
+
11
19
  this.init()
12
20
  this.stream()
13
21
  }
@@ -37,11 +45,14 @@ class Eplayer extends HTMLElement {
37
45
  this.$('.time').style.display = 'inline-block'
38
46
  }
39
47
  }
40
- if (name === 'danma') {
48
+ if (name === 'danmu') {
41
49
  this.danmaku.add({
42
50
  msg: newVal
43
51
  })
44
52
  }
53
+ if (name === 'cover') {
54
+ this.cover = newVal
55
+ }
45
56
  }
46
57
 
47
58
  $(key) {
@@ -79,14 +90,17 @@ class Eplayer extends HTMLElement {
79
90
  }
80
91
 
81
92
  play() {
93
+ console.log(this.$('.img-container'))
82
94
  if (this.video.paused) {
83
95
  this.video.play()
84
96
  this.danmaku.resume()
97
+ this.$('.img-container').classList.add('is-playing');
85
98
  this.$('.is-play').setAttribute('icon-id', 'pause')
86
99
  this.emit('play')
87
100
  } else {
88
101
  this.video.pause()
89
102
  this.danmaku.pause()
103
+ this.$('.img-container').classList.remove('is-playing');
90
104
  this.$('.is-play').setAttribute('icon-id', 'play')
91
105
  this.emit('pause')
92
106
  }
@@ -115,64 +129,143 @@ class Eplayer extends HTMLElement {
115
129
  }
116
130
 
117
131
  progress(e) {
118
- let offset = e.offsetX / this.$('.progress').offsetWidth
119
- this.video.currentTime = this.video.duration * offset
132
+ const progressBarRect = this.$('.progress').getBoundingClientRect()
133
+ let clickX = e.clientX - progressBarRect.left
134
+ clickX = Math.max(0, Math.min(clickX, progressBarRect.width))
135
+ const offsetRatio = clickX / progressBarRect.width
136
+
137
+ this.video.currentTime = this.video.duration * offsetRatio
138
+ this.$('.now').innerHTML = getTimeStr(this.video.currentTime)
139
+ this.$('.current').style.width = offsetRatio * 100 + '%'
120
140
  }
121
141
 
122
142
  down(e) {
123
- this.disX = e.clientX - this.$('.dot').offsetLeft
143
+ e.preventDefault()
124
144
  this.moving = true
125
- document.onpointermove = (e) => this.move(e)
126
- document.onpointerup = () => {
127
- this.moveing = false
128
- document.onpointermove = null
129
- document.onpointerup = null
130
- }
145
+ const progressBarRect = this.$('.progress').getBoundingClientRect()
146
+ this.progressBarWidth = progressBarRect.width
147
+ this.initialClientX = e.clientX
148
+ const initialOffsetRatio = this.video.currentTime / this.video.duration
149
+ this.initialOffsetPixels = initialOffsetRatio * this.progressBarWidth
150
+
151
+ document.addEventListener('pointermove', this.handleMove)
152
+ document.addEventListener('pointerup', this.handleUp, { once: true })
153
+ }
154
+
155
+ handleMove = (e) => {
156
+ if (!this.moving) return
157
+
158
+ const deltaX = e.clientX - this.initialClientX
159
+ let newOffsetPixels = this.initialOffsetPixels + deltaX
160
+ newOffsetPixels = Math.max(0, Math.min(newOffsetPixels, this.progressBarWidth))
161
+
162
+ this.$('.current').style.width = (newOffsetPixels / this.progressBarWidth) * 100 + '%'
163
+
164
+ const newTime = (newOffsetPixels / this.progressBarWidth) * this.video.duration
165
+ this.video.currentTime = Math.max(0, Math.min(newTime, this.video.duration))
166
+
167
+ this.$('.now').innerHTML = getTimeStr(this.video.currentTime)
168
+ }
169
+
170
+ handleUp = (e) => {
171
+ this.moving = false
172
+ document.removeEventListener('pointermove', this.handleMove)
173
+ delete this.progressBarWidth
174
+ delete this.initialClientX
175
+ delete this.initialOffsetPixels
131
176
  }
132
177
 
133
178
  move(e) {
134
179
  let offset = e.clientX - this.disX + 12
135
180
  if (offset < 0) offset = 0
136
181
  if (offset > this.$('.progress').clientWidth) {
137
- this.offset = this.$('.progress').clientWidth
182
+ offset = this.$('.progress').clientWidth
138
183
  }
139
184
  this.$('.current').style.width = offset + 'px'
140
185
  this.video.currentTime = (offset / this.$('.progress').clientWidth) * this.video.duration
141
- document.onpointermove = null
142
- setTimeout((document.onpointermove = (e) => e && this.move(e)), 30)
186
+ this.$('.now').innerHTML = getTimeStr(this.video.currentTime)
143
187
  }
144
188
 
145
189
  alow() {
146
190
  clearTimeout(this.timer)
147
191
  this.$('.mark').style.cursor = 'default'
148
192
  this.$('.eplayer').classList.add('hover')
149
- this.timer = setTimeout(() => {
150
- this.$('.eplayer').classList.remove('hover')
151
- this.$('.mark').style.cursor = 'none'
152
- }, 5000)
193
+ if (!this.cover) {
194
+ this.timer = setTimeout(() => {
195
+ this.$('.eplayer').classList.remove('hover')
196
+ }, 5000)
197
+ }
153
198
  }
154
199
 
155
200
  keydown(e) {
201
+ e.preventDefault()
156
202
  switch (e.keyCode) {
157
- case 37:
158
- this.video.currentTime -= 10
203
+ case 37: // 左箭头 - 后退10秒
204
+ this.video.currentTime = Math.max(0, this.video.currentTime - 10)
159
205
  break
160
- case 39:
161
- this.video.currentTime += 10
206
+ case 39: // 右箭头 - 前进10秒 (支持长按3倍速)
207
+ if (!this.isRightKeyPressed) {
208
+ this.isRightKeyPressed = true
209
+ this.rightKeyPressTime = Date.now()
210
+ this.originalPlaybackRate = this.video.playbackRate
211
+
212
+ // 设置定时器,如果持续按住超过500ms则开启3倍速
213
+ this.rightKeyHoldTimer = setTimeout(() => {
214
+ this.isSpeedModeActive = true
215
+ this.video.playbackRate = 3
216
+ this.$('.speed').innerText = '3x'
217
+ this.$('.speed-indicator').style.display = 'block'
218
+ }, 500)
219
+ }
162
220
  break
163
- case 38:
164
- try {
165
- this.video.volume = parseInt(this.video.volume * 100) / 100 + 0.05
166
- } catch (e) { }
221
+ case 38: // 上箭头 - 增加音量5%
222
+ e.preventDefault()
223
+ this.video.volume = Math.min(1, this.video.volume + 0.05)
167
224
  break
168
- case 40:
169
- try {
170
- this.video.volume = parseInt(this.video.volume * 100) / 100 - 0.05
171
- } catch (e) { }
225
+ case 40: // 下箭头 - 减少音量5%
226
+ e.preventDefault()
227
+ this.video.volume = Math.max(0, this.video.volume - 0.05)
172
228
  break
173
- case 32:
229
+ case 32: // 空格键 - 播放/暂停
230
+ e.preventDefault()
174
231
  this.play()
175
232
  break
233
+ case 77: // M键 - 静音/取消静音
234
+ this.volume()
235
+ break
236
+ default:
237
+ }
238
+ }
239
+
240
+ keyup(e) {
241
+ switch (e.keyCode) {
242
+ case 39: // 右箭头松开
243
+ if (this.isRightKeyPressed) {
244
+ const pressDuration = Date.now() - this.rightKeyPressTime
245
+ this.isRightKeyPressed = false
246
+
247
+ // 清除定时器
248
+ if (this.rightKeyHoldTimer) {
249
+ clearTimeout(this.rightKeyHoldTimer)
250
+ this.rightKeyHoldTimer = null
251
+ }
252
+
253
+ // 判断是短按还是长按
254
+ if (pressDuration < 500 && !this.isSpeedModeActive) {
255
+ // 短按:只快进10秒
256
+ this.video.currentTime = Math.min(this.video.duration, this.video.currentTime + 10)
257
+ } else if (this.isSpeedModeActive) {
258
+ // 长按结束:恢复原播放速度并隐藏提示
259
+ this.video.playbackRate = this.originalPlaybackRate
260
+ this.$('.speed').innerText = this.originalPlaybackRate + 'x'
261
+ this.$('.speed-indicator').style.display = 'none'
262
+ this.isSpeedModeActive = false
263
+ }
264
+
265
+ // 重置状态
266
+ this.rightKeyPressTime = null
267
+ }
268
+ break
176
269
  default:
177
270
  }
178
271
  }
@@ -245,13 +338,24 @@ class Eplayer extends HTMLElement {
245
338
  list-style:none;
246
339
  }
247
340
  .eplayer,video{
248
- font-family:'黑体';
341
+ font-family:'黑体';
249
342
  height:100%;
250
343
  width:100%;
251
344
  color:var(--icons,rgba(255,255,255,0.6));
252
345
  font-size:12px;
253
- background:#000
346
+ background:var(--bg,#000);
254
347
  }
348
+ .eplayer .cover{
349
+ position: absolute;
350
+ z-index: 1;
351
+ top: 0;
352
+ left: 0;
353
+ right: 0;
354
+ bottom: 0;
355
+ background:url(${this.cover || ''}) center/cover no-repeat #fff;
356
+ filter:blur(8px);
357
+ }
358
+
255
359
  .eplayer{
256
360
  user-select:none;
257
361
  position: relative;
@@ -261,14 +365,17 @@ class Eplayer extends HTMLElement {
261
365
  position:absolute;
262
366
  left:0;
263
367
  right:0;
264
- bottom:0;
265
- background:linear-gradient(transparent,rgba(0,0,0,.5));
266
- transition: .3s ease-out;
267
368
  bottom:-34px;
268
- z-index:1;
369
+ background:linear-gradient(transparent,rgba(0,0,0,.5));
370
+ transition: bottom .3s ease-out, opacity .3s ease-out;
371
+ opacity: 0;
372
+ pointer-events: none;
373
+ z-index:1000;
269
374
  }
270
- .hover:hover .controls{
375
+ .hover .controls{
271
376
  bottom:0;
377
+ opacity: 1;
378
+ pointer-events: auto;
272
379
  }
273
380
  .progress{
274
381
  display:${this.live ? 'none' : 'block'};
@@ -395,11 +502,28 @@ class Eplayer extends HTMLElement {
395
502
  bottom: 0;
396
503
  pointer-events: none;
397
504
  overflow: hidden;
398
- z-index: 999;
505
+ z-index: 0;
399
506
  height:100%;
400
507
  width:100%;
401
508
  }
402
509
 
510
+ .speed-indicator {
511
+ position: absolute;
512
+ top: 20px;
513
+ left: 50%;
514
+ transform: translateX(-50%);
515
+ background: rgba(148, 108, 230, 0.9);
516
+ color: white;
517
+ padding: 8px 16px;
518
+ border-radius: 20px;
519
+ font-size: 14px;
520
+ font-weight: bold;
521
+ z-index: 999;
522
+ display: none;
523
+ pointer-events: none;
524
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
525
+ }
526
+
403
527
  iconpark-icon{
404
528
  margin-right:5px;
405
529
  cursor:pointer;
@@ -413,13 +537,81 @@ class Eplayer extends HTMLElement {
413
537
  margin:0 10px 0 8px;
414
538
  cursor: pointer;
415
539
  }
416
-
540
+
541
+ .eplayer .img-container{
542
+ position: absolute;
543
+ z-index: 1;
544
+ width:250px;
545
+ height:250px;
546
+ border-radius:125px;
547
+ left:50%;
548
+ top:50%;
549
+ transform:translate(-50%,-50%);
550
+ }
551
+
552
+ .rotate-img {
553
+ width: 250px;
554
+ height: 250px;
555
+ border-radius: 50%;
556
+ animation: rotate 10s linear infinite;
557
+ object-fit: cover;
558
+ animation-play-state: paused; /* 默认暂停 */
559
+ }
560
+
561
+ .img-container::after,
562
+ .img-container::before {
563
+ content: '';
564
+ position: absolute;
565
+ top: 50%;
566
+ left: 50%;
567
+ width: 100%;
568
+ height: 100%;
569
+ border-radius: 50%;
570
+ transform: translate(-50%, -50%) scale(0);
571
+ animation: wave 2s infinite;
572
+ animation-play-state: paused; /* 默认暂停 */
573
+ }
574
+
575
+ .img-container::after {
576
+ background: rgba(255, 255, 255, 0.3);
577
+ }
578
+
579
+ .img-container::before {
580
+ background: rgba(255, 255, 255, 0.2);
581
+ animation-delay: 1s;
582
+ }
583
+
584
+ /* 播放状态 */
585
+ .is-playing .rotate-img,
586
+ .is-playing::after,
587
+ .is-playing::before {
588
+ animation-play-state: running;
589
+ }
590
+
591
+ @keyframes rotate {
592
+ from { transform: rotate(0deg); }
593
+ to { transform: rotate(360deg); }
594
+ }
595
+
596
+ @keyframes wave {
597
+ 0% {
598
+ transform: translate(-50%, -50%) scale(0);
599
+ opacity: 1;
600
+ }
601
+ 100% {
602
+ transform: translate(-50%, -50%) scale(2);
603
+ opacity: 0;
604
+ }
605
+ }
417
606
  </style>
418
607
 
419
608
  <div class="eplayer hover">
420
609
  <div class="danmaku"></div>
421
610
  <video id="video" class="video" src="${this.src || ''}"></video>
611
+ <div class="cover"></div>
612
+ <div class="img-container"><img src="${this.cover || ''}" class="rotate-img"/></div>
422
613
  <div class="mark loading"></div>
614
+ <div class="speed-indicator">倍速中</div>
423
615
  <div class="controls">
424
616
  <div class="progress">
425
617
  <b class="bg"></b>
@@ -436,7 +628,7 @@ class Eplayer extends HTMLElement {
436
628
  </span>
437
629
  </div>
438
630
  <div class="right">
439
- <em class="speed">倍速</em>
631
+ <em class="speed">1x</em>
440
632
  <em class="pip">画中画</em>
441
633
  <iconpark-icon icon-id="volume-ok" size="2rem" class="is-volume"></iconpark-icon>
442
634
  <iconpark-icon icon-id="web-fullscreen" size="2rem"></iconpark-icon>
@@ -454,6 +646,7 @@ class Eplayer extends HTMLElement {
454
646
  }).appendChild(template.content.cloneNode(true))
455
647
 
456
648
  const doms = [
649
+ '.img-container',
457
650
  '.video',
458
651
  '.mark',
459
652
  '.playing',
@@ -475,7 +668,8 @@ class Eplayer extends HTMLElement {
475
668
  '.panel',
476
669
  '.speed',
477
670
  '.pip',
478
- '.danmaku'
671
+ '.danmaku',
672
+ '.speed-indicator'
479
673
  ]
480
674
 
481
675
  for (const key of doms) {
@@ -503,6 +697,7 @@ class Eplayer extends HTMLElement {
503
697
  '.speed': this.speed,
504
698
  '.bg': this.progress,
505
699
  '.buffer': this.progress,
700
+ '.current': this.progress,
506
701
  '.pip': this.pip,
507
702
  })
508
703
  this.delegate('pointerdown', {
@@ -515,10 +710,35 @@ class Eplayer extends HTMLElement {
515
710
  this.full()
516
711
  },
517
712
  })
518
- this.delegate('keydown', this.keydown)
713
+
714
+ // 使用全局键盘监听以确保按键事件能在任何地方被捕获
715
+ this.keydownHandler = this.keydown.bind(this)
716
+ this.keyupHandler = this.keyup.bind(this)
717
+ document.addEventListener('keydown', this.keydownHandler)
718
+ document.addEventListener('keyup', this.keyupHandler)
719
+
519
720
  this.delegate('mousemove', this.alow)
520
721
  }
521
722
 
723
+ disconnectedCallback() {
724
+ // 清理全局键盘事件监听器
725
+ if (this.keydownHandler) {
726
+ document.removeEventListener('keydown', this.keydownHandler)
727
+ }
728
+ if (this.keyupHandler) {
729
+ document.removeEventListener('keyup', this.keyupHandler)
730
+ }
731
+
732
+ // 清理长按定时器和状态
733
+ if (this.rightKeyHoldTimer) {
734
+ clearTimeout(this.rightKeyHoldTimer)
735
+ this.rightKeyHoldTimer = null
736
+ }
737
+ this.isRightKeyPressed = false
738
+ this.isSpeedModeActive = false
739
+ this.rightKeyPressTime = null
740
+ }
741
+
522
742
  delegate(type, map) {
523
743
  const that = this
524
744
  if (typeof map === 'function') {
@@ -538,7 +758,7 @@ class Eplayer extends HTMLElement {
538
758
  }
539
759
  }
540
760
 
541
- emit (name) {
761
+ emit(name) {
542
762
  const fn = Eplayer.subs[name]
543
763
  fn && fn.call(this, this.shadowRoot)
544
764
  }
package/docs/index.html CHANGED
@@ -57,11 +57,16 @@
57
57
  .wrapper a:nth-child(3) {
58
58
  background: #1ea6e9;
59
59
  }
60
+
61
+ e-player {
62
+ --bg:#fff
63
+ }
60
64
  </style>
61
65
 
62
66
  <div class="wrapper">
63
67
  <e-player id="ep"
64
- src="https://a1.alibabausercontent.com/prod/feupload/user/edos8/2f0e7af0-29f1-11ef-b3cd-818e64ac843d.mp4">
68
+ src="https://aod.cos.tx.xmcdn.com/storages/9976-audiofreehighqps/C9/1C/GAqhF9kMsE7kACAAAAQZZzo_.wav"
69
+ cover="https://dd-static.jd.com/ddimgp/jfs/t20261103/331442/3/18576/2295463/68da8cdcF3f9f05fd/790caba1cab6753d.png">
65
70
  </e-player>
66
71
  </div>
67
72
  <script src="https://unpkg.com/@webcomponents/webcomponentsjs"></script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eplayer",
3
- "version": "1.6.7",
3
+ "version": "1.6.9",
4
4
  "description": "A web-components html5 video player facing future",
5
5
  "main": "./docs/eplayer.js",
6
6
  "module": "./docs/eplayer.js",