How to write a simple marquee effect with javascript - javascript

I need everyone's help. I currently need to implement a marquee effect. The yellow box needs to be scrolled up to show the name. Every time I scroll, I have to stay in the middle of the box for 1 second before continuing to scroll. I can find such an example on the Internet. , but the logic of this program is a bit difficult for me to understand for urban beginners. I wonder if anyone would like to provide a simpler and easier-to-understand writing method if I want to achieve this marquee effect?
​​Sorry, I am a beginner in the program, the current logic More complex programs are more difficult to understand.
function slideLine(box, stf, delay, speed, h) {
var slideBox = document.getElementById(box);
var delay = delay || 1000,
speed = speed || 20,
h = h || 40;
var tid = null,
pause = false;
var s = function() {
tid = setInterval(slide, speed);
};
var slide = function() {
if (pause) return;
slideBox.scrollTop += 1;
if (slideBox.scrollTop % h == 0) {
clearInterval(tid);
slideBox.appendChild(slideBox.getElementsByTagName(stf)[0]);
slideBox.scrollTop = 0;
setTimeout(s, delay);
}
};
setTimeout(s, delay);
}
slideLine("kanban_info", "p", 1000, 25, 40);
.kanban {
position: absolute;
margin: 0 auto;
width: 278px;
height: 50px;
background-color: yellow;
left: 50%;
transform: translate(-50%);
text-align: center;
line-height: 6;
}
.kanban .kenban_wrap {
height: 38px;
transform: translateY(28px);
overflow: hidden;
}
.kanban .kenban_wrap .kanban_info {
line-height: 38px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="kanban">
<div class="kenban_wrap" id='kanban_info'>
<p class="kanban_info">Allen</p>
<p class="kanban_info">james</p>
<p class="kanban_info">jack</p>
</div>
</div>

By combining scroll-behavior with anchor tags that are programmatically clicked you can simplify it. This should be easier to understand and you can go from there, even if it might not be the best solution.
let links = document.querySelectorAll("a"); // List of links
let div = document.querySelector("div");
let index = 0;
let t = 2000; // setTimeout duration
// Change Scroll behavior to prevent the animation from the last to first list item
function scrollBeh() {
if(index == 1) {
div.style.scrollBehavior = "auto";
t = 0; // Timeout duration to 0 to prevent `1` being shown longer than other list items
} else {
div.style.scrollBehavior = "smooth";
t = 2000;
}
}
// Loop through list items
function resetInd() {
if(index < 3) {
index++;
} else {
index = 0;
}
}
function clickLinks() {
links[index].click();
resetInd();
scrollBeh();
setTimeout(clickLinks, t);
}
setTimeout(clickLinks, t);
div {
width: 200px;
height: 100px;
background-color: darkblue;
overflow: hidden;
scroll-behavior: smooth;
}
li {
height: 100px;
list-style: none;
}
a {
text-decoration: none;
color: #FFF;
font-size: 50px;
}
<div>
<ul>
<li id="one">1</li>
<li id="two">2</li>
<li id="three">3</li>
<li id="one_loop">1</li>
</ul>
</div>

Related

How to add a smooth animation to the progress bar

When I click I want to smoothly add segments to the progress bar. They are added but instantly. What could be the problem?
I tried to implement a smooth animation with setInterval, but nothing comes out. Percentages are also added instantly.
let progressBar = document.querySelector(".progressbar");
let progressBarValue = document.querySelector(".progressbar__value");
const body = document.querySelector("body");
let progressBarStartValue = 0;
let progressBarEndValue = 100;
let speed = 50;
body.addEventListener("click", function(e) {
if (progressBarStartValue === progressBarEndValue) {
alert("you have completed all the tasks");
} else {
let progress = setInterval(() => {
if (progressBarStartValue != 100) {
progressBarStartValue += 10;
clearInterval(progress);
}
progressBarValue.textContent = `${progressBarStartValue}%`;
progressBar.style.background = `conic-gradient(
#FFF ${progressBarStartValue * 3.6}deg,
#262623 ${progressBarStartValue * 3.6}deg
)`;
}, speed);
}
});
.progressbar {
position: relative;
height: 150px;
width: 150px;
background-color: #262623;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.progressbar::before {
content: "";
position: absolute;
height: 80%;
width: 80%;
background-color: #0f0f0f;
border-radius: 50%;
}
.progressbar__value {
color: #fff;
z-index: 9;
font-size: 25px;
font-weight: 600;
}
<main class="main">
<section class="statistic">
<div class="container">
<div class="statistic__inner">
<div class="statistic__text">
<h2 class="statistic__title">You're almost there!</h2>
<p class="statistic__subtitle">keep up the good work</p>
</div>
<div class="progressbar"><span class="progressbar__value">0%</span></div>
</div>
</div>
</section>
</main>
This may not be exactly what you're looking for, but with the conic-gradient() implementation you're using, I'd recommend checking out a library call anime.js.
Here's an example with your implementation (same html and css):
// your.js
let progressBar = document.querySelector(".progressbar");
let progressBarValue = document.querySelector(".progressbar__value");
const body = document.querySelector("body");
// Switched to object for target in anime()
let progressBarObject = {
progressBarStartValue: 0,
progressBarEndValue: 100,
progressBarAnimationValue: 0 * 3.6 // New value needed for smoothing the progress bar, since the progress value needs to be multiplied by 3.6
}
// Not necessary, but I recommend changing the event listener to pointerup for better support
// Also not necessary, I changed function to arrow function for my own preference
body.addEventListener("pointerup", e => {
e.preventDefault()
if (progressBarObject.progressBarStartValue === progressBarObject.progressBarEndValue) {
alert("you have completed all the tasks");
} else {
let newValue = 0 // Needed so we can set the value, before it's applied in anime()
if (progressBarObject.progressBarStartValue != 100) {
// Math.ceil() allows us to round to the nearest 10 to guarantee the correct output
newValue = Math.ceil((progressBarObject.progressBarStartValue + 10) / 10) * 10;
}
// Optional: Prevents accidentally going over 100 somehow
if (newValue > 100) {
newValue = 100
}
anime({
targets: progressBarObject,
progressBarStartValue: newValue,
progressBarAnimationValue: newValue * 3.6,
easing: 'easeInOutExpo',
round: 1, // Rounds to nearest 1 so you don't have 0.3339...% displayed in progressBarValue
update: () => {
progressBar.style.backgroundImage = `conic-gradient(
#FFF ${progressBarObject.progressBarAnimationValue}deg,
#262623 ${progressBarObject.progressBarAnimationValue}deg)`;
progressBarValue.textContent = `${progressBarObject.progressBarStartValue}%`;
},
duration: 500
});
}
});
Here's a CodePen using the anime.js CDN: Circular Progress Bar Smoothing
If you don't want to use a javascript library, then I'd recommend switching from the conic-gradient() to something else. I hear using an .svg circle with stroke and stroke-dasharray can work great with CSS transition.
You shouldn't setInterval your progress variable like this. instead, put it as a global variable outside the function then use it to gradually add 1 as long as the start value is less than progress, and you still can control the speed with your speed variable.
let progressBar = document.querySelector(".progressbar");
let progressBarValue = document.querySelector(".progressbar__value");
const body = document.querySelector("body");
let progressBarStartValue = 0;
let progressBarEndValue = 100;
let speed = 50;
let progress = 0;
body.addEventListener("click", function(e) {
if (progressBarStartValue === progressBarEndValue) {
alert("you have completed all the tasks");
} else {
progress += 10;
setInterval(() => {
if (progressBarStartValue < progress) {
progressBarStartValue += 1;
clearInterval();
}
progressBarValue.textContent = `${progressBarStartValue}%`;
progressBar.style.background = `conic-gradient(
#FFF ${progressBarStartValue * 3.6}deg,
#262623 ${progressBarStartValue * 3.6}deg
)`;
}, speed);
}
});
.progressbar {
position: relative;
height: 150px;
width: 150px;
background-color: #262623;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 3px solid red;
}
.progressbar::before {
content: "";
position: absolute;
height: 80%;
width: 80%;
background-color: #0f0f0f;
border-radius: 50%;
border: 3px solid blue;
}
.progressbar__value {
color: #fff;
z-index: 9;
font-size: 25px;
font-weight: 600;
}
<main class="main">
<section class="statistic">
<div class="container">
<div class="statistic__inner">
<div class="statistic__text">
<h2 class="statistic__title">You're almost there!</h2>
<p class="statistic__subtitle">keep up the good work</p>
</div>
<div class="progressbar"><span class="progressbar__value">0%</span></div>
</div>
</div>
</section>
</main>

How do I fix my code such that the linear interpolation doesn't cross over?

I'm trying to make an infinite marquee (scrolling horizontal text), but on scroll it speeds up the translation of the HTML elements. I am achieving this effect by using a linear interpolation function.
You can see the effect on this site that I'm trying to remake: https://altsdigital.com/ It says "Not your usual SEO agency"
Mine almost works - the problem is that when my HTML resets its position - my text overlaps and briefly translates to the left before correcting. Keep your eyes on the left side of the page. You will see that the text overlaps at one brief moment then translates left (during it's movement to the right), it eventually corrects itself as it plays retaining the original gap. You can see in this screenshot the "t" and "I" are overlapping. Shortly after this, the text on the left translates left and there is a gap between the letters. I want it to have a gap and not briefly translate left.
I have no idea how to fix this - I've tried calling the lerp function on scroll but nothing seems to change. Thanks in advance.
Here's the code:
const lerp = (current, target, factor) => {
let holder = current * (1 - factor) + target * factor;
holder = parseFloat(holder).toFixed(3);
return holder;
};
class LoopingText {
constructor(DOMElements) {
this.DOMElements = DOMElements;
this.lerpingData = {
counterOne: { current: 0, target: 0 },
counterTwo: { current: 100, target: 100 },
};
this.interpolationFactor = 0.1;
this.direction = true;
this.speed = 0.2;
this.render();
this.onScroll();
}
onScroll() {
window.addEventListener("scroll", () => {
this.lerpingData["counterOne"].target += this.speed * 5;
this.lerpingData["counterTwo"].target += this.speed * 5;
});
}
lerp() {
for (const counter in this.lerpingData) {
this.lerpingData[counter].current = lerp(
this.lerpingData[counter].current,
this.lerpingData[counter].target,
this.interpolationFactor
);
}
this.lerpingData["counterOne"].target += this.speed;
this.lerpingData["counterTwo"].target += this.speed;
if (this.lerpingData["counterOne"].target < 100) {
this.DOMElements[0].style.transform = `translate(${this.lerpingData["counterOne"].current}%, 0%)`;
} else {
this.lerpingData["counterOne"].current = -100;
this.lerpingData["counterOne"].target = -100;
}
if (this.lerpingData["counterTwo"].target < 100) {
this.DOMElements[1].style.transform = `translate(${this.lerpingData["counterTwo"].current}%, 0%)`;
} else {
this.lerpingData["counterTwo"].current = -100;
this.lerpingData["counterTwo"].target = -100;
}
}
render() {
this.lerp();
window.requestAnimationFrame(() => this.render());
}
}
let textArray = document.getElementsByClassName("item");
new LoopingText(textArray);
#import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght#0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Poppins";
}
.hero-section {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
overflow: hidden;
position: relative;
width: 100%;
}
.loop-container {
position: relative;
width: 100%;
display: flex;
/* padding-right: 24px; */
}
.item {
position: absolute;
font-size: 15rem;
white-space: nowrap;
margin: 0;
}
span {
transition: all 0.2s;
cursor: default;
}
.hover:hover {
color: gray;
transition: all 0.2s;
}
<body>
<section class="hero-section">
<div class="loop-container">
<div class="item">Infinite Horizontal Looping Text</div>
<div class="item">Infinite Horizontal Looping Text</div>
</div>
</section>
<section class="hero-section">
</section>
</body>

Mobile browsers add a gap at the end of final element via css gap property

I'm back on Stack Overflow after a long time because I'm truly stuck at an issue I cannot get around even after hours piling up in front of the screen.
I have made a simple widget using CSS + HTML + JavaScript which scrolls elements in an overflowing-x container.
It works in a simple way, there is JavaScript code that adds a 205 value to the property scrollLeft of the overflowing container. The number comes from the fixed width of the images + the gap value which is 5px. Here is the code:
HTML:
<div id="controlContainer">
<a class="adButton" onclick="Scroll(-1)">❮</a>
<div id="topics">
<div class="adItem" onclick="ChangeTopic(1)">
<p>History</p>
<img src="images/other_samples/hundredgates.jpg">
</div>
<div class="adItem" onclick="ChangeTopic(2)">
<p>Oceans</p>
<img src="images/other_samples/goldensea.jpg">
</div>
<div class="adItem" onclick="ChangeTopic(3)">
<p>Sports</p>
<img src="images/other_samples/kite_surf.jpg">
</div>
<div class="adItem" onclick="ChangeTopic(4)">
<p>Travel</p>
<img src="images/other_samples/antiparos_church.jpg">
</div>
<div class="adItem" onclick="ChangeTopic(5)">
<p>Nightlife</p>
<img src="images/other_samples/nightlife.png">
</div>
</div>
<a class="adButton" onclick="Scroll(1)">❯</a>
</div>
CSS:
#controlContainer {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 20px;
}
#topics {
display: inherit;
gap: 5px;
overflow:hidden;
white-space: nowrap;
scroll-behavior: smooth;
}
.adItem {
position: relative;
}
.adItem img {
width: 200px;
height: 200px;
object-fit: cover;
border-radius: 20px;
}
.adItem p {
position: absolute;
left: 16px;
top: 8px;
text-align: center;
color: #ffff;
font-family: Georgia, "Times New Roman", Times, serif;
font-size: 50px;
margin: 0px;
user-select: none;
pointer: default:
}
And finally JS, which still needs some work tbh:
var LastClick;
var Delay = 300;
var SelectedElement;
var adControl;
var currentScroll;
window.onload = function () {SelectedElement = document.getElementById("ad1"); adControl = document.getElementById("topics"); resizeController();};
window.onresize = debounce(() => resizeController());; //resize the container when the screen does
//window.addEventListener('DOMContentLoaded', (event) => {SelectedElement = document.getElementById("ad1")});
function Scroll(n) {
if (LastClick >= (Date.now() - Delay)) {
return;
}
if (n == 1) {
adControl.scrollLeft += 205;
checkPos();
} else if (n == -1) {
adControl.scrollLeft -= 205;
checkPos();
}
LastClick = Date.now();
console.log(adControl.scrollLeft);
}; // This function is what's handling scrolling. THey are called via onclick events on the HTML Button elements
function checkPos() {
var elementWidth = adControl.scrollLeft;
if (elementWidth % 5 === 0) {
// do nothing
} else {
var newWidth = Math.ceil(elementWidth/5)*5;
console.log("old width: %s, new width: %s", elementWidth, newWidth)
adControl.scrollLeft = newWidth;
}
}; //Some position checks... it basically calculates if scrollLeft is divisible by 5, because all images are 200px long plus the 5px gap, so that should always be a multiple of 5.
function ChangeTopic(id) {
SelectedElement.style.display = "none";
SelectedElement = document.getElementById("ad" + id);
SelectedElement.style.display = "flex";
}; //That just changes the topic of another element.
function debounce(func, timeout = 1000){
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => { func.apply(this, args); }, timeout);
};
}; //This is a debounce function for the resize event, it prevents it from firing it too much.
function resizeController() {
adControl.style.maxWidth = "";
var elementWidth = adControl.offsetWidth;
var scroll = adControl.ScrollLeft;
var itemNo = (Math.floor(elementWidth / 200))
if (itemNo > 3) {
itemNo = 3
};
var newWidth = (itemNo*200);
newWidth = newWidth+(5*itemNo)
adControl.style.maxWidth = (newWidth + "px");
if (currentNo = itemNo) {
adControl.scrollLeft = scroll;
}
}; //resizes the container if need be (for mobile or tablet devices)
It actually works very well on Desktop, but on mobile, the CSS gap property which adds the gap between the images also adds a gap at the last element, like this:
That's even when I use a different browser from Firefox, like Chrome
On desktop, this gap does not exist, regardless of browser once again:
What is this? And how can I solve it? The main problem this causes is it will scroll in that tiny 5 gap space, which throws the position of my elements out of place, making them look like this:
I've thought of different methods like checking the property of ScrollLeft to detect when the view is out of the elements, but that property is completely unpredictable. For instance, when I scroll to the beginning of the element, it's not going to be necessarily zero, and even if I reach the end, the 205 value will be added even if there is not any space on the container. So that isn't reliable.
In short, I'd either need some kind of method to keep that gapping behaviour in check or solve the root problem altogether.
Yes... I'm not using any framework at all, my entire project is built on pure JavaScript. I'm not sure why I did this to myself, but oh well, all the challenge I guess.
Try and resize your font on the paragraph elements in your
div class="adItem" it appears to be overlapping the container and causing what would appear to be extra padding and i don't think it's happening on the others because the text is not long enough on others.
var LastClick;
var Delay = 300;
var SelectedElement;
var adControl;
var currentScroll;
window.onload = function () {SelectedElement = document.getElementById("ad1"); adControl = document.getElementById("topics"); resizeController();};
window.onresize = debounce(() => resizeController());; //resize the container when the screen does
//window.addEventListener('DOMContentLoaded', (event) => {SelectedElement = document.getElementById("ad1")});
function Scroll(n) {
if (LastClick >= (Date.now() - Delay)) {
return;
}
if (n == 1) {
adControl.scrollLeft += 205;
checkPos();
} else if (n == -1) {
adControl.scrollLeft -= 205;
checkPos();
}
LastClick = Date.now();
console.log(adControl.scrollLeft);
}; // This function is what's handling scrolling. THey are called via onclick events on the HTML Button elements
function checkPos() {
var elementWidth = adControl.scrollLeft;
if (elementWidth % 5 === 0) {
// do nothing
} else {
var newWidth = Math.ceil(elementWidth/5)*5;
console.log("old width: %s, new width: %s", elementWidth, newWidth)
adControl.scrollLeft = newWidth;
}
}; //Some position checks... it basically calculates if scrollLeft is divisible by 5, because all images are 200px long plus the 5px gap, so that should always be a multiple of 5.
function ChangeTopic(id) {
SelectedElement.style.display = "none";
SelectedElement = document.getElementById("ad" + id);
SelectedElement.style.display = "flex";
}; //That just changes the topic of another element.
function debounce(func, timeout = 1000){
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => { func.apply(this, args); }, timeout);
};
}; //This is a debounce function for the resize event, it prevents it from firing it too much.
function resizeController() {
adControl.style.maxWidth = "";
var elementWidth = adControl.offsetWidth;
var scroll = adControl.ScrollLeft;
var itemNo = (Math.floor(elementWidth / 200))
if (itemNo > 3) {
itemNo = 3
};
var newWidth = (itemNo*200);
newWidth = newWidth+(5*itemNo)
adControl.style.maxWidth = (newWidth + "px");
if (currentNo = itemNo) {
adControl.scrollLeft = scroll;
}
}; //resizes the container if need be (for mobile or tablet devices)
#controlContainer {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 20px;
}
#topics {
display: inherit;
gap: 5px;
overflow:hidden;
white-space: nowrap;
scroll-behavior: smooth;
}
.adItem {
position: relative;
}
.adItem img {
width: 200px;
height: 200px;
object-fit: cover;
border-radius: 20px;
}
.adItem p {
position: absolute;
left: 16px;
top: 8px;
text-align: center;
color: #ffff;
font-family: Georgia, "Times New Roman", Times, serif;
font-size: 50px;
margin: 0px;
user-select: none;
pointer: default:
}
<div id="controlContainer">
<a class="adButton" onclick="Scroll(-1)">❮</a>
<div id="topics">
<div class="adItem" onclick="ChangeTopic(1)">
<p>History</p>
<img src="images/other_samples/hundredgates.jpg">
</div>
<div class="adItem" onclick="ChangeTopic(2)">
<p>Oceans</p>
<img src="images/other_samples/goldensea.jpg">
</div>
<div class="adItem" onclick="ChangeTopic(3)">
<p>Sports</p>
<img src="images/other_samples/kite_surf.jpg">
</div>
<div class="adItem" onclick="ChangeTopic(4)">
<p>Travel</p>
<img src="images/other_samples/antiparos_church.jpg">
</div>
<div class="adItem" onclick="ChangeTopic(5)">
<p>Nightlife</p>
<img src="images/other_samples/nightlife.png">
</div>
</div>
<a class="adButton" onclick="Scroll(1)">❯</a>
</div>

