Animate css progress bar without jumping between updates? - html

I'm using this on my site …
<progress id='video-progress' min='0' max='100' value=''></progress>
This is the entire styling of the element …
#video-progress {
-webkit-appearance: none;
border: none;
position:fixed;
bottom:0;
left:0;
height:3px;
width:100%;
z-index:1;
}
So all it does is move from 0 to 100% screen width on the bottom of the page.
The progress bar is updated via Javascript.
However since my video is only 30 seconds long, the single steps of updates are executed as "jumps" for the progress bar. So there is no smooth motion of the bar.
Any idea how I could animate the progress bar or smooth it between the updated steps?
UPDATE:
JavaScript … 
function updateProgressBar() {
var progressBar = document.getElementById('video-progress');
var percentage = Math.floor((100 / fmVideo.duration) * fmVideo.currentTime);
progressBar.value = percentage;
}

You could animate it by incrementing its value every 15 millisecond using setInterval and stop incrementing if the value is greater than percentage using clearInterval.
This code extracts the current value and increments it until it reaches the percentage value.
Note: percentage is manually set to 70.
var progressBar = document.getElementById('video-progress');
function updateProgressBar() {
var percentage = 70;
var curr = progressBar.value;
var update = setInterval(function() {
if (curr > percentage) {
clearInterval(update);
}
progressBar.value = curr++;
}, 15)
}
updateProgressBar();
#video-progress {
-webkit-appearance: none;
border: none;
position: fixed;
bottom: 0;
left: 0;
height: 3px;
width: 100%;
z-index: 1;
}
<progress id='video-progress' min='0' max='100' value=''></progress>

This works perfectly for me!
function smoothProgress(e) {
var id = $("#"+e.data.id),
dur = 5000,
seq = 100,
max = parseInt( id.attr("max"), 10),
chunk = max / dur * seq,
loop = setInterval(function() {
if( id.val() < max )
id.val( id.val() + chunk );
else
clearInterval(loop);
}
}, seq);
}
$(document).ready(function() {
$("#launch").on("click", {id: $("progress").attr("id")}, smoothProgress);
});
Of course, you can adjust the dur parameter or retrieve it dynamically based on your video's duration, as well as the seq parameter (the lower, the smoother).
Here is a fiddle for demo.

Related

initializing a dynamic background

What my code does:
stays blank for 3 seconds
displayes index 1 of my list of image
loops through the list and restarts at index 0
<div id="background" class="text-center background">
<script type = "text/javascript">
var background = document.getElementById("background");
var currentPos = 0;
var imagesb = ["/images/indeximg0.jpg", "/images/indeximg11.jpg", "/images/indeximg33.jpg", "/images/indeximg44.jpg", "/images/indeximg55.jpg"], i = 0;
function changeimage()
{
if (++currentPos >= imagesb.length)
currentPos = 0;
background.style.backgroundImage = "url(" + imagesb[currentPos] + ")";
}
setInterval(changeimage, 3000);
</script>
</div>
What I want it to do:
no 3 second blank background delay
starts with index 0
What I tried:
I set currentPos = 4
this fixed the issue with the first image being displayed but the 3 second delay was still there. I also don't like this way of doing it because I would have to manually change currentPos if i add or remove images to the list
I tried to initialize my background before the first div to fix the 3 second back background with he following code:
<script type = "text/javascript">
background.style.backgoundImage= "url(/images/indeximg0.jpg)";
</script>
nothing changed. still had the 3 second delay
You should
Call the function once before the setInterval() and
When doing ++currentPos you are actually incrementing the variable so the first time you set the background, the value is already 1. The easiest way to change that is to set the background before your if test.
var background = document.getElementById("background");
var currentPos = 0;
var imagesb = ["https://via.placeholder.com/100", "https://via.placeholder.com/200", "https://via.placeholder.com/300"];
function changeimage() {
background.style.backgroundImage = "url(" + imagesb[currentPos] + ")";
if ((++currentPos) >= imagesb.length) {
currentPos = 0;
}
}
changeimage();
setInterval(changeimage, 1000);
body,
html {
height: 100%;
width: 100%;
margin: 0;
}
.background {
height: 100%;
width: 100%;
}
<div id="background" class="text-center background"></div>

Scrolling parent of display:grid unexpectedly scrolls when a child's grid-row-start changes

