How to make it so that the progress bar with the progress line and the numeric display of the active slide from the total number of slides are displayed simultaneously?
Since by default the simultaneous operation of the progress bar and pagination with a fraction is impossible, I added "type: 'progressbar'" in the "pagination" properties, and for the numeric display of slides I made an .image-slider__fraction block in which I placed all the elements.
Then I wrote java script code that I found on the Internet, but I get an error "Uncaught ReferenceError: myImageSLider is not defined". Why?
Site ilyin1ib.beget.tech
Code https://jsfiddle.net/qav8z7f3/
let mySliderAllSlides = document.querySelector('.image-slider__total');
let mySliderCurrentSlide = document.querySelector('.image-slider__current');
mySliderAllSlides.innerHTML = myImageSLider.slides.length;
myImageSLider.on('slideChange', function() {
let currentSlide = ++myImageSLider.realIndex;
mySliderCurrentSlide.innerHTML = currentSlide;
});
<div class="image-slider__fraction">
<div class="image-slider__current">1</div>
<div class="image-slider__sepparator">/</div>
<div class="image-slider__total">1</div>
</div>
use slideChange event. The script does not give the actual number of items because you used the loop inside the slider. That's why I found the value of the items before creating the slider script.
var totalSlide = $('.image-slider .swiper-slide').length;
const swiper = new Swiper('.image-slider', {
// Optional parameters
slidesPerView: 3,
spaceBetween: 30,
centeredSlides: true,
loop: true,
loopedSLides: 3,
simulateTouch: true,
grabCursor: true,
speed: 800,
pagination: {
el: '.swiper-pagination',
type: 'progressbar'
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
autoplay: {
delay: 1000,
}
});
//var count = $('.image-slider .image-slider__slide').length;
swiper.on('slideChange', function() {
var fragment = document.querySelector('.image-slider__current');
var current = swiper.realIndex + 1;
if (current > totalSlide)
current = 1;
var idx = current < 10 ? ("0" + current) : current;
var tdx = totalSlide < 10 ? ("0" + totalSlide) : totalSlide;
fragment.innerHTML = (idx + '/' + tdx);
});
demo in jsfiddle
I'm trying to reverse the animation on loopComplete - it works but only once. How can I make it loop forever?
Here's my code:
var params = {
container: document.getElementById('loader'),
renderer: 'svg',
loop: true,
autoplay: true,
animationData: animationData
};
var anim;
anim = lottie.loadAnimation(params);
anim.addEventListener('loopComplete', function() {
anim.setDirection(-1);
anim.play();
})
anim.addEventListener('complete', function() {
anim.setDirection(1);
anim.play();
})
You can use setInterval function for looping through Lottie.
Make loop: false and change the setInterval time according to your Animation.
var params = {
container: document.getElementById('loader'),
renderer: 'svg',
loop: false,
autoplay: true,
animationData: animationData
};
var anim;
anim = lottie.loadAnimation(params);
let dir = 1;
setInterval(function () {
if(dir == 1) {dir = '-1'} else {dir = '1'; }
anim.setDirection(dir);
anim.play();
}, 2500);
how to use the following method in ionic 4: https://swiperjs.com/api/#virtual
I tried to apply it according to the API documentation, but this code does not work. What am I doing wrong?
ngOnInit() {
//----------------------------------------------
var slides = document.querySelector("ion-slides");
// Optional parameters to pass to the swiper instance. See http://idangero.us/swiper/api/ for valid options.
slides.options = {
initialSlide: 1,
direction: "vertical",
speed: 150,
preloadImages: false,
lazy: true,
virtual: {
slides: (function() {
var slides = [];
for (var i = 0; i < 600; i += 1) {
slides.push(
'<ion-slide><img src="https://picsum.photos/600/600/?image=' +
(i + 1) +
'"/><p>' +
(i + 1) +
"</p></ion-slide>"
);
}
console.log(JSON.stringify(slides));
return slides;
}),
}
};
//----------------------------------------------
}
It looks like you have to initialize Swiper first
var mySwiper = new Swiper('.ion-slides');
can you try this here:
let mySwiper = new Swiper('.ion-slides', {
initialSlide: 1,
direction: "vertical",
speed: 150,
preloadImages: false,
lazy: true,
virtual: {
slides: (function() {
var slides = [];
for (var i = 0; i < 600; i += 1) {
slides.push(
'<ion-slide><img src="https://picsum.photos/600/600/?image=' +
(i + 1) +
'"/><p>' +
(i + 1) +
"</p></ion-slide>"
);
}
console.log(JSON.stringify(slides));
return slides;
})
}
});
I want to play videos in a loop. For some reason I don't want change video src on ended event.
So I created video elements for each video in a loop. Also I have video src and durations in array.
here is my idea:
Only current playing video tag can be visible. Others will be hided.
Instead of using ended event, I want to use setTimeout function. Video's duration will be delay parameter.
But they all play together. I couldn't make them play in order.
Here is what I done so far:
videoArray = [
{"video":video1.mp4, "duration": 5},
{"video":video2.mp4, "duration": 7},
{"video":video3.mp4, "duration": 9},
{"video":video4.mp4, "duration": 10},
]
for (var j = 0; j < videoArray.length; j++){
var video = document.createElement("video");
video.src=videoArray[j];
video.id="video-"+j;
video.preload = "metadata";
video.type="video/mp4";
video.autoplay = true;
video.style.display="none";
document.body.appendChild(video);
}
for (var count = 0; count < videoArray.length; count++) {
(function(num){
setTimeout(function() {
videoArray[num].video.style.display="block";
videoArray[num].video.play();
}, 1000 * videoArray[num].duration);
videoArray[num].video.style.display="none";
})(count);
}
Disclaimer
I know that the question was asked without the ended event, but I do not think that set time out is the way to go.
Think of the scenario where you have video buffering, or slowing down for any reason, your setTimeout will be out of sync.
At the bottom I've added another solution that answers the requirement of not using the ended event, but again, I do not recommend using it.
Solution
The idea is to have an event listener to the end of the video, in that case, even if you run the video on a different speed you are still going to run the next video regardless of the duration.
Another benefit is that you do not need to know the duration of the videos in the first place.
PS.
the event listener that you need to listen to is video.addEventListener("ended", callback);
You are more than welcome to run the code or to have a look at a working example I've created for you
Working Example
const videoUrls = [
'https://videos-play-loop.netlify.com/video1.mp4',
'https://videos-play-loop.netlify.com//video2.mp4',
'https://videos-play-loop.netlify.com//video3.mp4',
];
const createVideo = ({id, src, width, cls = 'video', display = 'block', playbackRate = 1, muted = true, type = 'video/mp4', autoplay = false, controls = true}) => {
const videoElement = document.createElement("video");
videoElement.id = id;
videoElement.src = src;
videoElement.classList.add(src);
videoElement.type = type;
videoElement.autoplay = autoplay;
videoElement.controls = controls;
videoElement.style.display = display;
videoElement.muted = muted;
videoElement.playbackRate = playbackRate;
return videoElement;
};
const addVideos = (container, videoUrls) => {
const videos = videoUrls.map((url, index) => {
const first = index === 0;
const display = first ? 'block' : 'none';
return createVideo({id: `video-${index}`, src: url,display, width: 640, autoplay: first, playbackRate: 3});
});
videos.forEach((video, index) => {
const last = index === videos.length - 1;
const playNext = (element) => {
element.target.style.display = "none";
const nextElementIndex = last ? 0 : index + 1;
const nextElement = videos[nextElementIndex];
nextElement.autoplay = true;
nextElement.style.display="block";
nextElement.load();
};
video.addEventListener("ended", playNext);
container.appendChild(video)
});
};
const videoWrapper = document.getElementById('video-wrapper');
addVideos(videoWrapper, videoUrls);
#video-wrapper video {
max-width: 600px;
}
<div id="video-wrapper"></div>
Working solution with setTimeout (please use the solution above)
const videoUrls = [{
url: `https://videos-play-loop.netlify.com/video3.mp4`,
duration: 3,
},
{
url: `https://videos-play-loop.netlify.com/video2.mp4`,
duration: 4
},
{
url: `https://videos-play-loop.netlify.com/video1.mp4`,
duration: 5
}
];
const createVideo = ({
id,
src,
width,
cls = 'video',
display = 'block',
duration,
playbackRate = 1,
muted = true,
type = 'video/mp4',
autoplay = false,
controls = true
}) => {
const videoElement = document.createElement("video");
videoElement.id = id;
videoElement.src = src;
videoElement.classList.add(src);
videoElement.type = type;
videoElement.autoplay = autoplay;
videoElement.controls = controls;
videoElement.style.display = display;
videoElement.muted = muted;
videoElement.playbackRate = playbackRate;
videoElement.setAttribute('data-duration', duration);
return videoElement;
};
const playNext = (videos, index) => {
const current = videos[index];
const activeVideoDuration = parseInt(current.dataset.duration) * 1000;
setTimeout(() => {
const last = index === videos.length - 1;
current.style.display = "none";
current.pause();
const activeVideoIndex = last ? 0 : index + 1;
const next = videos[activeVideoIndex];
next.autoplay = true;
next.style.display = "block";
next.load();
next.play();
playNext(videos, activeVideoIndex);
}, activeVideoDuration);
};
const addVideos = (container, videoUrls) => {
const videos = videoUrls.map((video, index) => {
const {
url,
duration
} = video;
const first = index === 0;
const display = first ? 'block' : 'none';
return createVideo({
id: `video-${index}`,
src: url,
duration,
display,
width: 640,
autoplay: first,
});
});
videos.forEach(video => container.appendChild(video));
playNext(videos, 0);
};
const videoWrapper = document.getElementById('video-wrapper');
addVideos(videoWrapper, videoUrls);
#video-wrapper video {
max-width: 600px;
}
<div id="video-wrapper"></div>
You could hold the duration of the videos in a variable and the accumulate this variable with the previous video duration and set this a the setTimeOut duration.
Note that the time of the videos is in seconds. And for the first video to play user has to interact otherwise the video will not play.
Working Example:
function startVideos(event) {
event.target.style.display= "none";
(function() {
videoArray = [
{
video:
"http://techslides.com/demos/sample-videos/small.mp4",
duration: 5
},
{
video:
"http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",
duration: 60
},
{
video:
"https://mobamotion.mobatek.net/samples/sample-mp4-video.mp4",
duration: 120
},
{
video:
"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
duration: 600
}
];
let videArrayElem = [];
for (var j = 0; j < videoArray.length; j++) {
var video = document.createElement("video");
video.src = videoArray[j].video;
video.id = "video-" + j;
video.preload = "metadata";
video.type = "video/mp4";
video.autoplay = false;
video.controls= true;
video.style.display = "none";
videArrayElem.push(video);
document.body.appendChild(video);
}
let prviousVideoDuration = 0;
for (var count = 0; count < videoArray.length; count++) {
(function(num) {
setTimeout(function() {
videArrayElem[num].style.display = "block";
videArrayElem[num].play();
}, prviousVideoDuration);
prviousVideoDuration += 1000 * videoArray[num].duration;
videArrayElem[num].style.display = "none";
})(count);
}
})();
}
video {
height: 100px;
width: 100px;
display: inline-block;
margin: 4px;
float: left;
}
<button type="button" onclick="startVideos(event)">Start Video Demo</button>
Aside from the facts that you are trying to achieve something strange, and that your code example will not compile. As I see it, you've got it.
The only missing thing is pause() and display = "none". With minimum edits you have what you need.
const videoArray = [
{"video":"video1.mp4", "duration": 5}, // of cause you need ""
{"video":"video2.mp4", "duration": 7},
{"video":"video3.mp4", "duration": 9},
{"video":"video4.mp4", "duration": 10},
]
for (let j = 0; j < videoArray.length; j++){
const video = document.createElement("video");
// you need to save DOM nodes somewhere to use them in the second loop
videoArray[j].video_el = video
video.src=videoArray[j].video; // `.video` is added
video.id="video-"+j;
video.preload = "metadata";
video.type="video/mp4";
video.autoplay = true;
video.style.display="none";
document.body.appendChild(video);
}
for (var count = 0; count < videoArray.length; count++) {
(function(num){
setTimeout(function() {
// add this to hide previous video node and stop video from playing
if( num ) {
videoArray[num-1].video_el.style.display="none";
videoArray[num-1].video_el.pause()
}
// videoArray[num].video - is a string, not a DOM node
// so, you need to change this:
// videoArray[num].video.style.display="block";
// for this:
videoArray[num].video_el.style.display="block";
// no need. `autoplay` is set to `true` in the first loop
// videoArray[num].video_el.play();
}, 1000 * videoArray[num].duration);
// no need. already done in the first loop
// videoArray[num].video_el.style.display="none";
})(count);
}
But there is a lot of flaws:
setTimeout does not care about network delays and other time related issues, so most probably your video sequence will not play seamlessly.
Because of the first flow and because I doubt that duration values are exact you should use pause().
As #IslamElshobokshy noted in comments to your question using play/pause is not so simple.
If user opens the page and does nothing more, you'll get:
Uncaught (in promise) DOMException: play() failed because the user didn't interact with the document first.
in Chrome. And
Autoplay is only allowed when approved by the user, the site is activated by the user, or media is muted.
NotAllowedError: The play method is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.
in Firefox.
So you'd probably be better off with the ended event after all, and with muted attribute (to mitigate the last issue):
for (let j = 0; j < videoArray.length; j++){
const video = document.createElement("video")
videoArray[j].video_el = video
video.src = videoArray[j].video
video.id = "video-"+j
video.preload = "metadata"
video.type = "video/mp4"
video.autoplay = true
// show the first video right away
if( j !== 0 ) video.style.display = "none"
// set muted attribute
video.muted = "muted"
// play next video after the previous had ended
video.addEventListener('ended', (num => function() {
this.style.display = "none";
if( num !== videoArray.length-1 ) {
videoArray[num+1].video_el.style.display="block";
// only needed if `muted` is not set
videoArray[num+1].video_el.play();
}
})(j), false);
document.body.appendChild(video);
}
If muted attribute is not the option, you can add some event listeners on the body to call play() on the first video as soon as the user starts interaction with the page. And maybe you would like to read more about autoplay here: https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide
I am trying to replace a JavaScript For Loop with the underscore.js each() function.
for (var x = 0; x < count; x++) {
slider[x].setAttribute('id', arguments[x]);
sliderPagination[x].setAttribute('id', arguments[x]+'Pagination');
// Initialise swiper
var slider = new Slider('#'+arguments[x], {
pagination: '#'+arguments[x]+'Pagination',
loop:true,
grabCursor: true,
paginationClickable: true
})
}
I am new to using underscore so not quite sure the best way to do this. Do I need the index iteration when using the _.each() function for this?
UPDATE:
// Function to initialize multiple instances of slider plugin
function loadSliders(values) {
var sliders = document.getElementsByClassName("swiper-container"),
slidersPaginations = document.getElementsByClassName("swiper-pagination"),
count = Math.min(sliders.length, arguments.length),
i = 0;
_.each(sliders, function(sliders, index) {
var argumentsVariable = values[index];
sliders.setAttribute('id', argumentsVariable);
slidersPaginations[index].setAttribute('id', argumentsVariable+'Pagination');
// Initialise swiper
var slider = new Swiper('#'+argumentsVariable, {
pagination: '#'+argumentsVariable+'Pagination',
loop:true,
grabCursor: true,
paginationClickable: true
})
});
}
I'm assuming here that you have 3 arrays:
- sliders
- sliderPaginations
- arguments
Then, you can do it that way:
_.each(sliders, function(slider, index) {
var argumentsVariable = arguments[index];
slider.setAttribute('id', argumentsVariable);
sliderPaginations[index].setAttribute('id', argumentsVariable+'Pagination');
// Initialise swiper
var slider = new Slider('#'+argumentsVariable, {
pagination: '#'+argumentsVariable+'Pagination',
loop:true,
grabCursor: true,
paginationClickable: true
})
}
Note that you can use EcmaScript5 forEach method that is defined for each array:
sliders.forEach(function(slider, index) {
var argumentsVariable = arguments[index];
slider.setAttribute('id', argumentsVariable);
sliderPagination.setAttribute('id', argumentsVariable+'Pagination');
// Initialise swiper
var slider = new Slider('#'+argumentsVariable, {
pagination: '#'+argumentsVariable+'Pagination',
loop:true,
grabCursor: true,
paginationClickable: true
})
}