JavaScript Firefox slow animation

For demonstration purposes provided example I tried to implement animation in which one div follows actual scrollbar in order to get as many changes as possible.
However, animation works really nice on Chrome, pretty well even in IE but it is extremely laggy in Firefox.
I tried with some hardware acceleration tricks like translateZ(0), etc, but with no success for Firefox.
Instead of top I tried to use transform, again, no luck.
Can I expect better performance if I try canvas?
(function () {
var scrollerElement = document.getElementById('scroller');
var elementToMove = document.getElementById('toMove');
var scrollerSize = scrollerElement.clientHeight;
var maxScroll = scrollerElement.scrollHeight - scrollerSize;
var whileAnimate;
var timeout;
var delay = 200;
scrollerElement.addEventListener('scroll', function (event) {
clearTimeout(timeout);
timeout = setTimeout(cancelAnimation, delay);
if (whileAnimate) {
return;
}
startAnimation();
});
function cancelAnimation() {
whileAnimate = false;
}
function startAnimation() {
var animationFrame;
whileAnimate = true;
animate();
function animate() {
if (!whileAnimate) {
cancelAnimationFrame(animationFrame);
return;
}
elementToMove.style.top = (scrollerElement.scrollTop / maxScroll * scrollerSize) + 'px';
animationFrame = requestAnimationFrame(animate);
}
}
})();
body {
margin: 0;
padding: 0;
}
#scroller {
width: 500px;
height: 500px;
border: 1px solid black;
overflow: auto;
}
#content {
width: 500px;
height: 20000px;
}
#toMove {
position: absolute;
top: 0;
width: 20px;
height: 20px;
background: red;
}
<div id="scroller">
<div id="toMove"></div>
<div id="content"></div>
</div>

