How do I create an animated number counter using Alpine.js? - javascript

I want to create a animated number counter using alpine js something exactly like this if there is a plugins or something can help me please told me.
Code :
<div id="counter">Counter: <b counter="0">0</b></div>
function update_users_count() {
$('#counter b').animate({
counter: 25000
}, {
duration: 6000,
easing: 'swing',
step: function(now) {
$(this).text(Math.ceil(now));
},
complete: update_users_count
});
};
update_users_count();
Alpine.JS
Error code is : Uncaught ReferenceError: counterA is not defined
<script>
function counterExample() {
return {
counterA: 0,
target: '+100',
time: 2000,
init() {
const start = counterA;
const steps = time / (target - start);
const handle = setInterval(() => {
if (counterA < target) {
counterA += Math.round((target - start) / steps);
} else {
clearInterval(handle);
counterA = target;
}
}, time / steps);
}
}
}
</script>
Example of what I want to do :

<!DOCTYPE html>
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.x.x/dist/alpine.min.js" defer></script><html>
<body>
<div x-data="{ current: 0, target: 1000, time: 300}" x-init="() => {
start = current;
const interval = Math.max(time / (target - start), 5);
const step = (target - start) / (time / interval);
const handle = setInterval(() => {
if(current < target)
current += step
else {
clearInterval(handle);
current = target
}
}, interval)
}">
<div class="card"x-text="Math.round(current)">
</div>
</body>
</html>

Related

Pause/Continue in Pomodoro Timer project

I am building a pomodoro tracker in order to practice a little bit of JavaScript. It's been a while since I started this project. After this particulary problem which is implement a pause/continue functionality I abandoned the project. I really got stucked. I know programming is not easy and I will be facing many problems in the future but I really can't figure out how to solve this task. I am feeling stupid
Here is the JavaScript code:
// General Variables
let display = document.querySelector('.display');
// is the timer paused?
// let isPaused = true;
// let count = 0;
//const playPomodoro = document.querySelector('.play');
const pause = document.querySelector('.pause');
const resume = document.querySelector('.resume');
const stopPomodoro = document.querySelector('.stop');
const pomodoro = document.querySelector('#pomodoro');
const shortBreak = document.querySelector('#shortbreak');
const longBreak = document.querySelector('#longbreak')
const audioBeep = document.querySelector('#audioBeep');
const twentyFiveMinutes = 60 * 25;
const fiveMinutes = 60 * 5;
const thirtyMinutes = 60 * 30;
// Start Pomodoro timer 25 minutes
pomodoro.addEventListener('click', () => {
startTimer(twentyFiveMinutes, display);
stopClick(shortBreak, longBreak);
pause.style.display = 'block';
stopPomodoro.style.display = 'block';
});
// Start Pomodoro short break
shortBreak.addEventListener('click', () => {
startTimer(fiveMinutes, display);
stopClick(pomodoro, longBreak);
pause.style.display = 'block';
stopPomodoro.style.display = 'block';
});
// Start Pomodoro Long break
longBreak.addEventListener('click', () => {
startTimer(thirtyMinutes, display);
stopClick(pomodoro, shortBreak);
pause.style.display = 'block';
stopPomodoro.style.display = 'block';
});
// Stopping Clicks Events
function stopClick(btn1, btn2) {
btn1.classList.add('avoid-clicks');
btn2.classList.add('avoid-clicks');
}
// Remove .avoid-clicks class
function removeAvoidClick(btn1, btn2, btn3) {
btn1.classList.remove('avoid-clicks');
btn2.classList.remove('avoid-clicks');
btn3.classList.remove('avoid-clicks');
}
// main start timer function
function startTimer(duration, display) {
let timer = duration, min, sec;
let countingDown = setInterval(function() {
min = parseInt(timer / 60, 10);
sec = parseInt(timer % 60, 10);
min = min < 10 ? "0" + min : min;
sec = sec < 10 ? "0" + sec : sec;
display.innerHTML = min + ":" + sec;
if (--timer < 0) {
timer = duration;
}
// stops the counting variable when it hits zero
if (timer == 0) {
clearInterval(countingDown);
display.innerHTML = "00:00";
audioBeep.play();
removeAvoidClick(pomodoro,shortBreak,longBreak);
}
// Pause the clock
pause.addEventListener('click', () => {
});
// Stop the counter and set it to 00:00 when the user clicks the stop button
stopPomodoro.addEventListener('click', () => {
clearInterval(countingDown);
display.innerHTML = "00:00";
removeAvoidClick(pomodoro,shortBreak,longBreak);
});
}, 1000);
}
I will store the timer status in an object, then you activate a timer that decreses the current time left and call UI update.
let timer = {
timeLeft: 0,
running: 0,
};
function startTimer(duration) {
timer.timeLeft = duration;
if (!timer.run) {
timer.run = true;
run();
}
}
function run() {
if (timer.run) {
timer.timeLeft -= 1;
setTimeout(run, 1000)
}
}
function pauseTimer() {
timer.run = false;
}
function resumeTimer() {
if (!timer.run) {
timer.run = true;
run();
}
update();
}
function update() {
// everything you need to update in the UI from the timer object
}