We have a list with cells that have explicit grid-rows/columns set inside a grid which is inside a scrollable element. We draw a selection border over a number of these cells.
When the scrolling element is not scrolled all the way to the top, changing the selection element's grid-row-start causes the scrolling area to scroll to the point where the offset of the top of the selection element in relation to the viewport is maintained. In other words, if the selection element is 100px from the top of the viewport, and you click on a cell lower down on the page, the scroll element will scroll down until the selection element is 100px from the top of the viewport again.
This usually happens. Sometimes it doesn't on a fresh page. But click on the scrollbar and scrolling a teeny bit causes this behavior to start happening again. This leads me to believe this is a browser bug and not intended behavior, but it repros in Chrome, Edge, AND FireFox.
You can even open the F12 tools and modify grid-row-start manually, and the scrolling behavior occurs.
Sample code is below. I've found a workaround, which is commented out, but it is not ideal. To replicate the "bug", scroll down a bit then start clicking on cells.
Is this intended behavior? What's causing it? Any easy way to avoid it?
Thanks!
window.onload = () => {
const list = document.getElementById('list');
const numRows = 100;
const numColumns = 8;
for (let i = 0; i < numRows; i++) {
let row = document.createElement('div');
row.className = 'row';
row.style.gridRow = String(i + 1);
for (let j = 0; j < numColumns; j++) {
let cell = document.createElement('div');
cell.className = 'cell';
cell.style.gridRow = String(i + 1);
cell.style.gridColumn = String(j + 1);
cell.innerText = `${i} - ${j}`;
cell.onclick = () => {
const selection = document.getElementById('selection');
selection.style.gridArea = `${i+1} / ${j+1} / ${i+2} / ${j+2}`;
/*
selection.style.display = 'none';
setTimeout(() => {
document.getElementById('selection').style.display = '';
}, 0);
*/
};
row.appendChild(cell);
}
list.appendChild(row);
}
};
.list {
display: grid;
}
.row {
display: contents;
}
.cell {
outline: 1px solid black;
padding: 12px 4px;
}
.selection {
outline: 5px solid green;
grid-area: 20 / 4 / 21 / 5;
z-index: 1;
}
<div class='list' id='list'>
<div class='selection' id='selection'></div>
</div>

Loop through array of elements to display on browser every X seconds