Animation within animation

I have animation that works like this:
var words_array = [];
words_array[0] = ['FUN', 'CREATIVE', 'INNOVATIVE'];
words_array[1] = ['WEB', 'WORLD'];
var words = ['We are <span class="words" style="background:#F33B65; font-weight:bold; padding: 0 10px;">FUN</span>',
'We like the <span class="words" style="background:#8be32d; font-weight:bold; padding: 0 10px;">WEB</span>'
];
$('#caption').html(words[0]);
var i = 0;
setInterval(function() {
$('#caption').animate({
width: 'toggle'
}, {
duration: 400,
done: function() {
$('#caption').html(words[i = (i + 1) % words.length]);
}
}).delay(300).animate({
width: 'toggle'
}, 400);
}, 5000);
body {
background: #333;
}
#caption {
height: 200px;
font-size: 80px;
line-height: 100px;
color: #fff;
overflow: hidden;
vertical-align: top;
white-space: nowrap;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id="caption"></div>
Every 5 seconds you get the toggle change of the words array. What I'd like to create, but I'm failing, is to have the toggle, then change few words in the .words span that are located in the words_array, and then after I've changed all the words, the toggle will happen, to the second sentence in the words array, and now I'll change the .words with the associated words_array and so on (if I have more sentences/words).
So the animation goes like this:
First 'slide': We are FUN
CREATIVE <- only this changes
INNOVATIVE
Slide toggle to second 'slide': We like the WEB
WORLD
And I could add as much words/slides as I want.
Doing one (just changing the words) or the other (sliding the sentence) is rather easy, but combining them is where I am stuck :\
EDIT:
I using the solution provided I tweaked the code a bit:
var words_array = [];
words_array[0] = ['FUN', 'CREATIVE', 'INNOVATIVE'];
words_array[1] = ['WEB', 'WORLD'];
var words = ['We are <span class="words" style="background:#F33B65; font-weight:bold; padding: 0 10px;">FUN</span>',
'We like the <span class="words" style="background:#8be32d; font-weight:bold; padding: 0 10px;">WEB</span>'
];
var $caption = $('#caption'),
i = 1,
w = 0,
$replace = $caption.find('.words');
function switchSentence() {
$caption.animate({
width: 'toggle'
}, {
duration: 400,
done: function() {
i = (i + 1) % words.length;
w = 0;
$caption.html(words[i]);
$replace = $caption.find('.words');
}
}).delay(300).animate({
width: 'toggle'
}, 400).delay(300);
}
switchSentence();
function switchWord() {
if (w >= words_array[i].length - 1) {
switchSentence();
w = 0;
} else {
w += 1;
}
if (words_array[i]) {
$replace.animate({
width: 'toggle'
}, {
duration: 400,
done: function() {
$replace.text(words_array[i][w]);
}
}).delay(300).animate({
width: 'toggle'
}, 400);
}
}
switchWord();
setInterval(switchWord, 2500);
body {
background: #333;
}
#caption {
height: 200px;
font-size: 80px;
line-height: 100px;
color: #fff;
overflow: hidden;
white-space: nowrap;
display: inline-block;
vertical-align: top;
}
.words {
display: inline-block;
vertical-align: top;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="caption"></div>
Added another animation in the words toggle. Thanks somethinghere for all the help!!
How about adding another timeout that will simply loop through the current available words? When you switch the arrays, simple reset the loop and let it check the correct amount of words. Notice in the snippet how the function switchSentence and switchWords are entirely unrelated. The switchWords function makes use of the currently selected sentence, and the swicthSentence function does the changing of the sentence, as the name suggests. This way you don't really have to know how to align them properly, they will do their job regardless. Have a look at the snippet:
var words_array = [
['FUN', 'CREATIVE', 'INNOVATIVE'],
['WEB', 'WORLD']
];
var words = [
'We are <span class="words fun">FUN</span>',
'We like the <span class="words like">WEB</span>'
];
var caption = $('#caption'),
i = 1,
w = 0,
replace = caption.find('span');
function switchSentence() {
caption.animate({width: 'toggle'},{
duration: 400,
done: function() {
i = (i + 1) % words.length;
w = 0;
caption.html(words[i]);
replace = caption.find('span');
}
}).delay(300).animate({width: 'toggle'}, 400);
}
switchSentence();
setInterval(switchSentence, 5000);
function switchWord(){
if(w >= words_array[i].length - 1) w = 0;
else w += 1;
if(words_array[i]) replace.text(words_array[i][w])
}
switchWord();
setInterval(switchWord, 500);
body {
background: #333;
}
#caption {
height: 200px;
font-size: 80px;
line-height: 100px;
color: #fff;
overflow: hidden;
vertical-align: top;
white-space: nowrap;
}
.fun, .like { font-weight: bold; }
.fun { background: #F33B65; }
.like { background: #8be32d; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id="caption"></div>
I also decided to clean up your code a bit to make it more legible and useable. I moved the two switching functions into separate functions and passed them to the interval listeners separately. This is so I could immediately kickstart them by calling them once myself. I also streamlined your array, and moved the style declaration into your CSS instead of inline styles (which makes both your JS and CSS look a lot cleaner).

Categories