<meter> Color Change on Animation Health Bar - javascript

I'm making a health bar and I wish to use <meter>. I have it working except it doesn't change color as it decreases. I figured I could fix this with an if tree but I don't know how to change the color of the bar when it hits a particular width. Any ideas? Thanks!!
FIDDLE:
http://jsfiddle.net/Thaikhan/d018xzbj/13/
HTML:
<meter id="your_meter" min="0" low="30" optimum="100" high="50" max="100" value="80"> </meter>
<input type="submit" id="byBtn" value="Change" onclick="change()"/>
JavaScript:
function change(){
setTimeout(function () {
var start = $('#your_meter').attr('value');
var end = 55;
if (start > 50) {
//STARTING FROM GREEN
if (end < 50) {
//CONTINUE PAST GREEN
var css = document.createElement("style");
css.type = "text/css";
css.innerHTML = "#your_meter::-webkit-meter-optimum-value { transition: width 3s; width: 50% !important; }";
document.body.appendChild(css);
//CHANGE COLOR AND CONTINUE TO DECREASE///??????????????????
if (end < 30) {
//CONTINUE PAST YELLOW
}
else {
//STOP BEFORE YELLOW ENDS
setTimeout(function() {
var css2 = document.createElement("style");
css2.type = "text/css";
css2.innerHTML = "#your_meter::-webkit-meter-optimum-value { transition: width 3s; width: "+end+"% !important; }";
document.body.appendChild(css2);
}, 3000);
}
}
else {
//STOP BEFORE GREEN ENDS
var css = document.createElement("style");
css.type = "text/css";
css.innerHTML = "#your_meter::-webkit-meter-optimum-value { transition: width 3s; width: "+end+"% !important; }";
document.body.appendChild(css);
}
}...

The reason is because whilst you are updating the meter optimum value through CSS, you are not updating the value that the meter is actually using.
You will need to dynamically update the value of the meter's value, whilst you're updating the CSS value. You can do it like this:
document.getElementById("your_meter").value = "30";
So, to do what you're trying to do would look something like this:
function change(){
var end = 35;
var meter = document.getElementById("your_meter");
var current = meter.value;
function render(){
console.log(meter.value);
if(current <= end){
window.cancelAnimationFrame(render);
return;
}
meter.value = current - 1;
current = meter.value;
window.requestAnimationFrame(render);
}
window.requestAnimationFrame(render);
render();
}
Based on a dynamic time:
function change(){
var end = 35;
var meter = document.getElementById("your_meter");
var current = meter.value;
var interval;
var speed = 30;
function render(){
console.log(meter.value);
if(current <= end){
clearInterval(interval);
return;
}
meter.value = current - 1;
current = meter.value;
window.requestAnimationFrame(render);
}
interval = setInterval(render, speed);
}

Related

How to use the counter when the counter appears on the page and not when the page is loaded

In my project, there should be a counter.
I want the counter to start when scrolling is done, not when the screen is loading.
I used "reveal" for this, but still the counter starts when the screen loads.
In the project in question, I like when scrolling the page from top to bottom and from bottom to top "reveal" arrives, the counter starts.
How can I solve this problem? Thanks in advance for your guidance.
//counter
const counter = (EL) => {
const duration = 4000; // Animate all counters equally for a better UX
const start = parseInt(EL.textContent, 10); // Get start and end values
const end = parseInt(EL.dataset.counter, 10); // PS: Use always the radix 10!
if (start === end) return; // If equal values, stop here.
const range = end - start; // Get the range
let curr = start; // Set current at start position
const loop = (raf) => {
if (raf > duration) raf = duration; // Stop the loop
const frac = raf / duration; // Get the time fraction
const step = frac * range; // Calculate the value step
curr = start + step; // Increment or Decrement current value
EL.textContent = parseInt(curr, 10); // Apply to UI as integer
if (raf < duration) requestAnimationFrame(loop); // Loop
};
requestAnimationFrame(loop); // Start the loop!
};
document.querySelectorAll("[data-counter]").forEach(counter);
//counter
// reveal
function reveal() {
var reveals = document.querySelectorAll(".reveal");
for (var i = 0; i < reveals.length; i++) {
var windowHeight = window.innerHeight;
var elementTop = reveals[i].getBoundingClientRect().top;
var elementVisible = 10;
if (elementTop < windowHeight - elementVisible) {
reveals[i].classList.add("active");
} else {
reveals[i].classList.remove("active");
}
}
}
window.addEventListener("scroll", reveal);
// reveal
.container{
overflow-y: scroll;
}
.pre-reveal{
width: 100%;
height: 80vh;
border: 1px solid red;
}
/* reveal */
.reveal {
border: 1px solid green;
position: relative;
opacity: 0;
margin-top: 1rem;
}
.reveal.active {
opacity: 1;
}
/* reveal */
<div class="container">
<div class="pre-reveal"></div>
<div class="reveal">
<span>A: </span>
<span data-counter="10">0</span>
<br>
<span>B: </span>
<span data-counter="2022">1000</span>
<br>
<span>C: </span>
<span data-counter="0">9999</span>
<br>
<span>D: </span>
<span data-counter="-1000">1000</span>
<br>
<span>E: </span>
<span data-counter="666">0</span>
<br>
</div>
</div>

Javascript style.transform = scale(x,y) animation not working

I was trying to make an animation using the style.transform property. The intention was to loop the transform scale property with slight increase in x and y on each round, but it failed. How can I achieve this effect?
x = document.getElementById("btn");
x.onclick = function() {
for (let y = 0; y < 1; y += 0.1) {
x.style.transform = 'scale(1.' + y + ',' + '1.' + y + ')';
}
}
<button id='btn'>button</button>
You should use CSS transitions.
Style your button like so:
#btn {
transition: transform 0.1s
}
That code will make the button transition during 0.1 seconds whenever the transform property is changed, for example the scale.
Then, from your JavaScript code, you juste have to assign the transform style once, and CSS will transition automatically.
x = document.getElementById("btn");
x.onclick = function() {
x.style.transform = 'scale(2,2)'; // or any x and y value
}
x = document.getElementById("btn");
x.onclick = function() {
x.style.transform = 'scale(2,2)'; // or any x and y value
}
#btn {
transition: transform 0.1s
}
<button id="btn">button</button>
You could do it with a combination of setInterval, requestAnimationFrame and CSS transitions to control the speed of the animation, good performance and a smooth controlled transition.
const button = document.getElementById("btn");
function scaleButton(speed, size = 1) {
let end = size * 2;
let interval = setInterval(() => {
if (size === end) {
clearInterval(interval);
button.style.transitionDuration = '';
}
size += 0.1;
size = parseFloat(size.toFixed(1)); // Round to 1 decimal point.
requestAnimationFrame(() => {
button.style.transform = `scale(${size}, ${size})`;
});
}, speed);
button.style.transitionDuration = `${speed}ms`;
}
button.onclick = function() {
scaleButton(50);
};
html {
width: 100%;
height: 100%;
}
body {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
#btn {
transition: transform linear;
}
<button id='btn'>button</button>
Here is a soultion using requestAnimationFrame;
x = document.getElementById("btn");
let y = 0;
function animate (y) {
if(y < 1) {
y += 0.1;
x.style.transform = `scale(${1 + y},${1 + y})`;
requestAnimationFrame( () => animate(y) );
}
}
x.onclick = function() {
animate(0);
}
<button id='btn'>button</button>
The browser has an, how to say, an painting cycle. Every 16ms (i am not sure if its exactly 16ms) the browser does an repaint.
The problem that you have, is that your loop is already done before the next repaint cycle.
Here is an solution with async / await:
You can create an function called readyToAnimate it returns an promise that resolves the callback function of the requestAnimationFrame.
The requestAnimationFrame callback gets executed before the next repaint.
Now in your loop you can use await readyToAnimate(). It will wait till the browser is ready for the next repaint.
x = document.getElementById("btn");
x.onclick = async function() {
for (let y = 0; y < 1; y += 0.1) {
await readyToAnimate();
x.style.transform = `translateX(${y * 200}px)`;
}
}
function readyToAnimate() {
return new Promise(res => requestAnimationFrame(res));
}
<button id='btn'>button</button>

className Changing not passing through all if and else if Statements

Having an issue with changing classes. I can get it to work if I hardcode pixels into the style change but wanted a cleaner version using classes and CSS. The goal is to have the pixel change sizes larger and smaller due to the value of healthPercent in width. This part works but the trouble part is changing the classes to change the color of the bar. For some reason, it does not pass through the if statements correctly and just stays green until death (zero or less for healthPercent), when it reaches zero and then turns red. I can't quite figure out why it passes through the first if check but not the rest. Any ideas on how to fix this?
EDIT: I'm working on a game. just to clarify.
JAVASCRIPT
displayLifeBar = function ()
{
pixelMod = 2.3; //added definition here but global on full code
healthPercent = 130; //added here for example but passed down
lifeBar = 'player_lifebar';
document.getElementById(lifeBar).style.width = healthPercent + 'px';
var criticalLife = 25 * pixelMod;
var lowLife = 50 * pixelMod;
var hurtLife = 75 * pixelMod;
if (healthPercent <= 0){
document.getElementById(lifeBar).className = '';
}
else if (healthPercent < criticalLife){
document.getElementById(lifeBar).className = 'lifebar critical';
}
else if (healthPercent < lowLife){
document.getElementById(lifeBar).className = 'lifebar low';
}
else if (healthPercent < hurtLife){
document.getElementById(lifeBar).className = 'lifebar hurt';
}
else
{
document.getElementById(lifeBar).className = 'lifebar';
}
}
CSS
/* LIFEBAR COLORS STARTS */
.lifebar{
height:20px;
background-color: forestgreen;
}
.lifebar.hurt{
background-color: gold;
}
.lifebar.low{
background-color: orange;
}
.lifebar.critical{
background-color: red;
}
/* LIFEBAR COLORS ENDS */
Your code snippet seems to be working from what you described - assuming your HTML just looks like <div id='player_lifebar'></div>.
var pixelMod = 2.3;
var healthPercent = 10;
var lifeBar = 'player_lifebar';
displayLifeBar = function ()
{
document.getElementById(lifeBar).style.width = healthPercent + 'px';
var criticalLife = 25 * pixelMod;
var lowLife = 50 * pixelMod;
var hurtLife = 75 * pixelMod;
if (healthPercent <= 0){
document.getElementById(lifeBar).className = '';
} else if (healthPercent < criticalLife){
document.getElementById(lifeBar).className = 'lifebar critical';
} else if (healthPercent < lowLife){
document.getElementById(lifeBar).className = 'lifebar low';
} else if (healthPercent < hurtLife){
document.getElementById(lifeBar).className = 'lifebar hurt';
} else {
document.getElementById(lifeBar).className = 'lifebar';
}
}
displayLifeBar();
// for testing:
function modifyHealth(points) {
healthPercent = healthPercent + points;
displayLifeBar(); //re-render healthbar
}
document.getElementById('heal').addEventListener('click', function() { modifyHealth(10) });
document.getElementById('attack').addEventListener('click', function() { modifyHealth(-10) });
.lifebar{
height:20px;
background-color: forestgreen;
}
.lifebar.hurt{
background-color: gold;
}
.lifebar.low{
background-color: orange;
}
.lifebar.critical{
background-color: red;
}
<div id='player_lifebar'></div>
<!-- for testing -->
<br>
<button id='heal'>♡ heal (+10)</button>
<button id='attack'>🗡 attack (-10)</button>

Creating a volume bar javascript

I need to create an audio player, and I can't see how to convert it to javascript.
This is what I have so far...
HTML:
<audio id="music" src="http://www.sousound.com/music/healing/healing_01.mp3"
data-title="Title of the audio" data-author="Author's name"></audio>
<span class="volumeScaleHolder">
<span id="progressBar">
<span id="myVolume"></span>
</span>
</span>
CSS:
#progressBar {
width: 100%;
height: 8px;
background-color: #666666;
display: inline-block;
}
#myVolume {
width: 10%;
height: 100%;
background-color: #B4BB6B;
display:block;
}
border: 0;
}
.volumeScaleHolder {
padding:0;
margin: 3px 0;
}
JAVASCRIPT:
var audio = document.getElementById("music");
var audioVolumeBar = document.getElementById("progressBar");
var myVolumeBar = document.getElementById("myVolume");
var audioVolume = audio.volume;
function volumeAsProgressBar(volume) {
var audioVolumeProgressBar = document.getElementById("progressBar");
var audioVolumeMyBar = document.getElementById("myVolume");
var volume = audioVolume;
var totalVolume = 1;
var minVolume = 0;
var maxVolume = 1;
}
alert(audio.volume);
myVolumeBar.innerHTML = myVolumeBar.innerHTML + volumeAsProgressBar(audioVolume);
I know the javascript is doing nothing. It is because I don't know how to proceed. So what I am trying to do is to check the value of the audio's volume, and then reflect it in the horizontal bar.... It is just some training, so I am not allowed to use any plugin, <progress> or input type range.
I have added width:10% to #myVolume, just to make sure it is there.
Then... I don't know how can I pass the value of the volume (from 0 to 1) to somehow a percentage, because I guess the bar works as a percentage, right?
I don't need anyone giving me the solution code....but some help about what should I think of...
I have made this jsfiddle, although it is quite useless... :(
https://jsfiddle.net/zuL6umjo/1/
To create a bar capable of changing the volume of a audio element here is a code that will help.
var e = document.querySelector('.volume-slider-con');
var eInner = document.querySelector('.volume-slider');
var audio = document.querySelector('audio');
var drag = false;
e.addEventListener('mousedown',function(ev){
drag = true;
updateBar(ev.clientX);
});
document.addEventListener('mousemove',function(ev){
if(drag){
updateBar(ev.clientX);
}
});
document.addEventListener('mouseup',function(ev){
drag = false;
});
var updateBar = function (x, vol) {
var volume = e;
var percentage;
//if only volume have specificed
//then direct update volume
if (vol) {
percentage = vol * 100;
} else {
var position = x - volume.offsetLeft;
percentage = 100 * position / volume.clientWidth;
}
if (percentage > 100) {
percentage = 100;
}
if (percentage < 0) {
percentage = 0;
}
//update volume bar and video volume
eInner.style.width = percentage +'%';
audio.volume = percentage / 100;
};
.volume-slider-con{
height:10px;
width:50%;
position:relative;
background-color:#ddd;
}
.volume-slider{
height:100%;
width:100%;
position:relative;
background-color:red;
}
<audio src="http://www.sousound.com/music/healing/healing_01.mp3" controls></audio>
<div class="volume-slider-con">
<div class="volume-slider"></div>
</div>
Your almost there. Here's an updated fiddle: https://jsfiddle.net/zuL6umjo/8/
Here is a way to expand/shrink your volume bar based off of the volume from the audio element.
function updateVolume () {
myVolumeBar.style.width = audioVolume * 100 + '%';
}
updateVolume();

Animate changing words width

I have a sentence where I fade in one word and replace it with another from an array. However, since the words all vary in length, the sentence width abruptly changes and it results in a choppy transition.
How can I animate the width change? I tried adding a transition to the container of the sentence in css but that didn't work. I applied the transition as 1.5s all linear, so it should be animating the width as well as everything else whenever there is change. Any ideas?
$(function() {
var hello = ['dynamic', 'a', 'aklsjdlfajklsdlkf', 'asdf'];
var used = ['dynamic'];
var greeting = $('#what');
var item;
function hey() {
item = hello[Math.floor(Math.random() * hello.length)];
if (hello.length != used.length) {
while (jQuery.inArray(item, used) != -1) {
item = hello[Math.floor(Math.random() * hello.length)];
}
used.push(item);
} else {
used.length = 0;
item = hello[Math.floor(Math.random() * hello.length)];
used.push(item);
}
greeting.html(item);
greeting.animate({
"opacity": "1"
}, 1500);
}
window.setInterval(function() {
greeting.animate({
"opacity": "0"
}, 1500);
setTimeout(hey, 1500)
}, 5000);
});
#sentence {
transition: 1.5s all linear;
}
#what {
font-style: italic;
text-decoration: underline;
color: red;
}
<p id="sentence">
This is a sentence that has <span id="what">dynamic</span> text that alters width.
</p>
<script src="//ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
EDIT: Sorry if I was unclear, I only want to fade out the word, not the entire sentence. I'm trying to animate the width to fit the new word. I don't want to change/add any elements, just solve with the current tags in place.
function dataWord () {
$("[data-words]").attr("data-words", function(i, d){
var $self = $(this),
$words = d.split("|"),
tot = $words.length,
c = 0;
// CREATE SPANS INSIDE SPAN
for(var i=0; i<tot; i++) $self.append($('<span/>',{text:$words[i]}));
// COLLECT WORDS AND HIDE
$words = $self.find("span").hide();
// ANIMATE AND LOOP
(function loop(){
$self.animate({ width: $words.eq( c ).width() });
$words.stop().fadeOut().eq(c).fadeIn().delay(1000).show(0, loop);
c = ++c % tot;
}());
});
}
// dataWord(); // If you don't use external fonts use this on DOM ready; otherwise use:
$(window).on("load", dataWord);
p{text-align: center;font-family: 'Open Sans Condensed', sans-serif;font-size: 2em;}
/* WORDS SWAP */
[data-words]{
vertical-align: top;
position: static;
}
[data-words] > span{
position: absolute;
color: chocolate;
}
<link href='http://fonts.googleapis.com/css?family=Open+Sans+Condensed:300' rel='stylesheet' type='text/css'>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>
We provide
<span data-words="code|solutions|design"></span>
for your business.
</p>
<p>
You ordered
<span data-words="1|3|28"></span>
<b>big</b>
<span data-words="salad|macs|chips"></span>
</p>
When you set new word for your sentence, you can save #what width and then make an animation with this width too. Like this:
// declare as global variable and update when you set new word
var width = greeting.css('width');
// animation
greeting.animate({
"opacity": "0", "width": width
}, 1500, function(){
});
I have had the same problem and went with a different approach, not fading but typing: jsfiddle demo
function type($el, text, position) {
if (text.length >= position) {
var rchars = 'qbvol'; // typo chars
if (position % 3 == 0 && Math.random() > .85) { // insert typo!
var typo;
var chr = text.substr(position, 1);
if (chr == chr.toUpperCase()) { typo = chr.toLowerCase(); }
else { typo = rchars.substr(Math.floor(Math.random() * rchars.length), 1); }
$el.text(text.substring(0, position - 1) + typo + '_');
setTimeout(function() { type($el, text, position - 1); }, 200)
}
else {
$el.text(text.substring(0, position) + '_');
setTimeout(function() { type($el, text, position + 1); }, 150)
}
}
else {
setTimeout(function() { $el.text(text); }, 400)
}
}
It basically inserts your new text on the page, with a nice caret and typo to make it look like someone is typing it.
Try this out:- http://jsfiddle.net/adiioo7/c8fFU/13/
You can update the sentence effect depending upon your requirement. Currently it is using fadein/fadeout.
JS:-
$(function () {
var hello = ['jupiter', 'a', 'aklsjdlfajklsdlkf', 'asdf'];
var used = ['jupiter'];
var greeting = $('#what');
var item;
var sentence = $('#sentence');
function hey() {
item = hello[Math.floor(Math.random() * hello.length)];
if (hello.length != used.length) {
while (jQuery.inArray(item, used) != -1) {
item = hello[Math.floor(Math.random() * hello.length)];
}
used.push(item);
} else {
used.length = 0;
item = hello[Math.floor(Math.random() * hello.length)];
used.push(item);
}
greeting.html(item);
greeting.animate({
"opacity": "1"
}, 1500);
sentence.fadeIn(1500);
}
window.setInterval(function () {
sentence.fadeOut(1500);
greeting.animate({
"opacity": "0"
}, 1500);
setTimeout(hey, 1500);
}, 5000);
});

Categories

Resources