How to add timer inside the progress bar - javascript

I want to add a timer which decrease Automatically (like 10 seconds, 9 seconds... till 0 seconds) but the progress bar will increase. And I am new to javascript, and the below code also copied from another site , so please help me in adding timer inside the progress bar
Till now I did this code
I want to make like this
Demo
<div class="progress"></div>
<style>
.progress-bar {
height: 20px;
background: #1da1f2;
box-shadow: 2px 14px 15px -7px rgba(30, 166, 250, 0.36);
border-radius: 50px;
transition: all 0.5s;
}
.progress {
width: 100%;
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: start;
background: #e6e9ff;
border-radius: 20px;
box-shadow: 0px 10px 50px #abb7e9;
}
</style>
<script>
/*
* (class)Progress<nowValue, minValue, maxValue>
*/
//helper function-> return <DOMelement>
function elt(type, prop, ...childrens) {
let elem = document.createElement(type);
if (prop) Object.assign(elem, prop);
for (let child of childrens) {
if (typeof child == "string") elem.appendChild(document.createTextNode(child));
else elem.appendChild(elem);
}
return elem;
}
//Progress class
class Progress {
constructor(now, min, max, options) {
this.dom = elt("div", {
className: "progress-bar"
});
this.min = min;
this.max = max;
this.intervalCode = 0;
this.now = now;
this.syncState();
if(options.parent){
document.querySelector(options.parent).appendChild(this.dom);
}
else document.body.appendChild(this.dom)
}
syncState() {
this.dom.style.width = this.now + "%";
}
startTo(step, time) {
if (this.intervalCode !== 0) return;
this.intervalCode = setInterval(() => {
console.log("sss")
if (this.now + step > this.max) {
this.now = this.max;
this.syncState();
clearInterval(this.interval);
this.intervalCode = 0;
return;
}
this.now += step;
this.syncState()
}, time)
}
end() {
this.now = this.max;
clearInterval(this.intervalCode);
this.intervalCode = 0;
this.syncState();
}
}
let pb = new Progress(15, 0, 100, {parent : ".progress"});
//arg1 -> step length
//arg2 -> time(ms)
pb.startTo(5, 500);
//end to progress after 5s
setTimeout( () => {
pb.end()
}, 10000)
</script>

I think the core problem is that the code you copied is overly complicated especially for beginners. What I would recommend is to start from what you know and build up.
Here is the functionality you want written using only core principles of JavaScript and CSS.
let initialTime = 10; //All time in seconds
let timeLeft = initialTime;
let interval;
let progressBarTextElement = document.getElementById('progress-bar-text');
let progressBarElement = document.getElementById('progress-bar');
function render() {
let progressPercentage = (1 - (timeLeft / initialTime) ) * 100;
progressBarElement.style.width = progressPercentage + '%';
progressBarTextElement.innerHTML = timeLeft + 's';
}
function tick() {
timeLeft = timeLeft - 1;
if(timeLeft <= 0) {
clearInterval(interval); //Stops interval
}
render(); //Updates html
}
function startProgressBar() {
interval = setInterval(tick, 1000); //Will call tick every second
render();
}
startProgressBar();
html {font-family: -apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",sans-serif;}
.progress-bar-continer {
height: 80px;
width: 100%;
position: relative;
display: flex;
justify-content: center;
align-items: center;
background-color: #406086;
}
.progress-bar {
background-color: #1b3e80;
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 0%;
transition: width 1s; /* Makes progressBar smooth */
transition-timing-function: linear; /* Try to remove this line for more tick tock feel */
}
.progress-bar-text {
color: white;
font-size: 24px;
font-weight: 700;
position: relative;
z-index: 1;
}
<div class="progress-bar-continer">
<div class="progress-bar" id="progress-bar"></div>
<div class="progress-bar-text" id="progress-bar-text"></div>
<div>
Try to understand the code best you can and you will have no problems adding any features you want on top of it. All the fancy stuff will come later with experience.

Related

Simulate 'backspace' in Jquery

I have a simple animation code, looks like a console input.
Originally from: https://codepen.io/atunnecliffe/pen/BaZyLR
I modified the splash screen intro into just a console input in my website:
Code:
<script>
//console
var textarea = $('.term');
var text = 'ping life';
var i = 0;
runner();
function runner() {
textarea.append(text.charAt(i));
i++;
setTimeout(
function () {
runner();
}, Math.floor(Math.random() * 1000) + 50);
}
</script>
Now the effect that I want is a bit complex, for me at least, as my knowledge about JQuery is limited. I wanted the code to enter ping life, then backspace completely, repeat infinitely. I looked up on how to simulate backspace in JQuery, using escape sequence of (8), but I am not sure how to use the escape sequence, nor implement the function into the existing recursive function, for it to repeat infinitely.
Any help would be wonderful :)
Like this?
Counting like this will give a zigzag like counting pattern. I added buffers for start and end of input, and a fixed timeout for deleting letters.
textarea.text(text.substr(0, i)) selects a substring of your text (treated as an array of letters - selecting everything between index 0 and i)
Easier than appending and deleting letters
var direction = 1;
var i = 0;
var textarea = $('.term');
var text = 'ping life';
// NOTE:
// I added the "#dev:~$ " as css:before elem, easier to write the code
function count() {
i += direction;
direction *= (((i % text.length) == 0) ? -1 : 1);
textarea.text(text.substr(0, i));
clearInterval(time);
// direction is 1 if counting up
if (direction === 1) {
if (i === 0) {
// buffer for start
time = setInterval(count, 1000);
} else {
time = setInterval(count, Math.floor(Math.random() * 1000) + 50);
}
} else {
// direction is -1 if counting down
if (i === text.length) {
time = setInterval(count, 1500);
} else {
// buffer for end
time = setInterval(count, 100);
}
}
}
// inital interval
// setTimeout doesn't work well here
var time = setInterval(count, 1000)
html,
body {
margin: 0 auto;
height: 100%;
}
pre {
padding: 0;
margin: 0;
}
pre::before {
content: "#dev:~$ ";
color: white;
}
.load {
margin: 0 auto;
min-height: 100%;
width: 100%;
background: black;
}
.term {
font-family: monospace;
color: #fff;
opacity: 0.8;
font-size: 2em;
overflow-y: auto;
overflow-x: hidden;
padding-top: 10px;
padding-left: 20px;
}
.term:after {
content: "_";
opacity: 1;
animation: cursor 1s infinite;
}
#keyframes cursor {
0% {
opacity: 0;
}
40% {
opacity: 0;
}
50% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
opacity: 0;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="load">
<pre class="term"></pre>
</div>

