I'm using the following code to fadein/fadeout images every second which works fine but I would like to fade the images in and out every 1/2 second. I can change the setInterval to 500 but this simply causes a bit of a mess. I clearly need to redfine fadein and fadeout.
I have bootstrap loaded so I'm guessing the functions are defined within the bootstrap js but how do I respecify their timing?
var $els = $('div[id^=image]'),
i = 0,
len = $els.length;
var start = 1;
var end = 999999999999999;
jQuery(function () {
$els.slice(1).hide();
spin = setInterval(function () {
$els.eq(i).fadeOut(function () {
i = Math.floor(Math.random() * len);
$els.eq(i).fadeIn();
});
start = new Date().getTime();
if (start > end) {
clearInterval(spin);
}
}, 1000);
{% for m in myusers %}
if (i == {{ forloop.counter0 }}) { document.getElementById('name{{ forloop.counter0 }}').style.display = 'Block';}
{% endfor %}
});
Since you are using jQuery, why not use fadeOut/fadeIn or fadeToggle?
$(document).ready(function() {
setInterval(function() {
$('.a1, .a2').stop().fadeToggle(500);
}, 500);
});
.wrapper {
position: relative;
width: 100px;
height: 100px;
margin: 1px;
display: inline-block;
}
.a1,
.a2 {
position: absolute;
left: 0;
top: 0;
width: 100px;
height: 100px;
background-color: blue;
}
.a2 {
display: none;
background-color: red;
}
.wrapper2 .a1 {
display: none;
}
.wrapper2 .a2 {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div class="a1"></div>
<div class="a2"></div>
</div>
<div class="wrapper wrapper2">
<div class="a1"></div>
<div class="a2"></div>
</div>
var box = document.getElementById('box');
function fadeOutIn(elem, speed ) {
if (!elem.style.opacity) {
elem.style.opacity = 1;
} // end if
var outInterval = setInterval(function() {
elem.style.opacity -= 0.02;
if (elem.style.opacity <= 0) {
clearInterval(outInterval);
var inInterval = setInterval(function() {
elem.style.opacity = Number(elem.style.opacity)+0.02;
if (elem.style.opacity >= 1)
clearInterval(inInterval);
}, speed/50 );
} // end if
}, speed/50 );
} // end fadeOut()
fadeOutIn(box, 2000 );
Hello please see my solution . It is your helpful or not.
Thanks.
Related
Already tried answer - jQuery Fade Images simultaneously
I have 2 divisions with 2 different images and i want them to load after i scroll down to that section, only 1 image is fading-in after i apply the same function to both images.
var opacity = 0;
var intervalID = 0;
window.onload = fadeIn;
function fadeIn()
{
setInterval(show, 200);
}
function show()
{
var star11 = document.getElementById("star1");
opacity = Number(window.getComputedStyle(star11).getPropertyValue("opacity"));
if (opacity < 1)
{
opacity = opacity + 0.1;
star11.style.opacity = opacity
}
else
{
clearInterval(intervalID);
}
}
var opacity = 0;
var intervalID = 0;
window.onload = fadeIn;
function fadeIn()
{
setInterval(show, 200);
}
function show()
{
var star22 = document.getElementById("star2");
opacity = Number(window.getComputedStyle(star22).getPropertyValue("opacity"));
if (opacity < 1)
{
opacity = opacity + 0.1;
star22.style.opacity = opacity
}
else
{
clearInterval(intervalID);
}
}
#star1{
opacity:0;
width:100px;
height:100px;
float:left;
}
#star2{
opacity:0;
width:100px;
height:100px;
float:right;
}
<div>
<img id="star1" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/socialmedia/apple/271/star_2b50.png" alt="star123">
</div>
<div>
<img id="star2" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/socialmedia/apple/271/star_2b50.png" alt="123star">
</div>
P.S - I am new to JS/JQuery.
Thank You
You are declaring the function show twice. So ehat happens here is that the first function that you defined for the first star will be over written by the second function written for the second star and hence the styles for the second star only works. Function defenition is just like variable assigning. The variable name taks the latest value for which that is assigned and will neglect the previous values when define multiple times.
So what I suggest is to decalre the function only once and pass the id as a parameter.
var opacity = 0;
var intervalID = 0;
window.onload = fadeIn;
function fadeIn() {
setInterval(() => show('star1'), 200);
setInterval(() => show('star2'), 200);
}
function show(starId) {
var star = document.getElementById(starId);
opacity = Number(
window.getComputedStyle(star).getPropertyValue("opacity")
);
if (opacity < 1) {
opacity = opacity + 0.1;
star.style.opacity = opacity;
} else {
clearInterval(intervalID);
}
}
#star1 {
opacity: 0;
width: 100px;
height: 100px;
float: left;
}
#star2 {
opacity: 0;
width: 100px;
height: 100px;
float: right;
}
<div>
<img
id="star1"
src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/socialmedia/apple/271/star_2b50.png"
alt="star123"
/>
</div>
<div>
<img
id="star2"
src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/socialmedia/apple/271/star_2b50.png"
alt="123star"
/>
</div>
Update
Handling scroll events.
I have refered this answer to prepare the javascript/jquery solution for scroll
$(document).ready(function () {
$(window).scroll(function () {
console.log("Scroll");
triggerScrollListner("star1");
triggerScrollListner("star2");
});
});
function triggerScrollListner(id) {
var hT = $(`#${id}`).offset().top,
hH = $(`#${id}`).outerHeight(),
wH = $(window).height(),
wS = $(this).scrollTop();
if (wS > hT + hH - wH) {
setInterval(() => show(id), 200);
}
}
var opacity = 0;
var intervalID = 0;
function show(starId) {
var star = document.getElementById(starId);
opacity = Number(
window.getComputedStyle(star).getPropertyValue("opacity")
);
if (opacity < 1) {
opacity = opacity + 0.1;
star.style.opacity = opacity;
} else {
clearInterval(intervalID);
}
}
body {
height: 1000px;
}
#star1 {
opacity: 0;
width: 100px;
height: 100px;
float: left;
}
#star2 {
opacity: 0;
width: 100px;
height: 100px;
float: right;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<div>
<img
id="star1"
src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/socialmedia/apple/271/star_2b50.png"
alt="star123"
/>
</div>
<div>
<img
id="star2"
src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/socialmedia/apple/271/star_2b50.png"
alt="123star"
/>
</div>
More generic solution with multiple stars
Since there was only one row, the visualiztion is a little hard. In this example I have added multiple rows and have made a little bit more visual.
$(document).ready(function () {
$(window).scroll(function () {
triggerScrollListner("star1");
triggerScrollListner("star2");
triggerScrollListner("star3");
triggerScrollListner("star4");
});
});
function triggerScrollListner(id) {
var hT = $(`#${id}`).offset().top,
hH = $(`#${id}`).outerHeight(),
wH = $(window).height(),
wS = $(this).scrollTop();
if (wS > hT + hH - wH) {
setInterval(() => show(id), 200);
}
}
var opacity = 0;
var intervalID = 0;
function show(starId) {
var star = document.getElementById(starId);
opacity = Number(
window.getComputedStyle(star).getPropertyValue("opacity")
);
if (opacity < 1) {
opacity = opacity + 0.1;
star.style.opacity = opacity;
} else {
clearInterval(intervalID);
}
}
.star {
opacity: 0;
width: 100px;
height: 100px;
}
.container1, .container2 {
display: flex;
width: 100%;
flex-direction: column;
justify-content: space-between;
flex-direction: row;
}
.container2 {
margin-top: 1500px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<div class="container1">
<img
id="star1"
class="star"
src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/socialmedia/apple/271/star_2b50.png"
alt="star123"
/>
<img
id="star2"
class="star"
src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/socialmedia/apple/271/star_2b50.png"
alt="123star"
/>
</div>
<div class="container2">
<img
id="star3"
class="star"
src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/socialmedia/apple/271/star_2b50.png"
alt="star123"
/>
<img
id="star4"
class="star"
src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/socialmedia/apple/271/star_2b50.png"
alt="123star"
/>
</div>
Just fiddled around into the code
Needed only 1 function and changed code in js only.
var opacity = 0;
var intervalID = 0;
window.onload = fadeIn;
function fadeIn()
{
setInterval(show, 200);
}
function show()
{
var star11 = document.getElementById("star1");
var star22 = document.getElementById("star2");
opacity =
Number(window.getComputedStyle(star22).getPropertyValue("opacity"));
opacity =
Number(window.getComputedStyle(star11).getPropertyValue("opacity"));
if (opacity < 1)
{
opacity = opacity + 0.1;
star11.style.opacity = opacity
star22.style.opacity = opacity
}
else
{
clearInterval(intervalID);
}
}
I'm being tormented in the past 4 hours to find out how to do this, I don't know what I'm doing wrong, I have a page with multiple layers, I wish to trigger some transition when the needed page has opacity 1, it should be simple when u think of it, here is my code, please help ;)
slide1 = document.querySelector('.slide1');
function videoPlay() {
var videoOne = document.getElementById('myVideo');
if ((slide1.style.opacity) > 0 ) {
videoOne.play();
}
}
videoPlay();
.slide {
width: 100%;
background-size: cover;
background-position: center;
position: absolute;
}
.slide1 {
width: 100%;
background: none;
opacity: 0;
}
<div class="slide slide1">
<div class="slide-content">
<div class="secondColumn">
<video muted id="myVideo">
<source src="Media/Acqua.mp4" type="video/mp4">
</video>
<div class="lowerTab"></div>
</div>
</div>
here is the code which i use to change the opacity using the wheel :
//wheel event
document.addEventListener('wheel',
function scrollWheel(event) {
var fig =event.deltaY;
if (fig > 0) {
slideMove();
}
else if (fig<0) {
slideMovReverse();
}
})
//basic movement
function slideMove() {
if (current === sliderImages.length-1 ) {
current = -1
}
reset();
sliderImages[current+1].style.transition = "opacity 1s ease-in 0s";
sliderImages[current+1].style.opacity= "1.0";
current++;
}
You can use the transitionend event, but you'd have to set up the transition first. As it sits now, there's not much information in your question about the different slides, how the transitions are set up, etc. Here's a baseline to give you an idea:
const slide1 = document.querySelector('.slide1');
const videoEl = document.querySelector('.slide1__video');
const button = document.querySelector('button');
let inView = false;
slide1.addEventListener('transitionend', () => {
let content = 'Playing';
if (inView) {
content = ''
}
videoEl.textContent = content;
inView = !inView;
})
button.addEventListener('click', () => {
slide1.classList.toggle('active')
})
.slide1 {
transition: opacity 500ms linear;
opacity: 0;
border: 1px solid green;
padding: 10px;
margin-bottom: 24px
}
.slide1.active {
opacity: 1
}
<div class="slide1">
Slide 1
<div class="slide1__video"></div>
</div>
<button>Next</button>
Edit
It'll need some love but I think it's in the right direction to what you're after.
const slides = Array.from(document.querySelectorAll('.slide'));
document.addEventListener('wheel', onScroll);
const SCROLL_TOLERANCE = 100;
let currentIndex = 0;
let currentScroll = 0;
function onScroll(e) {
if (e.deltaY > 0) {
currentScroll += 1;
} else {
currentScroll -= 1;
}
if (currentScroll >= (currentIndex * SCROLL_TOLERANCE) + 15) {
showNext();
} else if (currentScroll <= (currentIndex * SCROLL_TOLERANCE) - 15) {
showPrevious();
}
}
function showNext() {
if (currentIndex === slides.length - 1) {
return console.warn('At the end.');
}
currentIndex += 1;
setSlide();
}
function showPrevious() {
if (currentIndex === 0) {
return console.warn('At the beginning.');
}
currentIndex -= 1;
setSlide();
}
function setSlide() {
let newOpacity = 0;
slides.forEach(slide => {
if (+slide.dataset.index === currentIndex) {
newOpacity = 1
} else {
newOpacity = 0;
}
slide.style.opacity = newOpacity;
slide.addEventListener('transitionend', () => {
console.log('Done transitioning!');
// Do things here when the transition is over.
})
});
}
html,
body {
padding: 0;
margin: 0;
font-family: sans-serif;
font-size: 18px
}
.slide {
border: 3px solid #efefef;
position: absolute;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
transition: all 500ms linear;
opacity: 0;
transition-delay: 250ms;
}
.slide.active {
opacity: 1;
}
<div class="slide active" data-index="0">
Slide 1
</div>
<div class="slide" data-index="1">
Slide 2
</div>
<div class="slide" data-index="2">
Slide 3
</div>
<div class="slide" data-index="3">
Slide 4
</div>
I don't know why people's are not answering this question.I'm making a horizontal infinite loop slider. What approach i'm using is making a ul container which has 3 images, for example if there are 3 images then clone the first image and place it to the end of the slider, same with last image make clone and place it before the first image. So now total images are 5. Default slider translation always start from first image not from clone one. Here is an example. What i'm facing is, I want to reset the slider after slider comes to the last clone image with same continuous loop like a carousel slider. I try using addEventListener with the event name transitionend but that event doesn't perform correctly and showing unsatisfied behavior. Is there a way to fix this?
(function () {
var resetTranslation = "translate3d(-300px,0px,0px)";
var elm = document.querySelector('.Working');
elm.style.transform = resetTranslation;
var arr = document.querySelectorAll('.Working li');
var clonefirst,
clonelast,
width = 300;
index = 2;
clonefirst = arr[0].cloneNode(true);
clonelast = arr[arr.length - 1].cloneNode(true);
elm.insertBefore(clonelast, arr[0]);
arr[arr.length - 1].parentNode.insertBefore(clonefirst, arr[arr.length - 1].nextSibling);
//Update
arr = document.querySelectorAll('.Working li');
elm.style.transition = 'transform 1.5s ease';
setInterval(function () {
elm.style.transform = 'translate3d(-' + index * width + 'px,0px,0px)';
if (index == arr.length - 1) {
elm.addEventListener('transitionend', function () {
elm.style.transform = resetTranslation;
});
index = 1;
}
index++;
}, 4000)
})();
*{
box-sizing: border-box;
}
.wrapper{
position: relative;
overflow: hidden;
height: 320px;
width: 300px;
}
.Working{
list-style: none;
margin: 0;
padding: 0;
position: relative;
width: 3125%;
}
.Working li{
position: relative;
float: left;
}
img{
max-width: 100%;
display: block;
}
.SubContainer:after{
display: table;
clear: both;
content: "";
}
<div class="wrapper">
<ul class="SubContainer Working">
<li> <img class="" src="http://i.imgur.com/HqQb9V9.jpg" /></li>
<li><img class="" src="http://i.imgur.com/PMBBc07.jpg" /></li>
<li><img class="" src="http://i.imgur.com/GRrGSxe.jpg" /></li>
</ul>
</div>
I've messed around with your code to hack in a fix: https://jsfiddle.net/rap8o3q0/
The changed part:
var currentItem = 1;
setInterval(function () {
var getWidth = window.innerWidth;
if(len === currentItem){
i = 1;
currentItem = 1;
} else {
currentItem++;
i++;
}
var val = 'translate3d(-' + (i-1) * getWidth + 'px,0px,0px)';
UnorderedListElement.style.transform = val;
}, 3000);
Your transition end event is not immediately fire because last transition doesn't computed when last clone image appear. You can easily achieve this thing by using setTimeout function and pass number of milliseconds to wait, after that reset the translation. I don't know it's an efficient solution but i think its easily done by using this function.
Now I'm fixing your code with this.
(function () {
var elm = document.querySelector('.Working');
var arr = document.querySelectorAll('.Working li');
var clonefirst,
clonelast,
width = 300;
index = 2;
clonefirst = arr[0].cloneNode(true);
clonelast = arr[arr.length - 1].cloneNode(true);
elm.insertBefore(clonelast, arr[0]);
arr[arr.length - 1].parentNode.insertBefore(clonefirst, arr[arr.length - 1].nextSibling);
//Update
arr = document.querySelectorAll('.Working li');
setInterval(function () {
$(elm).css({
'transform': 'translate3d(-' + (index * width) + 'px,0px,0px)',
'transition': 'transform 1.5s ease'
});
if (index == arr.length - 1) {
setTimeout(function () {
$(elm).css({'transform': 'translate3d(-300px,0px,0px)', 'transition': 'none'});
index = 1;
}, 1400);
}
index++;
}, 2000)
})();
* {
box-sizing: border-box;
}
.wrapper {
position: relative;
overflow: hidden;
height: 320px;
width: 300px;
margin-top: 8px;
}
.Working {
list-style: none;
margin: 0;
padding: 0;
position: relative;
transform: translateX(-300px);
width: 3125%;
}
.Working li {
position: relative;
float: left;
}
img {
max-width: 100%;
display: block;
}
.SubContainer:after {
display: table;
clear: both;
content: "";
}
#checkboxer:checked + .wrapper {
overflow: visible;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label for="checkboxer">Remove Overflow Hidden</label>
<input type="checkbox" id="checkboxer" name="checkboxer"/>
<div class="wrapper">
<ul class="SubContainer Working">
<li> <img class="" src="http://i.imgur.com/HqQb9V9.jpg" /></li>
<li><img class="" src="http://i.imgur.com/PMBBc07.jpg" /></li>
<li><img class="" src="http://i.imgur.com/GRrGSxe.jpg" /></li>
</ul>
</div>
To come back your slider in its first position, you need to trigger a transition end event after the last (3th) translation, That works only one time.
$(UnorderedListElement).one("webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend", function ()
{
{
$(UnorderedListElement).css('transform', 'translate3d(0, 0, 0)');
}
});
But you need to make it to translate immediately. In link below you can see my slider that is very similar to yours, but works by absolute positioning and change the left property instead of transform.
Slider
Last thing: It is not a good idea to slide the hole of the image container. You must slide your images separately. In your way there is an obvious problem: When the page is sliding out the last image, there is not the next image to push it!
You can update your setInterval as
setInterval(function() {
var getWidth = window.innerWidth;
var val = 'translate3d(-' + i * getWidth + 'px,0px,0px)';
UnorderedListElement.style.transform = val;
i++;
if (i == 3) { //assuming three images here
i = 0
}
}, 3000)
var DomChanger;
(function() {
var listItem = document.querySelectorAll('.ah-slider li');
var len = listItem.length;
var getImage = document.querySelector('.ah-slider li img');
var UnorderedListElement = document.querySelector('.ah-slider');
var outerDiv = document.querySelector('.Slider');
UnorderedListElement.setAttribute('style', 'width:' + (len * 1000 + 215) + '%');
var i = 1;
DomChanger = function() {
for (var i = 0; i < len; ++i) {
listItem[i].setAttribute('style', 'width:' + window.innerWidth + 'px');
}
outerDiv.setAttribute('style', 'height:' + getImage.clientHeight + 'px');
};
setInterval(function() {
var getWidth = window.innerWidth;
var val = 'translate3d(-' + i * getWidth + 'px,0px,0px)';
UnorderedListElement.style.transform = val;
i++;
if (i == 3) {
i = 0
}
}, 3000)
})();
window.onload = function() {
DomChanger();
};
window.onresize = function() {
DomChanger();
};
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
background-color: #fff;
}
.Slider {
width: 100%;
margin: 50px 0 0;
position: relative;
overflow: hidden;
}
.ah-slider {
padding: 0;
margin: 0;
list-style: none;
position: relative;
transition: transform .5s ease;
}
.ah-slider li {
position: relative;
float: left;
}
.ah-slider li img {
max-width: 100%;
display: block;
}
.clearFix:after {
content: "";
display: table;
clear: both;
}
<div class="Slider">
<ul class="ah-slider clearFix">
<li>
<img class="" src="http://i.imgur.com/L9Zi1UR.jpg" title="" />
</li>
<li>
<img class="" src="http://i.imgur.com/FEcEwFs.jpg" title="" />
</li>
<li><img class="" src=http://i.imgur.com/hURSKNa.jpg" title="" /></li>
</ul>
</div>
Ok I have created a stamina container that has three stamina bars within each other, and when you click on the attack button some of the stamina is taken off the first stamina bar. However, if you keep clicking on the first stamina bar until it depletes to 0 it will then start to deplete the second stamina bar and so on. Now, that is how it was intended to happen, but I'm not able get it to function that way. Here is the jsfiddle for it, whenever I click on the attack button multiple times the stamina bar is lagging up and once the stamina bar is complete it will still deplete the stamina bar and regnerate itself again. You have to try the jsfiddle to understand what I'm talking about.
HTML
<body>
<div id="container">
<div class="hBar"> <div class="health"></div></div><!--hbar -->
<div class="sBar">
<div class="s3">
<div class="s2">
<div class="s1">
</div><!--s1 -->
</div><!--s2 -->
</div><!--s3 -->
</div><!--sBar -->
<div class="attack">attack</div>
</div><!--container -->
</body>
CSS
*{ margin:0px; padding:0px;}
.hBar{width:400px; height:40px; border:1px solid grey; margin-bottom:20px;}
.health{background:green;width:100%; height:100%;}
.sBar{ width:400px; height:40px; border:1px solid grey; margin-bottom:20px;}
.s3{ width:100%; height:100%; background:red;}
.s2{width:100%; height:100%; background:orange;}
.s1{width:100%; height:100%; background:yellow;}
#container{background:white; width:80%; padding:10px; margin:auto;}
body{background:#CCC;}
.attack{ border-radius:90px; border:black solid 1px; height:75px; width:75px; text-align:center; line-height:75px;}
.attack:hover{cursor:pointer;}
Javascript
$(document).ready(function () {
// one , two, and three variables are collecting the stamina bars
var one = $('.s1');
var two = $('.s2');
var three = $('.s3');
var oneWidth = one.width();
var twoWidth = two.width();
var threeWidth = three.width();
var stam = $('.sBar').width();
var damage;
var subtractHealth;
var num;
$('.attack').click(function () {
// timer is supposed to be the variable for a setInterval function
let timer;
// damage is supposed to take away the stamina
damage = 100;
// This function is supposed to stop the interval and the animation done on the
// stamina bars
function stopAnimate() {
clearInterval(timer);
one.stop();
two.stop();
three.stop();
}
// if the first and the second stamina bar is below 0, then add subtract the width to .s3
if (oneWidth <= 0 && twoWidth <= 0) {
subtractHealth = $('.s3').width() - damage;
three.animate({
'width': subtractHealth
}, 'fast');
// if the first stamina bar is less than 0, the subtract the width of .s2
} else if (oneWidth <= 0) {
subtractHealth = $('.s2').width() - damage;
two.animate({
'width': subtractHealth
}, 'fast');
// if the first stamina bar is not below 0 then run the content in this
} else {
subtractHealth = $('.s1').width() - damage;
one.animate({
'width': subtractHealth
}, 'fast');
}
// regenerates all the three stamina bars with the animate method
function regenerate(stam1, stam2, stam3) {
stam1.animate({
'width': stam
}, 1000, function () {
if (stam1.width() == stam) {
stam2.animate({
'width': stam
}, 1000, function () {
if (stam2.width() == stam) {
stam3.animate({
'width': stam
}, 1000)
}// if stam2
});//stam2.animate
}//if stam.width()
})//stam1.animate
setTimeout(stopAnimate(), 5000); //end function
}; //end regenerate
// run setInterval and assign the method to timer
timer = setInterval(regenerate(one, two, three), 1000);
}); //end click
}); //end ready
I'm not 100% certain that i have the effect you are after, but if not I think you should be able to modify this code to get the result you seek. If you would like additional assistance, drop a comment and I will be happy to see what I can do.
var staminaMax = 1000;
var staminaCurrent = staminaMax;
var staminaHealInterval;
var staminaTick = 100;
var staminHealPerTick = 10;
var $sBar3 = $(".sBar .sBarStatus.s3");
var $sBar2 = $(".sBar .sBarStatus.s2");
var $sBar1 = $(".sBar .sBarStatus.s1");
var healStamina = function() {
staminaCurrent = Math.min(staminaCurrent + staminHealPerTick, staminaMax);
var rawPct = staminaCurrent / staminaMax;
var s1Pct = (function() {
if (rawPct <= (2 / 3)) { return 0; }
return (rawPct - (2 / 3)) / (1 / 3);
})();
var s2Pct = (function() {
if (rawPct <= (1 / 3)) { return 0; }
if (rawPct >= (2 / 3)) { return 1; }
return (rawPct - (1 / 3)) / (1 / 3);
})();
var s3Pct = (function() {
if (rawPct >= (1 / 3)) { return 1; }
return (rawPct - (0 / 3)) / (1 / 3);
})();
$sBar3.css("width", 100 * s3Pct + "%");
$sBar2.css("width", 100 * s2Pct + "%");
$sBar1.css("width", 100 * s1Pct + "%");
if (staminaCurrent >= staminaMax) {
clearInterval(staminaHealInterval);
staminaHealInterval = null;
}
};
var dingStamina = function(amount) {
staminaCurrent = Math.max(staminaCurrent - amount, 0);
if (!staminaHealInterval) {
staminaHealInterval = setInterval(healStamina, staminaTick);
}
}
$('.attack').click(function() {
dingStamina(100);
});
* {
margin: 0px;
padding: 0px;
}
.hBar {
width: 400px;
height: 10px;
border: 1px solid grey;
margin-bottom: 20px;
}
.health {
background: green;
width: 100%;
height: 100%;
}
.sBar {
width: 400px;
height: 10px;
border: 1px solid grey;
margin-bottom: 20px;
position: relative;
}
.sBar .sBarStatus {
position: absolute;
width: 100%;
height: 100%;
}
.s3 {
background: red;
}
.s2 {
background: orange;
}
.s1 {
background: yellow;
}
#container {
background: white;
width: 80%;
padding: 10px;
margin: auto;
}
body {
background: #CCC;
}
.attack {
border-radius: 90px;
border: black solid 1px;
height: 75px;
width: 75px;
text-align: center;
line-height: 75px;
}
.attack:hover {
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div class="hBar">
<div class="health"></div>
</div>
<div class="sBar">
<div class="sBarStatus s3"></div>
<div class="sBarStatus s2"></div>
<div class="sBarStatus s1"></div>
</div>
<div class="attack">attack</div>
</div>
I've got a bunch of horizontal boxes containing text. The boxes are all in a horizontally scrolling container:
// generate some random data
var model = {
leftEdge: ko.observable(0)
};
model.rows = populateArray(10 + randInt(20), randRow);
ko.applyBindings(model);
$(function() {
$('.slide').on('scroll', function() {
model.leftEdge(this.scrollLeft);
})
})
function randRow() {
var events = populateArray(50 + randInt(100), randEvent);
var left = randInt(1000);
events.forEach(function(event) {
event.left = left;
left += 10 + event.width + randInt(1000);
});
return {
events: events
}
}
function randEvent() {
var word = randWord()
var width = 50 + Math.max(8 * word.length, randInt(200));
var event = {
left: 0,
width: width,
label: word
};
event.offset = ko.computed(function() {
// reposition the text to stay
// * within its container
// * fully on-screen (if possible)
var leftEdge = model.leftEdge();
return Math.max(0, Math.min(
leftEdge - event.left,
event.width - 8 * event.label.length
));
});
return event;
}
function randWord() {
var n = 2 + randInt(5);
var ret = "";
while (n-- > 0) {
ret += randElt("rmhntsk");
ret += randElt("aeiou");
}
return ret;
}
function randElt(arr) {
return arr[randInt(arr.length)];
}
function populateArray(n, populate) {
var arr = new Array(n);
for (var i = 0; i < n; i++) {
arr[i] = populate();
}
return arr;
}
function randInt(n) {
return Math.floor(Math.random() * n);
}
.slide {
max-width: 100%;
overflow: auto;
border: 5px solid black;
}
.row {
position: relative;
height: 25px;
}
.event {
position: absolute;
top: 2.5px;
border: 1px solid black;
padding: 2px;
background: #cdffff;
font-size: 14px;
font-family: monospace;
}
.event > span {
position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class="slide" data-bind="foreach: rows">
<div class="row" data-bind="foreach: events">
<div class="event" data-bind="style: { left: left+'px', width: width+'px' }"><span data-bind="text:label, style: { left: offset() + 'px' }"></div>
</div>
</div>
What I'd like to do is as the user scrolls from left-to-right, reposition the text within each box that partially overlaps the left border of the visible window to keep the text as visible as possible.
Currently I'm doing this by manually repositioning each item of text.
Is there a cleaner way to do this using CSS?
A friend helped me come up with this solution.
In English, the idea is to add an overlay to each row that is positioned relatively to the frame of the scrolling box, rather than the contents.
Then we can place a label for any box that overlaps the left edge in this overlay and it will appear to smoothly move as the box underneath it scrolls.
// generate some random data
var model = {
leftEdge: ko.observable(0),
};
model.rows = populateArray(10 + randInt(20), randRow);
model.width = Math.max.apply(Math, $.map(model.rows, function(row) {
return row.width
}));
ko.applyBindings(model);
$(function() {
$('.slide').on('scroll', function() {
model.leftEdge(this.scrollLeft);
})
})
function randRow() {
var events = populateArray(50 + randInt(100), randEvent);
var left = randInt(1000);
events.forEach(function(event) {
event.left = left;
left += 10 + event.width + randInt(1000);
});
return {
events: events,
width: left
}
}
function randEvent() {
var word = randWord()
var width = 50 + Math.max(8 * word.length, randInt(200));
var event = {
width: width,
label: word,
};
event.tense = ko.computed(function() {
// reposition the text to stay#
// * within its container
// * fully on-screen (if possible)
var leftEdge = model.leftEdge();
return ['future', 'present', 'past'][
(leftEdge >= event.left) +
(leftEdge > event.left + event.width - 8 * event.label.length)
];
});
return event;
}
function randWord() {
var n = 2 + randInt(5);
var ret = "";
while (n-- > 0) {
ret += randElt("rmhntsk");
ret += randElt("aeiou");
}
return ret;
}
function randElt(arr) {
return arr[randInt(arr.length)];
}
function populateArray(n, populate) {
var arr = new Array(n);
for (var i = 0; i < n; i++) {
arr[i] = populate();
}
return arr;
}
function randInt(n) {
return Math.floor(Math.random() * n);
}
.wrapper {
position: relative;
border: 5px solid black;
font-size: 14px;
font-family: monospace;
}
.slide {
max-width: 100%;
overflow: auto;
}
.slide > * {
height: 25px;
}
.overlay {
position: absolute;
width: 100%;
left: 0;
}
.overlay .past {
display: none
}
.overlay .present {
position: absolute;
z-index: 1;
top: 5.5px;
left 0;
}
.overlay .future {
display: none
}
.row {
position: relative;
}
.event {
position: absolute;
top: 2.5px;
border: 1px solid black;
padding: 2px;
background: #cdffff;
height: 14px;
}
.event .past {
float: right;
}
.event .present {
display: none;
}
.event .future {
float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class="wrapper">
<div class="slide" data-bind="foreach: rows, style: { width: width + 'px' }">
<div class="overlay" data-bind="foreach: events">
<span data-bind="text:label, css: tense"></span>
</div>
<div class="row" data-bind="foreach: events">
<div class="event" data-bind="style: { left: left+'px', width: width+'px' }"><span data-bind="text:label, css: tense"></div>
</div>
</div></div>
This doesn't result in less javascript, but it does result in more efficient javascript, as class changes happen much less often than offset changes, so fewer updates to DOM elements are required.
You can avoid processing every "event" (in the above example) by doing some pre-partitioning of the horizontal space, and only updating events in the relevant partition.