]>
Commit | Line | Data |
---|---|---|
512decf3 | 1 | import videojs from 'video.js' |
f5fcd9f7 C |
2 | |
3 | function getMainTemplate (options: any) { | |
4 | return ` | |
5 | <div class="vjs-upnext-top"> | |
6 | <span class="vjs-upnext-headtext">${options.headText}</span> | |
7 | <div class="vjs-upnext-title"></div> | |
8 | </div> | |
9 | <div class="vjs-upnext-autoplay-icon"> | |
10 | <svg height="100%" version="1.1" viewbox="0 0 98 98" width="100%"> | |
11 | <circle class="vjs-upnext-svg-autoplay-circle" cx="49" cy="49" fill="#000" fill-opacity="0.8" r="48"></circle> | |
12 | <circle class="vjs-upnext-svg-autoplay-ring" cx="-49" cy="49" fill-opacity="0" r="46.5" stroke="#FFFFFF" stroke-width="4" transform="rotate(-90)"></circle> | |
13 | <polygon class="vjs-upnext-svg-autoplay-triangle" fill="#fff" points="32,27 72,49 32,71"></polygon></svg> | |
14 | </div> | |
15 | <span class="vjs-upnext-bottom"> | |
16 | <span class="vjs-upnext-cancel"> | |
17 | <button class="vjs-upnext-cancel-button" tabindex="0" aria-label="Cancel autoplay">${options.cancelText}</button> | |
18 | </span> | |
19 | <span class="vjs-upnext-suspended">${options.suspendedText}</span> | |
20 | </span> | |
21 | ` | |
22 | } | |
23 | ||
24 | export interface EndCardOptions extends videojs.ComponentOptions { | |
25 | next: Function, | |
26 | getTitle: () => string | |
27 | timeout: number | |
28 | cancelText: string | |
29 | headText: string | |
30 | suspendedText: string | |
31 | condition: () => boolean | |
32 | suspended: () => boolean | |
33 | } | |
34 | ||
35 | const Component = videojs.getComponent('Component') | |
36 | class EndCard extends Component { | |
37 | options_: EndCardOptions | |
38 | ||
39 | dashOffsetTotal = 586 | |
40 | dashOffsetStart = 293 | |
41 | interval = 50 | |
42 | upNextEvents = new videojs.EventTarget() | |
43 | ticks = 0 | |
44 | totalTicks: number | |
45 | ||
46 | container: HTMLDivElement | |
47 | title: HTMLElement | |
48 | autoplayRing: HTMLElement | |
49 | cancelButton: HTMLElement | |
50 | suspendedMessage: HTMLElement | |
51 | nextButton: HTMLElement | |
52 | ||
7e37e111 | 53 | constructor (player: videojs.Player, options: EndCardOptions) { |
f5fcd9f7 C |
54 | super(player, options) |
55 | ||
56 | this.totalTicks = this.options_.timeout / this.interval | |
57 | ||
58 | player.on('ended', (_: any) => { | |
59 | if (!this.options_.condition()) return | |
60 | ||
61 | player.addClass('vjs-upnext--showing') | |
62 | this.showCard((canceled: boolean) => { | |
63 | player.removeClass('vjs-upnext--showing') | |
64 | this.container.style.display = 'none' | |
65 | if (!canceled) { | |
66 | this.options_.next() | |
67 | } | |
68 | }) | |
69 | }) | |
70 | ||
71 | player.on('playing', () => { | |
72 | this.upNextEvents.trigger('playing') | |
73 | }) | |
74 | } | |
75 | ||
76 | createEl () { | |
77 | const container = super.createEl('div', { | |
78 | className: 'vjs-upnext-content', | |
79 | innerHTML: getMainTemplate(this.options_) | |
80 | }) as HTMLDivElement | |
81 | ||
82 | this.container = container | |
83 | container.style.display = 'none' | |
84 | ||
85 | this.autoplayRing = container.getElementsByClassName('vjs-upnext-svg-autoplay-ring')[0] as HTMLElement | |
86 | this.title = container.getElementsByClassName('vjs-upnext-title')[0] as HTMLElement | |
87 | this.cancelButton = container.getElementsByClassName('vjs-upnext-cancel-button')[0] as HTMLElement | |
88 | this.suspendedMessage = container.getElementsByClassName('vjs-upnext-suspended')[0] as HTMLElement | |
89 | this.nextButton = container.getElementsByClassName('vjs-upnext-autoplay-icon')[0] as HTMLElement | |
90 | ||
91 | this.cancelButton.onclick = () => { | |
92 | this.upNextEvents.trigger('cancel') | |
93 | } | |
94 | ||
95 | this.nextButton.onclick = () => { | |
96 | this.upNextEvents.trigger('next') | |
97 | } | |
98 | ||
99 | return container | |
100 | } | |
101 | ||
102 | showCard (cb: Function) { | |
103 | let timeout: any | |
104 | ||
105 | this.autoplayRing.setAttribute('stroke-dasharray', '' + this.dashOffsetStart) | |
106 | this.autoplayRing.setAttribute('stroke-dashoffset', '' + -this.dashOffsetStart) | |
107 | ||
108 | this.title.innerHTML = this.options_.getTitle() | |
109 | ||
110 | this.upNextEvents.one('cancel', () => { | |
111 | clearTimeout(timeout) | |
112 | cb(true) | |
113 | }) | |
114 | ||
115 | this.upNextEvents.one('playing', () => { | |
116 | clearTimeout(timeout) | |
117 | cb(true) | |
118 | }) | |
119 | ||
120 | this.upNextEvents.one('next', () => { | |
121 | clearTimeout(timeout) | |
122 | cb(false) | |
123 | }) | |
124 | ||
125 | const goToPercent = (percent: number) => { | |
126 | const newOffset = Math.max(-this.dashOffsetTotal, - this.dashOffsetStart - percent * this.dashOffsetTotal / 2 / 100) | |
127 | this.autoplayRing.setAttribute('stroke-dashoffset', '' + newOffset) | |
128 | } | |
129 | ||
130 | const tick = () => { | |
131 | goToPercent((this.ticks++) * 100 / this.totalTicks) | |
132 | } | |
133 | ||
134 | const update = () => { | |
135 | if (this.options_.suspended()) { | |
136 | this.suspendedMessage.innerText = this.options_.suspendedText | |
137 | goToPercent(0) | |
138 | this.ticks = 0 | |
139 | timeout = setTimeout(update.bind(this), 300) // checks once supsended can be a bit longer | |
140 | } else if (this.ticks >= this.totalTicks) { | |
141 | clearTimeout(timeout) | |
142 | cb(false) | |
143 | } else { | |
144 | this.suspendedMessage.innerText = '' | |
145 | tick() | |
146 | timeout = setTimeout(update.bind(this), this.interval) | |
147 | } | |
148 | } | |
149 | ||
150 | this.container.style.display = 'block' | |
151 | timeout = setTimeout(update.bind(this), this.interval) | |
152 | } | |
153 | } | |
154 | ||
155 | videojs.registerComponent('EndCard', EndCard) |