Iterate HTML classes to create class objects with javascript

class CVP {
constructor(obj) {
const { P, V, PC, BPB, MPB, VRC, VBC, FS, PROG, PROGC, TW, TC } = obj;
// player
this.player = document.querySelector(P);
// video el
this.video = document.querySelector(V);
// player controller
this.playerController = document.querySelector(PC);
// play controlers
this.bigPlayBtn = document.querySelector(BPB);
this.miniPlayBtn = document.querySelector(MPB);
// volume controllers
this.volumeRangeController = document.querySelector(VRC);
this.volumeBtnController = document.querySelector(VBC);
// fullscreen
this.fullscreen = document.querySelector(FS);
// progress bar
this.progress = document.querySelector(PROG);
this.progressCurr = document.querySelector(PROGC);
// time displayers
this.timeWhole = document.querySelector(TW);
this.timeCurr = document.querySelector(TC);
// bool vars
this.isMousedown = false;
this.isFullScreen = false;
// timer vars
this.timer = '';
this.timerTime = 1000;
}
setIsMouseDown = () => {
this.isMousedown = !this.isMousedown;
};
bigPlayBtnHandler = () => {
this.bigPlayBtn.classList.toggle('HideBigPlayBtn');
};
showHideControlls = () => {
this.player.classList.toggle('paused');
};
static changeIcon(target, remove, add) {
target.classList.remove(remove);
target.classList.add(add);
}
changePlayIcon() {
console.log(this.video.src);
const target = this.miniPlayBtn;
let icon = this.video.paused ? 'play' : 'pause';
if (icon === 'pause') {
CVP.changeIcon(target, 'play', 'pause');
} else {
CVP.changeIcon(target, 'pause', 'play');
}
}
changeVolumeIcon(volElIconID) { // not in use
const target = document.querySelector(volElIconID);
if (this.video.muted) {
CVP.changeIcon(target, 'max', 'mute');
} else {
CVP.changeIcon(target, 'mute', 'max');
}
}
handleVideoTime(time) {
// minutes and seconds So Far
let hours;
let min = Math.floor(time / 60);
let sec = Math.floor(time % 60);
let output = '';
sec = (sec < 10) ? sec = `0${sec}` : sec;
min = (min < 10) ? min = `0${min}` : min;
output = `${min}:${sec}`;
return output;
}
rangeVolumeController() {
cvp.video[this.name] = this.value;
if (cvp.volumeRangeController.value == 0) {
cvp.video.muted = true;
CVP.changeIcon(cvp.volumeBtnController, 'max', 'mute');
} else {
cvp.video.muted = false;
CVP.changeIcon(cvp.volumeBtnController, 'mute', 'max');
}
}
btnVolumeController() {
if (cvp.video.muted) {
cvp.video.muted = false;
CVP.changeIcon(cvp.volumeBtnController, 'mute', 'max');
cvp.volumeRangeController.value = 1;
cvp.video.volume = 1;
} else {
cvp.video.muted = true;
CVP.changeIcon(cvp.volumeBtnController, 'max', 'mute');
cvp.volumeRangeController.value = 0;
}
}
hideWhenFullScreen() {
if (this.isFullScreen) {
this.playerController.hidden = false; // or this.playerController.style.bottom = '0px';
document.body.style.cursor = 'auto';
clearTimeout(this.timer);
this.timer = setTimeout(() => {
this.playerController.hidden = true; // or this.playerController.style.bottom = '-50px';
document.body.style.cursor = 'none';
}, this.timerTime);
} else {
this.playerController.hidden = false; // or this.playerController.style.bottom = '0px';
document.body.style.cursor = 'auto';
}
}
toggleFullScreen() {
if (this.isFullScreen) {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitCancelFullScreen) {
document.webkitCancelFullScreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
} else {
console.error('Unable to find a fullscreen exit method.');
}
} else {
if (this.player.requestFullscreen) {
this.player.requestFullscreen();
} // standard
else if (this.player.webkitRequestFullscreen) {
this.player.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
} else if (this.player.mozRequestFullScreen) {
this.player.mozRequestFullScreen();
} else if (this.player.msRequestFullscreen) {
this.player.msRequestFullscreen();
} else {
console.error('Unable to find a fullscreen request method');
}
}
}
toggleFullScreenHelper() {
this.player.classList.toggle('isFullScreen');
this.isFullScreen = !this.isFullScreen;
}
togglePlay() {
const action = this.video.paused ? 'play' : 'pause';
this.video[action]();
this.playerController.style.bottom = '0px';
this.changePlayIcon();
this.showHideControlls();
}
showVideoTime() {
const {
currentTime,
duration
} = cvp.video;
const pct = (currentTime / duration) * 100;
cvp.progressCurr.style.width = pct + '%';
cvp.timeCurr.innerText = cvp.handleVideoTime(currentTime.toFixed(0));
cvp.timeWhole.innerText = cvp.handleVideoTime(duration);
}
scrub(e) {
console.log(e);
console.log('e', e, 'this', this);
let seconds = (e.offsetX / this.progress.offsetWidth) * this.video.duration;
this.video.currentTime = seconds;
}
loadVideo() {
// il metodo รจ da applciare assieme a un loader e ad altre gestioni degli eventi video
// esempi di loader: https://www.w3schools.com/howto/howto_css_loader.asp
// metodi video: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadeddata_event
// per i tooltips: https://www.w3schools.com/howto/howto_css_tooltip.asp
console.log(this.video.src);
}
init() {
// Hook up the Event Listeners
// toggle Play
this.video.addEventListener('click', this.togglePlay.bind(this));
this.miniPlayBtn.addEventListener('click', this.togglePlay.bind(this));
this.bigPlayBtn.addEventListener('click', this.togglePlay.bind(this));
this.video.addEventListener('loadeddata', this.loadVideo.bind(this));
// bigPlayBtn show/hide handler
this.video.addEventListener('click', this.bigPlayBtnHandler);
this.bigPlayBtn.addEventListener('click', this.bigPlayBtnHandler);
this.miniPlayBtn.addEventListener('click', this.bigPlayBtnHandler);
// time update
this.video.addEventListener('timeupdate', this.showVideoTime);
// progress bar events
this.progress.addEventListener('click', this.scrub.bind(this));
this.progress.addEventListener('mousemove', (e) => this.isMousedown && this.scrub(e));
this.progress.addEventListener('mousedown', this.setIsMouseDown);
this.progress.addEventListener('mouseup', this.setIsMouseDown);
// fullscreen
this.fullscreen.addEventListener('click', this.toggleFullScreen.bind(this));
// volume controllers
this.volumeBtnController.addEventListener('click', this.btnVolumeController);
this.volumeRangeController.addEventListener('change', this.rangeVolumeController);
this.volumeRangeController.addEventListener('mousemove', this.rangeVolumeController);
// cross-browser fullscreen hanlder
const browsersFullScreen = [
'fullscreenchange',
'mozfullscreenchange',
'webkitfullscreenchange',
'msfullscreenchange',
];
browsersFullScreen.forEach(browser => document.addEventListener(browser, this.toggleFullScreenHelper.bind(this)));
// player show/hide controlls on mousemove event
this.player.addEventListener('mousemove', this.hideWhenFullScreen.bind(this));
}
}
const initializeCVP = {
P: '.CVP',
V: '.video',
PC: '.playerController',
BPB: '.bigPlayBtn',
MPB: '.miniPlayBtn',
VRC: '.volumeRC',
VBC: '.volumeBC',
FS: '.fullscreen',
PROG: '.progress',
PROGC: '.progressCurr',
TW: '.timeWhole',
TC: '.timeCurr',
};
let cvp = new CVP(initializeCVP);
cvp.init();
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
display: flex;
flex-direction: column;
justify-content: center;
align-content: center;
align-items: center;
height: 100vh;
background-image: linear-gradient(to top, #dad4ec 0%, #dad4ec 1%, #f3e7e9 100%);
/* background-image: linear-gradient(-20deg, #f794a4 0%, #fdd6bd 100%); */
}
[hidden] {
display: none !important;
}
.player {
display: flex;
justify-content: center;
border-radius: 10px;
width: 700px;
max-height: 100%;
position: relative;
font-size: 16px;
overflow: hidden;
text-align: center;
background: #000;
color: #fff;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.4), 0 10px 20px -3px rgba(0, 0, 0, 0.2);
margin-bottom: 5vh;
}
/* This css is only applied when fullscreen is active. */
.player.isFullScreen {
max-width: none;
max-height: none;
width: 100%;
height: 100%;
background: #000;
}
.player.isFullScreen video {
width: 100%;
height: auto;
}
.video {
max-width: 100%;
max-height: 100%;
-o-object-fit: fill;
object-fit: fill;
/* These are the actual dimensions of the video. Avoid the awkward element resize after the video loads. */
/* width: 640px;
height: 358px; */
}
.playerController {
display: flex;
align-items: center;
justify-content: space-evenly;
width: 98%;
max-width: 1000px;
/* limiting in fullscreen mode */
position: absolute;
background: rgba(51, 51, 51, 0.8);
color: #fff;
border-radius: 10px;
margin-bottom: 1%;
z-index: 2147483649;
transform: translateY(150px);
transition: .2s;
bottom: -50px;
/* 0 to show in preview */
}
.playerController * {
color: rgba(255, 255, 255, 0.9);
}
.playerController button {
background: none;
border: 0;
outline: 0;
text-align: center;
cursor: pointer;
}
.icon {
font-size: 16px;
font-family: 'Font Awesome 5 Free';
font-weight: 900;
}
.player:hover .playerController,
.player.paused .playerController {
transform: translateY(0);
}
.player__btn {
max-width: 50px;
}
.play::before {
content: '\f04b';
}
.pause::before {
content: '\f04c';
}
.progress {
/* position: relative; */
width: 50%;
cursor: pointer;
user-select: none;
height: 10px;
/* or 15px */
background: rgba(160, 154, 154, 0.8);
overflow: hidden;
border-radius: 50px;
}
.progressCurr {
height: 100%;
width: 0;
background: rgba(255, 255, 255, 0.9);
/* flex: 0;
flex-basis: 0%; */
/* old styles yellow progressCurr bg flex-basic insted of width*/
}
.fullscreen::before {
font-family: 'FontAwesome';
content: '\f065';
font-size: 16px
}
.player.isFullScreen .playerController .fullscreen::before {
content: '\f066';
}
.timeCurr,
.timeWhole {
font-size: 14px;
user-select: none;
}
.volumeContainer {
display: flex;
}
.volumeBC {
cursor: pointer;
padding-right: 5px;
}
.volumeRC {
width: 10px;
height: 30px;
}
.max::before {
content: '\f028'
}
.mute::before {
content: '\f6a9'
}
.bigPlayBtn {
font-size: 80px;
position: absolute;
top: 50%;
left: 50%;
cursor: pointer;
box-sizing: border-box;
transform: translate(-50%, -50%);
transition: .5s;
}
.bigPlayBtn::before {
content: '\f04b';
}
.HideBigPlayBtn {
cursor: default;
opacity: 0;
transform: translate(-50%, -50%) scale(1.2);
}
<div class='player paused CVP'>
<video class='video' src='http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4' width='700' height='350' poster='https://i.ytimg.com/vi/A1sVB8XBNDo/maxresdefault.jpg'>
</video>
<div class='bigPlayBtn icon'></div>
<div class='playerController'>
<button class='player__btn miniPlayBtn icon' title='Play/Pause'></button>
<div class='timeCurr'>00:00</div>
<div class='progress'>
<span id='buffered'>
<div class='progressCurr'></div>
</span>
</div>
<div class='timeWhole'>--:--</div>
<div class='volumeContainer'>
<button class='player__btn max volumeBC icon'></button>
<input type='range' name='volume' class='volume volumeRC' min='0' max='1' step='0.01' value='1' title='Volume' />
</div>
<button class='player__btn fullscreen icon' title='Full Screen'></button>
</div>
</div>
<div class='player paused CVP'>
<video class='video' src='http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4' width='700' height='350' poster='https://i.ytimg.com/vi/A1sVB8XBNDo/maxresdefault.jpg'>
</video>
<div class='bigPlayBtn icon'></div>
<div class='playerController'>
<button class='player__btn miniPlayBtn icon' title='Play/Pause'></button>
<div class='timeCurr'>00:00</div>
<div class='progress'>
<span id='buffered'>
<div class='progressCurr'></div>
</span>
</div>
<div class='timeWhole'>--:--</div>
<div class='volumeContainer'>
<button class='player__btn max volumeBC icon'></button>
<input type='range' name='volume' class='volume volumeRC' min='0' max='1' step='0.01' value='1' title='Volume' />
</div>
<button class='player__btn fullscreen icon' title='Full Screen'></button>
</div>
</div>
Sorry if I'm disturbing you for something that will probably appear you very simple, but I can't find a way to solve the problem on my own.
And, maybe, the title is not appropriated...
Well, I have a class with a constructor that works with HTML tag VIDEO, named CVP. It creates methods to act on video controls. The initial method consist in hooking the event listeners to HTML controls'classes.
Anyway, with a single VIDEO tag in the HTML code, it works correctly, but I don't know how to iterate the class on any VIDEO when there are many of them. This is an examples of the VIDEO tag:
<div class='player paused CVP'>
<video class='video' src="../../video/pietro.mp4">
</video>
etc...
This is the beginnig of the JS CLASS:
class CVP {
constructor (obj) {
const { P, V, PC, BPB, MPB, VRC, VBC, FS, PROG, PROGC, TW, TC } = obj;
this.player = document.querySelector(P);
this.video = document.querySelector(V);
etc..
The class ends this way:
(many other methods...)
init () {
// Hook up the Event Listeners
// toggle Play
this.video.addEventListener('click', this.togglePlay.bind(this));
this.miniPlayBtn.addEventListener('click', this.togglePlay.bind(this));
this.bigPlayBtn.addEventListener('click', this.togglePlay.bind(this));
etc...
}
}
Then, there's a inizialization's variable:
const initializeCVP = {
P: '.CVP',
V: '.video',
PC: '.playerController',
etc...
};
To add a single VIDEO tag:
const cvp = new CVP(initializeCVP);
cvp.init();
In this case it works perfectly. But, How can I assign the JS class to many VIDEO tags?
I tried this way, but it doesn't work:
window.onload = function () {
const CVPs = document.querySelectorAll('.CVP');
CVPs.forEach((cvp) => {
cvp = new CVP(initializeCVP);
cvp.init();
});
};
Is it clear? I hope so. I'm quite a beginner, so plase, don't complain...
Change your constructor so it takes elements rather than selectors. The caller can call document.querySelector() or document.querySelectorAll() when creating the initialization object.
class CVP {
constructor (obj) {
const { P, V, PC, BPB, MPB, VRC, VBC, FS, PROG, PROGC, TW, TC } = obj;
this.player = P;
this.video = V;
etc..
}
}
window.onload = function() {
const players = document.querySelectorAll('.player');
players.forEach(player => {
const initializeCVP = {
P: player,
V: player.querySelector('.video'),
PC: player.querySelector('.playerController'),
BPB: player.querySelector('.bigPlayBtn'),
MPB: player.querySelector('.miniPlayBtn'),
VRC: player.querySelector('.volumeRC'),
VBC: player.querySelector('.volumeBC'),
FS: player.querySelector('.fullscreen'),
PROG: player.querySelector('.progress'),
PROGC: player.querySelector('.progressCurr'),
TW: player.querySelector('.timeWhole'),
TC: player.querySelector('.timeCurr'),
};
const cvp = new CVP(initializeCVP);
cvp.init();
});
};
I found a way to fix what #Barman and #RandyCasburn pointed out regarding the single 'cvp' istance connected to class methods.
First, I applied #Barman suggestion, so to iterate all players has in the previous answer.
Then, I put a global array to collect the single 'cvp', with a couple of other new variables:
let cvpItems = [];
let cvpItem;
let currentCVP;
Then, I pushed all player istances in the HTML, applying to anyone an addEventListener. The window.onload code is changed in this way:
window.onload = function () {
const players = document.querySelectorAll('.player');
players.forEach(player => {
const initializeCVP = {
P: player,
V: player.querySelector('.video'),
PC: player.querySelector('.playerController'),
BPB: player.querySelector('.bigPlayBtn'),
MPB: player.querySelector('.miniPlayBtn'),
VRC: player.querySelector('.volumeRC'),
VBC: player.querySelector('.volumeBC'),
FS: player.querySelector('.fullscreen'),
PROG: player.querySelector('.progress'),
PROGC: player.querySelector('.progressCurr'),
TW: player.querySelector('.timeWhole'),
TC: player.querySelector('.timeCurr'),
};
cvpItem = new CVP(initializeCVP);
cvpItems.push(cvpItem);
cvpItem.init();
player.addEventListener('click', currentItem, false);
});
};
With the currentItem function I find the current player iteration using the title attribute in the HTML video tags:
let currentItem = function () {
for (var i = 0; i < cvpItems.length; i++) {
if (cvpItems[i].video.title == this.querySelectorAll('.video')[0].title) {
currentCVP = i;
break;
}
}
};
So, now I can identify the specific player in the JS class. For examples, the previous rangeVolumeController method changes from:
rangeVolumeController () {
cvp.video[this.name] = this.value;
if (cvp.volumeRangeController.value == 0) {
cvp.video.muted = true;
CVP.changeIcon(cvp.volumeBtnController, 'max', 'mute');
} else {
cvp.video.muted = false;
CVP.changeIcon(cvp.volumeBtnController, 'mute', 'max');
}
}
to
rangeVolumeController () {
console.log(CVP, this, event.target);
cvpItems[currentCVP].video[this.name] = this.value;
if (cvpItems[currentCVP].volumeRangeController.value == 0) {
cvpItems[currentCVP].video.muted = true;
CVP.changeIcon(cvpItems[currentCVP].volumeBtnController, 'max', 'mute');
} else {
cvpItems[currentCVP].video.muted = false;
CVP.changeIcon(cvpItems[currentCVP].volumeBtnController, 'mute', 'max');
}
}
It works! But, I know, it's not an elegant way for coding. If someone wants to propose a better or more "elegant" solution, I'll be grateful.

my function doesn't retrigger animation when the loop starts. Traffic light flashing signal works once

I am doing this loop to switch between the traffic lights but want to make the animation yellow flashing. It works only once? The variables for each loop are all local.
I commented out some code but it should work fine.
document.getElementById('stopButton').onclick = illuminateRed;
const yellow = document.getElementById('slowButton')
document.getElementById('goButton').onclick = illuminateGreen
yellow.onclick = illuminateYellow;
function illuminateYellow() {
clearLights();
document.getElementById('slowLight').style.backgroundColor = "#EFB700";
document.getElementById('slowLight').style.animation = " blinker 500ms linear";
document.getElementById("slowLight").style.animationDuration = "3s"
var timeLeft = 3;
var elem = document.getElementById('flashing');
var timerId = setInterval(countdown, 600);
function countdown() {
if (timeLeft == -1) {
clearTimeout(timerId);
illuminateRed();
} else {
//elem.innerHTML = timeLeft ; time left display
timeLeft--;
}
}
}
function clearLights() {
document.getElementById('stopLight').style.backgroundColor = "black";
document.getElementById('slowLight').style.backgroundColor = "black";
document.getElementById('goLight').style.backgroundColor = "black";
}
If you really need to use animation, you just need add 3 before linear, than you need reset it when timeLeft is reached.
But, since you already using setInterval you can simply change it's color from there:
document.getElementById('stopButton').onclick = illuminateRed;
const yellow = document.getElementById('slowButton')
document.getElementById('goButton').onclick = illuminateGreen
yellow.onclick = illuminateYellow;
clearLights();
var timerId; //should be global variable
function illuminateYellow() {
clearLights();
// document.getElementById('slowLight').style.backgroundColor = "#EFB700";
// document.getElementById('slowLight').style.animation = " blinker 600ms 6 linear alternate";
// document.getElementById("slowLight").style.animationDuration = "0.6s"
var timeLeft = 4;
var elem = document.getElementById('flashing');
timerId = setInterval(countdown, 600);
countdown();
function countdown() {
document.getElementById('slowLight').style.backgroundColor = timeLeft % 2 ? "black" : "#EFB700";
if (timeLeft == -1) {
// clearInterval(timerId);
illuminateRed();
// document.getElementById('slowLight').style.animation = "";
} else {
//elem.innerHTML = timeLeft ; time left display
timeLeft--;
}
}
}
function illuminateRed()
{
clearLights();
document.getElementById('stopLight').style.backgroundColor = "";
}
function illuminateGreen()
{
clearLights();
document.getElementById('goLight').style.backgroundColor = "";
}
function clearLights() {
clearInterval(timerId);
document.getElementById('stopLight').style.backgroundColor = "black";
document.getElementById('slowLight').style.backgroundColor = "black";
document.getElementById('goLight').style.backgroundColor = "black";
}
.lights
{
background-color: black;
width: fit-content;
padding: 0.5em;
border-radius: 0.5em;
margin-top: 1em;
}
.lights > div
{
border-radius: 50%;
width: 2em;
height: 2em;
margin: auto;
background-size: 4px 4px;
background-image: radial-gradient(black, transparent, black, transparent);
}
#stopLight
{
background-color: red;
}
#slowLight,
#flashing
{
background-color: #EFB700;
}
#goLight
{
background-color: green;
}
#keyframes blinker {
from {
background-color: black;
}
to {
background-color: #EFB700;
}
}
<button id="goButton">go</button>
<button id="slowButton">slow</button>
<button id="stopButton">stop</button>
<div class="lights">
<div id="stopLight"></div>
<div id="slowLight"></div>
<div id="goLight"></div>
</div>
Also note, that when you use setInterval you need use clearInterval not clearTimeout.
And as a side note, you should avoid using inline styles, it's better use attributes/class of elements instead:
const elLights = document.getElementById("lights");
document.getElementById('stopButton').onclick = illuminateRed;
const elYellow = document.getElementById('slowButton')
document.getElementById('goButton').onclick = illuminateGreen
elYellow.onclick = illuminateYellow;
clearLights();
var timerId; //should be global variable
function illuminateYellow(e) {
if (e) clearTimeout(auto.timer); //stop auto
clearLights();
elLights.setAttribute("lit", "slowLight");
var timeLeft = 4;
var elem = document.getElementById('flashing');
timerId = setInterval(countdown, 600);
countdown();
function countdown() {
if (timeLeft == -1) {
illuminateRed();
} else {
//elem.innerHTML = timeLeft; // time left display
timeLeft--;
}
}
}
function illuminateRed(e)
{
if (e) clearTimeout(auto.timer); //stop auto
clearLights();
elLights.setAttribute("lit", "stopLight");
}
function illuminateGreen(e)
{
if (e) clearTimeout(auto.timer); //stop auto
clearLights();
elLights.setAttribute("lit", "goLight");
}
function clearLights() {
clearInterval(timerId);
elLights.removeAttribute("lit");
}
function auto()
{
clearTimeout(auto.timer); //stop auto
clearLights();
!function loop()
{
const min = elLights.getAttribute("lit") == "goLight" ? 6 : 3, //minimum seconds
max = 3; //max seconds
if (min > 3)
illuminateYellow();
else
illuminateGreen();
auto.timer = setTimeout(loop, 1000 * (Math.random() * max + min)); //set 3 - 9 sec to repeat this loop
}()
}
#lights
{
background-color: black;
width: fit-content;
padding: 0.5em;
border-radius: 0.5em;
margin-top: 1em;
}
#lights > div
{
border-radius: 50%;
width: 2em;
height: 2em;
margin: auto;
background-size: 4px 4px;
background-image: radial-gradient(black, transparent, black, transparent);
}
#lights[lit="stopLight"] #stopLight
{
background-color: red;
}
#lights[lit="slowLight"] #slowLight
{
background-color: #EFB700;
animation: blinker 600ms infinite linear alternate;
}
#lights[lit="slowLight"] #slowLight.fast
{
animation: blinker-fast 600ms infinite step-end alternate;
}
#lights[lit="goLight"] #goLight
{
background-color: green;
}
#keyframes blinker {
0% {
background-color: transparent;
}
100% {
background-color: #EFB700;
}
}
#keyframes blinker-fast {
0% {
background-color: transparent;
}
50% {
background-color: #EFB700;
}
}
<button id="goButton">go</button>
<button id="slowButton">slow</button>
<button id="stopButton">stop</button>
<button onclick="auto()">auto</button>
<label><input type="checkbox" oninput="document.getElementById('slowLight').classList.toggle('fast')">fast blinker</label>
<div id="lights">
<div id="stopLight"></div>
<div id="slowLight"></div>
<div id="goLight"></div>
</div>
<span id="flashing"></span>