How to stop my timer if all inputs are filled in

I have a method with a form, how much input fields there are varies. The form has a timer. On input the timer will start running, how can I detect that all the forms are filled in. to stop the timer once all inputs are filled in ?
self.tm.start() starts the timer right after the .on('input', function()
inputField(i)
{
let self = this;
let textarea = self.JSON_DATA.main_object.exercises[i].textarea;
let div = $("<div/>", {
class: "form-group row"
});
let word;
for (let a = 0; a < textarea.length; a++)
{
if (textarea[a].checked == "true")
{
word = $("<input/>", {
class: 'form-control col-2 my-1 mx-1'
}).on('input', function(){
//Timer start
self.tm.start()
if ($(this).val() == textarea[a].word)
{
$(this).nextAll('input').first().focus();
$(this).addClass('btn-success').attr("disabled", true)
}});
} else {
word = $("<span/>", {
class: 'col-form-label mx-1'
}).html(textarea[a].word);
}
div.append(word);
}
return div;
}
Timer
/**
* var t = new TIMER([ID]/[CLASS]);
* (object) target is the element on which the timer will be displayed
*/
function TIMER(target) {
var self = this;
self.timer = false;
self.sec = 0;
/**
* Initiates TIMER on target-element(s)
*/
self.init = function () {
var style = $('<style></style>').attr('type', 'text/css').html(target + ' .inline{display: inline-block}');
$('head').append(style);
self.sec = 0;
$(target).html('');
var minutes = $('<div/>', {
id: 'minutes',
class: 'inline'
}).css({
'padding-right': '6px'
}).html(self.pad(parseInt(self.sec / 60, 10)));
var seconds = $('<div/>', {
id: 'seconds',
class: 'inline'
}).css({
'padding-left': '6px'
}).html(self.pad(self.sec % 60));
$(target).append(minutes, ':', seconds).css('font-size', '2.0em');
};
/**
* (int) val
* Puts a leading 0 in front if val < 10
*/
self.pad = function (val) {
return val > 9 ? val : "0" + val;
};
/**
* Start TIMER with interval of 1000ms
*/
self.start = function () {
if (self.timer === false) {
self.timer = setInterval(
function () {
$("#seconds").html(self.pad(++self.sec % 60));
$("#minutes").html(self.pad(parseInt(self.sec / 60, 10)));
},
1000);
}
};
/**
* Stops TIMER
*/
self.stop = function () {
clearInterval(self.timer);
};
/**
* Stops TIMER and reinitiates it
*/
self.reset = function () {
self.stop();
self.init();
};
/**
* returns (string) with current min and sec
*/
self.getTime = function () {
return self.pad(parseInt(self.sec / 60, 10)) + ':' + self.pad(self.sec % 60);
}
self.init();
}
self.tm.stop() will stop the timer, but how do I implent it.
Alright, fixed my own problem.
I made 2 class variables. I call these in the constructor
this.timeStop = 0;
this.timeStop1 = 0;
Then this is my input function
inputField(i)
{
let self = this;
let textarea = self.JSON_DATA.main_object.exercises[i].textarea;
let div = $("<div/>", {
class: "form-group row"
});
let word;
for (let a = 0; a < textarea.length; a++)
{
if (textarea[a].checked == "true")
{
word = $("<input/>", {
class: 'form-control col-2 my-1 mx-1',
id:'answerField'
}).on('input', function() {
self.tm.start() // Timer start
if ($(this).val() == textarea[a].word){
self.timeStop++; // COUNTING VARIABLE 1
$(this).nextAll('input').first().focus();
$(this).addClass('btn-success').attr("disabled", true);
self.check_answers();
}});
this.timeStop1++; // COUNTING VARIABLE 2
self.checkAnswers(); // CALLING METHOD TO SEE IF VARIABLES ARE EQUAL TO EACHOTHER
} else {
word = $("<span/>", {
class: 'col-form-label mx-1'
}).html(textarea[a].word);
}
div.append(word);
}
return div;
}
I count both of the previous variables. One in the if statement for each input field. The other in the for loop but after the if statement.
Then I have another method that checks if these are equal to eachother and stops the timer.
checkAnswers(){
let self = this;
if (this.timeStop == this.timeStop1) {
self.tm.stop();
}
}

Time intervals with animejs

I am working on an animation to show this kind of effect using animejs, I have been able to show push the text after 1000ms each and change the opercity to 0.5. But its not in sync like the one in this gif what can I do to add make the effect effective for delay and duration.
My html code with the text all the text, which is shown base on the loop.
<div class="onboarding-content">
<div [class]="bottomStyle">
<div class="onboardtxt one">
<p [class]="firstAnimation">Hello, Meet Mat</p>
</div>
<div class="onboardtxt two">
<p [class]="secondAnimation">Some text</p>
</div>
<div class="onboardtxt three">
<p [class]="thirdAnimation">Some text two </p>
</div>
<div class="onboardtxt four">
<p [class]="forthAnimation">Some text three</p>
</div>
</div>
</div>
My js file
// Animation
count: number;
display: string = "display";
firstAnimation: string = "first";
secondAnimation: string = "second";
thirdAnimation: string = "third";
forthAnimation: string = "forth"
truthy: any;
bottomStyle: string = "bottom"
constructor(public navCtrl: NavController) {
var messages = ['1','2','3','4','5']
var count = 0;
var that = this
var timer = function () {
if (count === 1) {
that.firstAnimation = "first-para-animation";
} else if (count === 2) {
that.firstAnimation = "first-second-fire-animation";
that.secondAnimation = "second-para-animation";
} else if (count === 3) {
that.secondAnimation = "second-second-fire-animation";
that.thirdAnimation = "third-para-animation";
} else if (count === 4) {
that.thirdAnimation = "third-second-fire-animation";
that.forthAnimation = "forth-para-animation";
} else if (count === 5) {
that.truthy = true;
// that.bottomStyle = "no-margin"
}
// check the length of count before pushing
if (count < messages.length) {
count += 1
} else {
clearInterval(interval)
}
}
// // setting the interval after how long it should call the timer function
var interval = setInterval(timer, 1000)
}
ionViewDidLoad() {
console.log('ionViewDidLoad home page');
anime.timeline({ loop: false })
.add({
targets: 'div.bottomStyle',
translateY: [
{ value: 10, delay: 0 },
// { value: 0, duration: 400 },
],
easing: "easeOutQuart",
duration: 0,
// delay: function (el, i, l) {
// return 1000 * i;
// },
// delay: function(el, i) {
// return 800 * i;
// },
color: '#fff'
})
anime.timeline({ loop: false })
.add({
targets: ['.onboardtxt.one',],
duration:100,
// delay:1200,
opacity: 0.5
})
anime.timeline({ loop: false })
.add({
targets: ['.onboardtxt.two',],
// delay:1300,
delay:4400,
opacity: 0.5,
// opacity: 0.5,
})
anime.timeline({ loop: false })
.add({
targets: ['.onboardtxt.three',],
delay:5500,
opacity: 0.5,
// delay:500
});
}
Have you looked at the documentation since then? It's quite good: https://animejs.com/documentation/#timelineBasics
Based on your code, you need to add to only ONE timeline, so that they are synced (the pushed texts happen sequentially).
Example derived from "Timeline Basics":
// Create a timeline with default parameters
var tl = anime.timeline({
easing: 'easeOutExpo',
duration: 750
});
// Add children
tl
.add({
targets: '.onboardtxt.one',
translateX: 250,
})
.add({
targets: '.onboardtxt.two',
translateX: 250,
})
.add({
targets: '.onboardtxt.three',
translateX: 250,
});

How to get multiple sliders to work with java script/html/jquery

I'm currently working on a site that hosts podcasts. I have it so the podcasts plays, but I can only get very first podcast volume slider to work; the second podcast's sliders moves, but doesn't change the volume at all. Each player has a "player" id that is used to identify it. I've everything else and am stuck on this one little bug. Any help would be appreciated. Here is the javascript that creates the sliders and tries to links them to each player:
<script>
audiojs.events.ready(function()
{
var as = audiojs.createAll();
$('.slider').each(function()
{
var slider = $('.slider'),
tooltip = $('.tooltip'),
audioList = document.getElementsByTagName("audio").length;
console.log(audioList)
tooltip.hide();
slider.slider(
{
range: "min",
min: 0,
max: 100,
value: 50,
change: function()
{
for (var i = 0; i <= as.length; i++)
{
//console.log(i)
var value = $(".slider").slider("value");
document.getElementById("player").volume = (value / 100);
};
},
start:function(event,ui)
{
tooltip.fadeIn('fast');
},
slide: function(event, ui)
{
var volume = $('.volume'),
value = $(".slider").slider("value");
document.getElementById("player").volume = (value / 100);
tooltip.css('left',value / 2).text(ui.value);
if(value <= 5)
{
volume.css('background-position', '0 0');
}
else if (value <= 25)
{
volume.css('background-position', '0 -25px');
}
else if (value <= 75)
{
volume.css('background-position', '0 -50px');
}
else
{
volume.css('background-position', '0 -75px');
};
},
stop:function(event,ui)
{
tooltip.fadeOut('fast');
},});});}); </script>

jQuery addClass to multiple elements shows an unexpected delay

I'm making a flipping counter which is supposed to change color when reaching the target number (1000 in the example). But the thing is the different parts of the counter doesn't change color at the same time, we can clearly see a delay between the tiles that make up the counter...
I'm using a simple jQuery addClass to trigger the color change:
$("#rhcounter .count").addClass("red");
Any ideas what could be causing that ?
Here is the fiddle: http://jsfiddle.net/ka6ke28m/6/
Thanks for your help !
First issue:
There was a huge amount of wasted processing going on. jQuery selectors have an overhead so reduce them to a minimum and complex selectors more-so. I have reduced that considerably.
Second issue:
There is a nasty visual glitch on some browsers that looked like this:
Which you can eliminate by using background-color: instead of background: (which tries to completely re-render the area instead of just fill the background colour).
Third issue:
The color blue left behind was down to slow repainting of the screen. The above two fixes had a huge impact and I also tried adding specific CSS animations that worked only with the red class. This can probably be improved now you know the causes of the slow painting (e.g. have blue and red CSS animation?):
http://jsfiddle.net/TrueBlueAussie/ka6ke28m/10/
$(function () {
var total = 1000,
current = 950,
timeout = 150,
inc = 7,
prevTiles = ["0", "0", "0", "0"],
interval = setInterval(function () {
increase()
}, timeout),
increase = function () {
current += inc;
if (current >= total) {
clearInterval(interval);
current = total;
}
if (current === total) {
$("#rhcounter .count").addClass("red");
}
// instant timer to delay css
setTimeout(function () {
var tiles = [false, false, false, false],
currStr = (current + "").split("").reverse().join("");
for (var i = 0; i < currStr.length; i++) {
if (currStr[i] !== prevTiles[i]) {
tiles[i] = true;
prevTiles[i] = currStr[i];
}
}
tiles.forEach(function (tile, index) {
if (!tile) {
return;
}
// Get the current tile
var $tile = $("#rhcounter div.tile" + index);
$tile.children('span.curr').each(function () {
$(this).text($tile.text());
});
$tile.removeClass("flip");
setTimeout(function () {
$tile.addClass("flip");
}, 5);
var top = $tile.find("span.count.next.top"),
bottom = $tile.find("span.count.next.bottom"),
delay = (index === 0 ? timeout : 250);
setTimeout(function () {
top.text(prevTiles[index]);
}, delay / 2);
setTimeout(function () {
bottom.text(prevTiles[index]);
}, delay / 2);
});
}, 1);
};
});
that was happening because you were changing color before changing text. i just shifted if condition and i think that is what you wanted DEMO
$(window).load(function() {
var total = 1000, current = 950, timeout = 150, inc = 7,
prevTiles = ["0","0","0","0"],
interval = setInterval(function(){increase()}, timeout),
increase = function () {
current += inc;
if (current >= total) {
clearInterval(interval);
current = total;
}
var tiles = [false, false, false, false],
currStr = (current+"").split("").reverse().join("");
for (var i = 0; i < currStr.length; i++) {
if (currStr[i] !== prevTiles[i]) {
tiles[i] = true;
prevTiles[i] = currStr[i];
}
}
tiles.forEach(function (tile, index) {
if (!tile) { return; }
$("#rhcounter > div[class~='tile"+index+"'] > span[class~='curr']").each(function() {
$(this).text($("#rhcounter > div[class~='tile"+index+"'] > span.count.next.top").text());
});
$("#rhcounter > div[class~='tile"+index+"']").removeClass("flip");
setTimeout(function(){$("#rhcounter > div[class~='tile"+index+"']").addClass("flip");}, 5);
var top = $("#rhcounter > div[class~='tile"+index+"'] > span.count.next.top"),
bottom = $("#rhcounter > div[class~='tile"+index+"'] > span.count.next.bottom"),
delay = (index === 0 ? timeout : 250);
setTimeout(function(){ top.text(prevTiles[index]); }, delay/2);
setTimeout(function(){ bottom.text(prevTiles[index]); }, delay/2);
});
if (current === total) {
$("#rhcounter .count").addClass("red");
}};
});

Categories