PageSourceSearch

https://www.ticketfairy.com/resources/js/canvas-video-player.js

js ticketfairy.com collected 2026-09-24 09:09:59 UTC 6,974 bytes, 273 lines download raw bytes

1var cvpHandlers = {
2	canvasClickHandler: null,
3	videoTimeUpdateHandler: null,
4	videoCanPlayHandler: null,
5	windowResizeHandler: null
6};
7
8var CanvasVideoPlayer = function(options) {
9	var i;
10
11	this.options = {
12		framesPerSecond: 25,
13		hideVideo: true,
14		autoplay: false,
15		makeLoop: false,
16		pauseOnClick: true,
17		audio: false,
18		timelineSelector: false,
19		resetOnLastFrame: true
20	};
21
22	for (i in options) {
23		this.options[i] = options[i];
24	}
25
26	this.video = document.querySelector(this.options.videoSelector);
27	this.canvas = document.querySelector(this.options.canvasSelector);
28	this.timeline = document.querySelector(this.options.timelineSelector);
29	this.timelinePassed = document.querySelector(this.options.timelineSelector + '> div');
30
31	if (!this.options.videoSelector || !this.video) {
32		console.error('No "videoSelector" property, or the element is not found');
33		return;
34	}
35
36	if (!this.options.canvasSelector || !this.canvas) {
37		console.error('No "canvasSelector" property, or the element is not found');
38		return;
39	}
40
41	if (this.options.timelineSelector && !this.timeline) {
42		console.error('Element for the "timelineSelector" selector not found');
43		return;
44	}
45
46	if (this.options.timelineSelector && !this.timelinePassed) {
47		console.error('Element for the "timelinePassed" not found');
48		return;
49	}
50
51	if (this.options.audio) {
52		if (typeof(this.options.audio) === 'string'){
53			// Use audio selector from options if specified
54			this.audio = document.querySelectorAll(this.options.audio)[0];
55
56			if (!this.audio) {
57				console.error('Element for the "audio" not found');
58				return;
59			}
60		} else {
61			// Creates audio element which uses same video sources
62			this.audio = document.createElement('audio');
63			this.audio.innerHTML = this.video.innerHTML;
64			this.video.parentNode.insertBefore(this.audio, this.video);
65			this.audio.load();
66		}
67
68		var iOS = /iPad|iPhone|iPod/.test(navigator.platform);
69		if (iOS) {
70			// Autoplay doesn't work with audio on iOS
71			// User have to manually start the audio
72			this.options.autoplay = false;
73		}
74	}
75
76	// Canvas context
77	this.ctx = this.canvas.getContext('2d');
78
79	this.playing = false;
80
81	this.resizeTimeoutReference = false;
82	this.RESIZE_TIMEOUT = 1000;
83
84	this.init();
85	this.bind();
86};
87
88CanvasVideoPlayer.prototype.init = function() {
89	this.video.load();
90
91	this.setCanvasSize();
92
93	if (this.options.hideVideo) {
94		this.video.style.display = 'none';
95	}
96};
97
98// Used most of the jQuery code for the .offset() method
99CanvasVideoPlayer.prototype.getOffset = function(elem) {
100	var docElem, rect, doc;
101
102	if (!elem) {
103		return;
104	}
105
106	rect = elem.getBoundingClientRect();
107
108	// Make sure element is not hidden (display: none) or disconnected
109	if (rect.width || rect.height || elem.getClientRects().length) {
110		doc = elem.ownerDocument;
111		docElem = doc.documentElement;
112
113		return {
114			top: rect.top + window.pageYOffset - docElem.clientTop,
115			left: rect.left + window.pageXOffset - docElem.clientLeft
116		};
117	}
118};
119
120CanvasVideoPlayer.prototype.jumpTo = function(percentage) {
121	this.video.currentTime = this.video.duration * percentage;
122
123	if (this.options.audio) {
124		this.audio.currentTime = this.audio.duration * percentage;
125	}
126};
127
128CanvasVideoPlayer.prototype.bind = function() {
129	var self = this;
130
131	// Playes or pauses video on canvas click
132	if(this.options.pauseOnClick === true){
133		this.canvas.addEventListener('click', cvpHandlers.canvasClickHandler = function() {
134			self.playPause();
135		});
136	}
137
138	// On every time update draws frame
139	this.video.addEventListener('timeupdate', cvpHandlers.videoTimeUpdateHandler = function() {
140		self.drawFrame();
141		if (self.options.timelineSelector) {
142			self.updateTimeline();
143		}
144	});
145
146	// Draws first frame
147	this.video.addEventListener('canplay', cvpHandlers.videoCanPlayHandler = function() {
148		self.drawFrame();
149	});
150
151	// To be sure 'canplay' event that isn't already fired
152	if (this.video.readyState >= 2) {
153		self.drawFrame();
154	}
155
156
157	if (self.options.autoplay) {
158	  self.play();
159	}
160
161	// Click on the video seek video
162	if (self.options.timelineSelector) {
163		this.timeline.addEventListener('click', function(e) {
164			var offset = e.clientX - self.getOffset(self.canvas).left;
165			var percentage = offset / self.timeline.offsetWidth;
166			self.jumpTo(percentage);
167		});
168	}
169
170	// Cache canvas size on resize (doing it only once in a second)
171	window.addEventListener('resize', cvpHandlers.windowResizeHandler = function() {
172		clearTimeout(self.resizeTimeoutReference);
173
174		self.resizeTimeoutReference = setTimeout(function() {
175			self.setCanvasSize();
176			self.drawFrame();
177		}, self.RESIZE_TIMEOUT);
178	});
179
180	this.unbind = function() {
181		this.canvas.removeEventListener('click', cvpHandlers.canvasClickHandler);
182		this.video.removeEventListener('timeupdate', cvpHandlers.videoTimeUpdateHandler);
183		this.video.removeEventListener('canplay', cvpHandlers.videoCanPlayHandler);
184		window.removeEventListener('resize', cvpHandlers.windowResizeHandler);
185
186		if (this.options.audio) {
187			this.audio.parentNode.removeChild(this.audio);
188		}
189	};
190};
191
192CanvasVideoPlayer.prototype.updateTimeline = function() {
193	var percentage = (this.video.currentTime * 100 / this.video.duration).toFixed(2);
194	this.timelinePassed.style.width = percentage + '%';
195};
196
197CanvasVideoPlayer.prototype.setCanvasSize = function() {
198	this.width = this.canvas.clientWidth;
199	this.height = this.canvas.clientHeight;
200
201	this.canvas.setAttribute('width', this.width);
202	this.canvas.setAttribute('height', this.height);
203};
204
205CanvasVideoPlayer.prototype.play = function() {
206	this.lastTime = Date.now();
207	this.playing = true;
208	this.loop();
209
210	if (this.options.audio) {
211		// Resync audio and video
212		this.audio.currentTime = this.video.currentTime;
213		this.audio.play();
214	}
215};
216
217CanvasVideoPlayer.prototype.pause = function() {
218	this.playing = false;
219
220	if (this.options.audio) {
221		this.audio.pause();
222	}
223};
224
225CanvasVideoPlayer.prototype.playPause = function() {
226	if (this.playing) {
227		this.pause();
228	}
229	else {
230		this.play();
231	}
232};
233
234CanvasVideoPlayer.prototype.loop = function() {
235	var self = this;
236
237	var time = Date.now();
238	var elapsed = (time - this.lastTime) / 1000;
239
240	// Render
241	if(elapsed >= (1 / this.options.framesPerSecond)) {
242		this.video.currentTime = this.video.currentTime + elapsed;
243		this.lastTime = time;
244		// Resync audio and video if they drift more than 300ms apart
245		if(this.audio && Math.abs(this.audio.currentTime - this.video.currentTime) > .3){
246			this.audio.currentTime = this.video.currentTime;
247		}
248	}
249
250	// If we are at the end of the video stop
251	if (this.video.currentTime >= this.video.duration) {
252		if(this.options.makeLoop === false){
253			this.playing = false;
254		}
255
256		if (this.options.resetOnLastFrame === true) {
257			this.video.currentTime = 0;
258		}
259	}
260
261	if (this.playing) {
262		this.animationFrame = requestAnimationFrame(function(){
263			self.loop();
264		});
265	}
266	else {
267		cancelAnimationFrame(this.animationFrame);
268	}
269};
270
271CanvasVideoPlayer.prototype.drawFrame = function() {
272	this.ctx.drawImage(this.video, 0, 0, this.width, this.height);
273};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.