Fade in text letter by letter based on scroll

I'm having problems with a functionality that I have to implement, I'm looking to do this (check the second section) -> https://www.jacquemus.com/fr/simon
The letters need to fade in and fade out, based on the scroll, this is what I got so far
<div class="ml3">
<h1>THIS IS MY TEXT THAT IT'S GOING TO SHOW IN SCROLL</h1>
</div>
:root {
--percentage: 0;
}
body {
background-color: #000;
margin: 0;
height: 120vh;
}
.ml3 {
position: sticky;
top: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
span {
font-family: Helvetica;
margin: 0;
padding: 0;
font-size: 48px;
color: #fff;
letter-spacing: -0.3px;
}
.ml3 span{
opacity: var(--percentage);
}
var textWrapper = document.querySelector('.ml3');
textWrapper.innerHTML = textWrapper.textContent.replace(/\S/g, "<span class='letter'>$&</span>");
var letter = document.querySelectorAll('.letter');
var i = 0;
var currentID = 0;
var slideCount = letter.length;
document.addEventListener('scroll', (e) => {
let scrolled = document.documentElement.scrollTop / (document.documentElement.scrollHeight - document.documentElement.clientHeight);
var nextID = currentID + 1;
if(nextID < slideCount){
letter[nextID].style.setProperty('--percentage', `${scrolled / 1}` * nextID);
}
currentID = nextID;
});
https://codepen.io/federicomartin/pen/eYdBbQm
As you can see, it's no near what I want, but I really don't know how to do it, if someone could help me, would be awesome! Thanks!
I like that effect a lot!! Thank for submitting that question! ;)
So here is a something to help you continue on this challenge.
In the scroll handler, I replaced:
var nextID = currentID + 1;
if(nextID < slideCount){
letter[nextID].style.setProperty('--percentage', `${scrolled / 1}` * nextID);
}
currentID = nextID;
with:
letter.forEach(function (l, i) {
if (i / letter.length < scrolled) {
l.style.setProperty("--percentage", 1);
}else{
l.style.setProperty("--percentage", 0);
}
});
It compares the scrolled percentage you calculated with the letter index "percentage" in the letter collection to set it's opacity to 0 or 1.
I would then adjust the scrollHeight of the body with the real text to reveal... Below, I used height: 600vh; and may be a bit too much. ;)
var textWrapper = document.querySelector(".ml3");
textWrapper.innerHTML = textWrapper.textContent.replace(
/\S/g,
"<span class='letter'>$&</span>"
);
var letter = document.querySelectorAll(".letter");
var i = 0;
var currentID = 0;
var slideCount = letter.length;
document.addEventListener("scroll", (e) => {
let scrolled =
document.documentElement.scrollTop /
(document.documentElement.scrollHeight -
document.documentElement.clientHeight);
// var nextID = currentID + 1;
// if (nextID < slideCount) {
// letter[nextID].style.setProperty(
// "--percentage",
// `${scrolled / 1}` * nextID
// );
// }
// currentID = nextID;
letter.forEach(function (l, i) {
// console.log("====",i / letter.length, i, letter.length)
if (i / letter.length < scrolled) {
l.style.setProperty("--percentage", 1);
} else {
l.style.setProperty("--percentage", 0);
}
});
});
:root {
--percentage: 0;
}
body {
background-color: #000;
margin: 0;
height: 1120vh;
}
.ml3 {
position: sticky;
top: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
span {
font-family: Helvetica;
margin: 0;
padding: 0;
font-size: 48px;
color: #fff;
letter-spacing: -0.3px;
}
.ml3 span {
opacity: var(--percentage);
}
<div class="ml3">
<h1>THIS IS MY TEXT THAT IT'S GOING TO SHOW IN SCROLL</h1>
</div>
CodePen

Eventhandler is disabled for longer than expected

I'm making a simple full viewport scroller. You can change sections by triggering wheel event.
To prevent the eventhandler from firing many times in row and skipping pages, I've added a timer, calculating the difference between date.now() stored in variable and date.now() inside the eventHandler. This happens mostly if you spam scrolling, it makes you have to wait about 3 seconds to scroll again instead of 200ms. How to prevent this from happening?
document.ready = (fn) => {
if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading"){
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
document.ready(() => {
const SECTIONS = document.querySelectorAll('.section');
let current;
let onWheelTimeout = 'poop';
let time = Date.now()
// initialize first section as active
_.first(SECTIONS).classList.add('active');
document.addEventListener('wheel', onWheel)
function goSectionUp() {
const current = document.querySelector('.active');
current.classList.remove('active');
if(current.previousElementSibling) {
current.previousElementSibling.classList.add('active');
} else {
_.last(SECTIONS).classList.add('active');
}
}
function goSectionDown() {
const current = document.querySelector('.active');
current.classList.remove('active');
if(current.nextElementSibling) {
current.nextElementSibling.classList.add('active');
} else {
_.first(SECTIONS).classList.add('active');
}
}
function onWheel(e) {
const now = Date.now()
const diff = now - time;
time = now;
if(diff > 200) {
if(e.deltaY < 0) {
onScroll('up')
} else {
onScroll('down')
}
}
}
function onScroll(direction) {
if(direction === 'up') {
goSectionUp()
} else {
goSectionDown()
}
};
});
html {
box-sizing: border-box;
overflow: hidden;
width: 100%; height: 100%;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
padding: 0; margin: 0;
overflow: hidden;
height: 100%; width: 100%;
position: relative;
}
#page {
width: 100%; height: 100%;
transition: all 1s ease;
transform: none !important;
}
.section {
height: 100vh; width: 100%;
opacity: 0;
visibility: hidden;
position: absolute;
top: 0;
left: 0;
transition: all .7s ease-in-out;
}
.section:nth-of-type(1) {
background-color: red;
}
.section:nth-of-type(2) {
background-color: aquamarine;
}
.section:nth-of-type(3) {
background-color: blueviolet;
}
.section:nth-of-type(4) {}
.active {
opacity: 1; visibility: visible;
}
#button {
position: sticky;
top: 0; left: 100px;
z-index: 1000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
<div id="page">
<div class="section">one</div>
<div class="section">two</div>
<div class="section">three</div>
<div class="section">four</div>
</div>
It seems like what you want is a debounce function. I'd recommend using this one, by David Walsh:
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
Usage
var myScrollFunction = debounce(function() {
// All the taxing stuff you do
}, 250);
document.addEventListener('wheel', myScrollFunction);
To answer why your code doesn't work as expected: The mouse wheel produces a series of continuous events while it is scrolling, so your time diff is constantly < 200. Here's an example of it working "properly" (though the best answer is still a true debounce function as stated above).
JSBin example
https://jsbin.com/cuqacideto/edit?html,console,output

Categories

Resources