I am trying to make an array of text elements to display every X number of seconds. For example "Hello" would display and then after X seconds the text would change and display "I am 2 years old.
I am grabbing a DOM element and using .innerHTML to change the text with the dialog array elements created in javascript. I troubleshot the code and it seems like setTimeout is not working because it is not waiting every X seconds to display each array element (I have it for 5 seconds). I believe this is why I am getting only the last element to display instead of seeing each one display X number of second. Can someone help me out? I am also very new to coding.
Thanks.
Also It would be great if you can help me with creating the effect of fading the text elements in an out each time the text is changed.
var dialog = ['Hello,', 'My name is James', 'I am 2 years old.', 'I have a dog'];
function changeText() {
var timer = 0;
for (var i = 0; i < dialog.length; i++) {
setTimeout(document.getElementById('h1').innerHTML = dialog[i], timer);
timer = timer + 5000;
}
}
changeText();
<div id="h1">Hello</div>
First, you are not using the setTimeout() function correctly. Corrected version would be.
var inside for loop should be converted to let. Read why?
var dialog = ['Hello,', 'My name is James', 'I am 2 years old.', 'I have a dog'];
function changeText() {
var timer = 0;
// Use let instead of var
for (let i = 0; i < dialog.length; i++) {
setTimeout(() => {
document.getElementById('h1').innerHTML = dialog[i];
}, timer);
timer = timer + 5000;
}
}
changeText();
<div id="h1">Hello</div>
Its better to use setInterval() instead of setTimeout. Also initialize let elem = document.getElementById('h1'); at once not each time timer has been called.
var dialog = ['Hello,', 'My name is James', 'I am 2 years old.', 'I have a dog'];
let displayIndex = 0;
let elem = document.getElementById('h1');
let delay = 1000; // 1 second delay
setInterval(() => {
if (elem) {
elem.innerHTML = dialog[displayIndex];
}
// Move to the next item in dialog
displayIndex++;
// If display index goes out of index range, start again
if (displayIndex >= dialog.length) {
displayIndex = 0;
}
}, delay);
<div id="h1"></div>
Fading effect version
To get fading effect you need to change you html structure as well as javascript accordingly.
let displayIndex = 0;
let elems = $('#h1 > span');
let delay = 1000;
setInterval(() => {
elems.removeClass();
elems.addClass('hidden');
// Move to the next item in dialog
displayIndex++;
// If display index goes out of index range, start again
if (displayIndex >= elems.length) {
displayIndex = 0;
}
$(elems[displayIndex]).addClass('visible');
}, delay);
#h1 {
position: relative;
}
#h1 span {
position: absolute;
left: 0;
top: 0;
opacity: 0;
visibility: hidden;
transition: all 0.4s ease-in-out;
transition-delay: 0s;
}
span.visible {
opacity: 1 !important;
visibility: visible !important;
}
span.hidden {
opacity: 0;
visibility: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="h1">
<span class="visible">Hello,</span>
<span class="hidden">My name is James</span>
<span class="hidden">I am 2 years old.</span>
<span class="hidden">I have a dog</span>
</div>
setTimeout accepts a callback, not plain code statements - pass it a function instead. Also use let instead of var so that each iteration has a separate binding for i:
var dialog = ['Hello,', 'My name is James', 'I am 2 years old.', 'I have a dog'];
function changeText() {
var timer = 0;
for (let i = 0; i < dialog.length; i++) {
setTimeout(() => document.getElementById('h1').innerHTML = dialog[i], timer);
timer = timer + 1000;
}
}
changeText();
<div id="h1">Hello</div>
Check docs for setTimeout. First argument needs to be anonymous function or function. Try to wrap your first argument in setTimeout in 'function() {}' .

How to make progress bar match curve of parent

I have a toast notification with a progress bar like the following image and I like the rounded corners but I can't figure out how to hide the portion of the loading bar that goes outside the rounded corners. How would I do that given the setup in this example. I would also like to know how I could reverse the direction of the indicator so it starts full and goes toward empty then the notification disappears. Lobibox doesn't appear to have either of these options out of the box but I would really like to add them. Thanks for the help!
Here is a sample of a lobibox notification:
Lobibox.notify('success', {
size: 'mini',
rounded: true,
delayIndicator: true,
msg: 'Project Saved Successfully!',
iconSource: 'fontAwesome',
position: 'top right',
delay: 50000,
});
you can override the css
Lobibox.notify('success', {
size: 'mini',
rounded: true,
delayIndicator: false,
msg: 'Project Saved Successfully!',
iconSource: 'fontAwesome',
position: 'top right',
delay: 20000,
delayIndicator: true
});
body {
background-color: black;
}
.lobibox-notify .lobibox-delay-indicator {
left: 22px !important;
width: 360px;
}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lobibox#1.2.7/dist/css/lobibox.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lobibox#1.2.7/dist/js/lobibox.min.js"></script>
Figured out how to reverse the indicator direction. Find the _addDelay function in the source and overwrite it with my updated version below. This adds the ability to set options.reverseDelayIndicator = true to reverse the direction of the indicator. It also allows you to have the indicator display properly on rounded and square edge notifications if you include the css snippet below in your solution.
var _addDelay = function ($el) {
if (!me.$options.delay) {
return;
}
if (me.$options.delayIndicator) {
var delay = $('<div class="lobibox-delay-indicator"><div></div></div>');
if (me.$options.rounded) {
delay.addClass("lobibox-delay-rounded");
} else {
delay.removeClass("lobibox-delay-rounded");
}
$el.append(delay);
}
var time = 0;
var interval = 1000 / 30;
var currentTime = new Date().getTime();
var timer = setInterval(function () {
if (me.$options.continueDelayOnInactiveTab) {
time = new Date().getTime() - currentTime;
} else {
time += interval;
}
if (me.$options.reverseDelayIndicator) {
var width = 100 - (100 * time / me.$options.delay);
if (width <= 0) {
width = 0;
me.remove();
timer = clearInterval(timer);
}
} else {
var width = 100 * time / me.$options.delay;
if (width >= 100) {
width = 0;
me.remove();
timer = clearInterval(timer);
}
}
if (me.$options.delayIndicator) {
delay.find('div').css('width', width + "%");
}
}, interval);
if (me.$options.pauseDelayOnHover) {
$el.on('mouseenter.lobibox', function () {
interval = 0;
}).on('mouseleave.lobibox', function () {
interval = 1000 / 30;
});
}
};
CSS To allow both rounded and square indicators display properly:
.lobibox-notify .lobibox-delay-indicator.lobibox-delay-rounded {
left: 22px;
width: calc(100% - 44px);
}

Smooth same screen scrolling [duplicate]

I'm using this link:
<a class="" onclick="location.href='#top'" href="superContent">superContent</a>
It does two things at once:
Jumps user to top of the page
Performs this other (unrelated) ajax load function
Everything works great, except I'm trying to figure out how to get it to scroll to the top more smoothly. I've tried adding .scroll to attach it to my jquery scrollTo plugin, but nothing happens, which probably has something to do with the fact that I'm using javascript onclick, while the href attribute does something else entirely.
Is there a way to attach animated smooth-scrolling to onclick="location.href='#top'" ?
Try this, it animates the scrollTop() function.
Set link's id:
<a id="link">link</a>
jquery to scroll:
$('#link').click(function(e){
var $target = $('html,body');
$target.animate({scrollTop: $target.height()}, 500);
});
document.querySelector('button').addEventListener('click', function(){
scrollTo( document.querySelector('aside'), Math.floor(Math.random() * 1000) + 1 , 600 );
});
function scrollTo(element, to, duration) {
var start = element.scrollTop,
change = to - start,
currentTime = 0,
increment = 20;
var animateScroll = function(){
currentTime += increment;
var val = Math.easeInOutQuad(currentTime, start, change, duration);
element.scrollTop = val;
if(currentTime < duration) {
setTimeout(animateScroll, increment);
}
};
animateScroll();
}
//t = current time
//b = start value
//c = change in value
//d = duration
Math.easeInOutQuad = function (t, b, c, d) {
t /= d/2;
if (t < 1) return c/2*t*t + b;
t--;
return -c/2 * (t*(t-2) - 1) + b;
};
button{ float:left; }
aside{ height:200px; width:50%; border:2px dashed red; overflow:auto; }
aside::before{
content:'';
display:block;
height:1000px;
background: linear-gradient(#3f87a6, #ebf8e1, #f69d3c);
}
<button>click to random scroll</button>
<aside></aside>