I'm building a CMS site for a client who'll be uploading videos. I'm trying to build a custom progress bar for each video too, I've done this before but there was only one video present so I could use a getElementByID to call each video, but as there's going to be multiple videos per page I tried to call an each function and find each element by it's class within it's parent container.
You can see my codepen here: https://codepen.io/neal_fletcher/pen/JJzbYP
As you can see though the progress bar isn't moving when the video plays (as it should), there's only one video here but there will be multiple videos so I CANT use a function that relies on ID's of each element.
My jQuery markup below too:
window.onload = function() {
$(".video-wrap").each(function() {
var slideshowVideo = document.getElementsByClassName("homepage-video");
var slideshowSeek = document.getElementsByClassName("seek-bar");
slideshowSeek.addEventListener("change", function() {
// Calculate the new time
var time = slideshowVideo.duration * (slideshowSeek.value / 100);
// Update the video time
slideshowVideo.currentTime = time;
});
});
Any suggestions on the best solution for this would be greatly appreciated!
Working code
window.onload = function() {
$(".video-wrap").each(function() {
var slideshowVideo = document.getElementsByClassName("homepage-video");
var slideshowSeek = document.getElementsByClassName("seek-bar");
slideshowSeek[0].addEventListener("change", function() {
// Calculate the new time
var time = slideshowVideo.video.duration * (slideshowSeek[0].value / 100);
// Update the video time
slideshowVideo.video.currentTime = time;
});
slideshowVideo.video.addEventListener('timeupdate', function(video){
slideshowSeek[0].value = slideshowVideo.video.currentTime * 100 / slideshowVideo.video.duration;
}, false);
});
}
check below pen
https://codepen.io/anon/pen/qjvRXP?editors=0110
Related
My aim is to add buttons below my player that jump to specific moments in the video.
There's a demo example that does basically what I want:
https://flowplayer.com/demos/using-the-api — however it is based on the cloud player implementation of Flowplayer, and I'm using the Javascript API to embed my player. The basic relevant script is this:
flowplayer.cloud.then(function () {
var api = flowplayer("#player")
seek3.onclick = function() {
api.fas.seek_to('00:01:01:11');
}
});
An example I found of creating buttons using the Javascript API is as follows:
flowplayer(function(opts, root, api) {
// small jQuery-compatible utility built inside the player
var $ = flowplayer.mq;
// render a custom button when the player is mounted
api.on('mount', function() {
var btn = $('<button>').txt('Test Button').on('click', function() {
api.toggleMute();
});
$('.fp-controls', root).append(btn);
});
});
That works fine with my player. When I try to merge the two approaches, though, I fail. Here is the broken code:
flowplayer(function(opts, root, api) {
var api = flowplayer("#flowplayer");
seek3.onclick = function() {
api.fas.seek_to('00:01:01:11');
}
});
I tried also as alternatives
document.getElementById('seek3').onclick = function()
And
$('#seek1').onclick = function() {
(preceded by the additional line of code borrowed from the working example):
var $ = flowplayer.mq;
but with every variation I keep getting the following error: "Uncaught TypeError: Cannot set property 'onclick' of null".
Any help would be much appreciated. I've found the Flowplayer documentation really difficult to use, since in search results it's often hard to even tell which version of the player is being referenced. I would really like to find some basic info about interacting with the API in addition to solving this particular problem.
For anyone else who might be having difficulties interacting with the API, Flowplayer kindly posted a new demo example -- with similar functionality as in the cloudplayer demo, but this one for the javascript player: https://codepen.io/team/flowplayer/pen/KOzWOK
var api = flowplayer("#player",
{ src : "//edge.flowplayer.org/drive.m3u8"
, autoplay : false
, token : "eyJraWQiOiJqOEF6djg5NlJMMWIiLCJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJjIjoie1wiYWNsXCI6NixcImlkXCI6XCJqOEF6djg5NlJMMWJcIixcImRvbWFpblwiOltcIm5ncm9rLmlvXCJdfSIsImlzcyI6IkZsb3dwbGF5ZXIifQ.UtJliSh4IcIKs71PVzXWdIrJJ8-1_KRK0hKd7OKp5EJhAdpZStuYbbU4bgDs--C6Gak6OBrb-xQBh6sd4dyrlA"
, muted : true
})
/**
* helper function to seek if possible or
* tell the player to seek when possible
*
* btn {HTMLElement}
* seconds {Number}
*/
function chapter (btn, seconds) {
btn.onclick = function () {
// player is preload=none so we must tell
// the player to seek when it is possible
if (api.readyState == 0) api.setOpts({start_time: seconds})
// player has already started loading content
if (api.readyState > 0) api.currentTime = seconds
// attempt to play content
api.togglePlay(true)
}
}
// bind the buttons to times
chapter(seek1, 20)
chapter(seek2, 45)
chapter(seek3, 60)
My working version includes the following variations on the JS code:
/**
* helper function to seek if possible or
* tell the player to seek when possible
*
* seconds {Number}
*/
function seek_to_cue (seconds) {
//alert('bingo?'); //tft
// player is preload=none so we must tell the player to seek when it is possible
if (api.readyState == 0) {
api.setOpts({start_time: seconds});
}
// player has already started loading content
if (api.readyState > 0) {
api.currentTime = seconds;
}
// attempt to play content
api.togglePlay(true);
}
and in my PHP code which includes the JS player call:
$button_actions .= 'document.getElementById("'.$button_id.'").addEventListener("click", function(){ seek_to_cue('.$start_time_seconds.'); });';
Trying to get links to map to different points in an embedded Vimeo video, like chapter markers.
I'm using their API and it works for the first link I've got, but not the second.
JSFiddle Link 15 Seconds link works fine, the 30 seconds link does nada.
I get it's probably to do with this line in the setupChapterLinks() function:
var links = container.querySelector('ul.chapterLinks'),
Which is only going to return the first item it finds (15 secs), and ignores the rest.
Any ideas what I'm not doing correctly?
Your issue was that you weren't looping through the list of links you queried for. The result was that only the first link had event attached. Here's an example of how you would do it to all of them (a setupChapterLinks method to replace your current one):
function setupChapterLinks() {
var links = container.querySelector('ul.chapterLinks'),
seekLinks = links.querySelectorAll('.seek');
for (var i = 0, length = seekLinks.length; i < length; i++)
{
link = seekLinks[i];
// Call seekTo when seek link clicked
addEvent(link, 'click', function() {
var seekVal = this.name;
froogaloop.api('seekTo', seekVal);
}, false);
}
}
Basically I'm trying to merge the two scripts below for a project. My goal is to keep the functionality of the first link, where it fades in random divs at different intervals, but attach it to PaulPRO's version, in that it keeps looping over and over again, say every 5 seconds. Any help is greatly appreciated!
http://jsfiddle.net/cMQdj/1/ (thanks to mblase75)
http://jsfiddle.net/Paulpro/G4pxq/ (thanks to PaulPRO)
Hows this ->
http://jsfiddle.net/G4pxq/9/
(function fadeInDiv(){
var divs = $('.fadeIn');
var elem = divs.eq(Math.floor(Math.random()*divs.length));
if (!elem.is(':visible')){
elem.fadeIn(Math.floor(Math.random()*1000),fadeInDiv);
} else {
elem.fadeOut(Math.floor(Math.random()*1000),fadeInDiv);
}
})();
Update
Maintaining position / order :
http://jsfiddle.net/G4pxq/12/
$('.fadeIn').before('<div> </div>');
(function fadeInDiv() {
var divs = $('.fadeIn');
var elem = divs.eq(Math.floor(Math.random() * divs.length));
if (!elem.is(':visible')) {
elem.prev().remove();
elem.fadeIn(Math.floor(Math.random() * 1000), fadeInDiv);
} else {
elem.fadeOut(Math.floor(Math.random() * 1000), function() {
elem.before('<div> </div>');
fadeInDiv();
});
}
})();
I think you have your examples reversed.
If you want to loop Paul's example, just surround his code in a setInterval method.
setInterval($('.fadeIn').before('<div> </div>'), 2000);
I want to display several images of the same size at the same position, one at a time, with a 5s interval between each change. To do so I've used jQuery.Timer, that uses setInterval() to call some show_next_image() function every 5s.
It actually does work with IE, Opera, Safara, Firefox and.. partly with Google Chrome. It's not working with Google Chrome if I open a new window and directly type my website URL: it'll show the second image and stop. And with any other situation (reload, from another link, not right after opening a new window) it'll badly work: one can see the back image before the front image is shown.
Thus I'm wondering whether I've done something wrong with my JavaScript source. What I do is I use a front and a back image. When I want to show the next image, the back img source is set to the new image, and the front image is faded out while the back one is faded in through jQuery. You can check it out at http://www.laurent-carbon.com/ (in French). The two img are identified with bg1 and bg2.
var images = ["/img/IMG_0435bg.jpg", "/img/IMG_0400bg.jpg", "/img/maisonnette 2.jpg", "/img/IMG_0383bg.jpg", "/img/IMG_0409bg.jpg", "/img/IMG_0384bg.jpg"];
var idx = 1;
var waitTime = 5000; // ms
$(document).ready(function() {
$("#bg2").hide();
$.timer(waitTime, load_next);
$.preLoadImages(images);
});
function load_next(timer) {
var toshow = images[idx];
idx++;
idx %= images.length;
back_image().attr('src', toshow);
swap_images();
}
function front_image() {
return (idx % 2 == 0) ? $("#bg1") : $("#bg2");
}
function back_image() {
return (idx % 2 == 0) ? $("#bg2") : $("#bg1");
}
function swap_images() {
back_image().fadeOut('slow');
front_image().fadeIn('slow');
}
Thanks,
Ceylo
Ok I've worked out a solution .... without the use of plugins.
Demo
http://jsfiddle.net/morrison/PvPXM/9/show
source
http://jsfiddle.net/morrison/PvPXM/9/
This approach is a lot cleaner and removes the problem I had while viewing your page in chrome: the animation getting out of sync and flashing.
The only thing you have to do in the HTML is wrap the two images in a <div id="fadeBox" style="position:relative"></div>
$(function() {
var images = [
"http://www.laurent-carbon.com/img/IMG_0435bg.jpg",
"http://www.laurent-carbon.com/img/IMG_0400bg.jpg",
"http://www.laurent-carbon.com/img/maisonnette 2.jpg",
"http://www.laurent-carbon.com/img/IMG_0383bg.jpg",
"http://www.laurent-carbon.com/img/IMG_0409bg.jpg",
"http://www.laurent-carbon.com/img/IMG_0384bg.jpg"
];
var idx = 1;
var max = images.length;
var easing = "swing";
var waitTime = 5000; // ms
var fadeTime = 2000; // ms
var fadeShow = function(fadeTime, fadeDelay) {
var $topImage = $("#fadeBox img:last");
$topImage.fadeTo(fadeDelay, 1, function() {
$topImage.fadeTo(fadeTime, 0, easing, function() {
$topImage
.fadeTo(0, 1)
.insertBefore("#fadeBox img:first")
.attr("src", images[++idx == max ? idx = 0 : idx]);
fadeShow(fadeTime, fadeDelay);
});
});
};
fadeShow(fadeTime, waitTime);
});
Hope this helps
PS thanks to Levi for cleaning the code up a bit.
Answer: http://jsfiddle.net/morrison/RxyZY/
Notes:
You are trying to reinvent the wheel. You are creating a simple slideshow. There are numerous plugins to do exactly this and much more. I used jQuery cycle in my example, which is extremely customizable.
You should wrap your stuff up in a function, creating an expression. In my example, the (function($){}(jQuery)) is what does the trick. It scopes your variables to the function, rather than the global namespace.
I want to be able to do a cross fade transition on large images whose width is set to 100% of the screen. I have a working example of what I want to accomplish. However, when I test it out on various browsers and various computers I don't get a buttery-smooth transition everywhere.
See demo on jsFiddle: http://jsfiddle.net/vrD2C/
See on Amazon S3: http://imagefader.s3.amazonaws.com/index.htm
I want to know how to improve the performance. Here's the function that actually does the image swap:
function swapImage(oldImg, newImg) {
newImg.css({
"display": "block",
"z-index": 2,
"opacity": 0
})
.removeClass("shadow")
.animate({ "opacity": 1 }, 500, function () {
if (oldImg) {
oldImg.hide();
}
newImg.addClass("shadow").css("z-index", 1);
});
}
Is using jQuery animate() to change the opacity a bad way to go?
You might want to look into CSS3 Transitions, as the browser might be able to optimize that better than Javascript directly setting the attributes in a loop. This seems to be a pretty good start for it:
http://robertnyman.com/2010/04/27/using-css3-transitions-to-create-rich-effects/
I'm not sure if this will help optimize your performance as I am currently using IE9 on an amped up machine and even if I put the browser into IE7 or 8 document mode, the JavaScript doesn't falter with your current code. However, you might consider making the following optimizations to the code.
Unclutter the contents of the main photo stage by placing all your photos in a hidden container you could give an id of "queue" or something similar, making the DOM do the work of storing and ordering the images you are not currently displaying for you. This will also leave the browser only working with two visible images at any given time, giving it less to consider as far as stacking context, positioning, and so on.
Rewrite the code to use an event trigger and bind the fade-in handling to the event, calling the first image in the queue's event once the current transition is complete. I find this method is more well-behaved for cycling animation than some timeout-managed scripts. An example of how to do this follows:
// Bind a custom event to each image called "transition"
$("#queue img").bind("transition", function() {
$(this)
// Hide the image
.hide()
// Move it to the visible stage
.appendTo("#photos")
// Delay the upcoming animation by the desired value
.delay(2500)
// Slowly fade the image in
.fadeIn("slow", function() {
// Animation callback
$(this)
// Add a shadow class to this image
.addClass("shadow")
// Select the replaced image
.siblings("img")
// Remove its shadow class
.removeClass("shadow")
// Move it to the back of the image queue container
.appendTo("#queue");
// Trigger the transition event on the next image in the queue
$("#queue img:first").trigger("transition");
});
}).first().addClass("shadow").trigger("transition"); // Fire the initial event
Try this working demo in your problem browsers and let me know if the performance is still poor.
I had the same problem too. I just preloaded my images and the transitions became smooth again.
The point is that IE is not W3C compliant, but +1 with ctcherry as using css is the most efficient way for smooth transitions.
Then there are the javascript coded solutions, either using js straight (but need some efforts are needed to comply with W3C Vs browsers), or using libs like JQuery or Mootools.
Here is a good javascript coded example (See demo online) compliant to your needs :
var Fondu = function(classe_img){
this.classe_img = classe_img;
this.courant = 0;
this.coeff = 100;
this.collection = this.getImages();
this.collection[0].style.zIndex = 100;
this.total = this.collection.length - 1;
this.encours = false;
}
Fondu.prototype.getImages = function(){
var tmp = [];
if(document.getElementsByClassName){
tmp = document.getElementsByClassName(this.classe_img);
}
else{
var i=0;
while(document.getElementsByTagName('*')[i]){
if(document.getElementsByTagName('*')[i].className.indexOf(this.classe_img) > -1){
tmp.push(document.getElementsByTagName('*')[i]);
}
i++;
}
}
var j=tmp.length;
while(j--){
if(tmp[j].filters){
tmp[j].style.width = tmp[j].style.width || tmp[j].offsetWidth+'px';
tmp[j].style.filter = 'alpha(opacity=100)';
tmp[j].opaque = tmp[j].filters[0];
this.coeff = 1;
}
else{
tmp[j].opaque = tmp[j].style;
}
}
return tmp;
}
Fondu.prototype.change = function(sens){
if(this.encours){
return false;
}
var prevObj = this.collection[this.courant];
this.encours = true;
if(sens){
this.courant++;
if(this.courant>this.total){
this.courant = 0;
}
}
else{
this.courant--;
if(this.courant<0){
this.courant = this.total;
}
}
var nextObj = this.collection[this.courant];
nextObj.style.zIndex = 50;
var tmpOp = 100;
var that = this;
var timer = setInterval(function(){
if(tmpOp<0){
clearInterval(timer);
timer = null;
prevObj.opaque.opacity = 0;
nextObj.style.zIndex = 100;
prevObj.style.zIndex = 0;
prevObj.opaque.opacity = 100 / that.coeff;
that.encours = false;
}
else{
prevObj.opaque.opacity = tmpOp / that.coeff;
tmpOp -= 5;
}
}, 25);
}