Shuffling Text Animation with CSS/JS? - javascript

I want to display the word 'Hello' on the home page of a website. I used CSS to make the 'Hello' transition up as the page loads in the beginning. I would like to implement a shuffling animation that randomly shuffles between the word Hello in different languages. I would like to do so with an animation where as the 'Hello' slides up at the beginning, the 'Hello' slides up more, fades out and disappears. As this occurs, a 'Bonjour' for example slides up from beneath and takes place. I picture this repeating forever.
Is there any way to implement such animation using CSS, JavaScript, Jquery, or any other web tools? Below is the HTML, CSS, and JS structure I have that only achieves the initial transition as the page loads:
<body>
<section>
<h1 id="switch">Hello</h1>
</section>
</body>
section {
text-align: left;
}
section h1 {
font-size: 100px;
font-weight: 420;
position: absolute;
top: 130px;
left: 200px;
opacity: 0;
transform: translateY( 43px );
animation-name: fade-in;
animation-duration: 0.6s;
animation-timing-function: ease-out;
animation-fill-mode: forwards;
}
var currentIndex = 0;
var hello = new Array( 'Hello', 'Bonjour', 'Hola' );
function randomIndex( ) {
return Math.floor( Math.random( ) * hello.length);
};
window.setInterval( function( ) {
var newIndex = randomIndex( );
while( newIndex === currentIndex ) newIndex = randomIndex();
currentIndex = newIndex;
document.getElementById("switch").textContent = hello[ currentIndex ];
}, 2300 );

In CSS you need to set up #keyframes for your fade-in animation,. Then you can add a percentage of the duration that you wish to animate the animate-able properties opacity and top position. Make sure your duration matches the setInterval time => 2300 => 2.3s.
#keyframes:
In my example I set up a tween that will start at 0% with opacity 0 and top position in vh lengths, then as the tween reaches 70%, it is shown moving upwards to 5vh, where it will stay at an opacity of 1 until 90%, when its opacity will start to fade out. At 100% it will be opacity of 0, then the loop starts over as it is set to infinte in the css animation, the element will reset to 20vh and the animation repeats itself over again.
*You can play around with the percentages in the #keyframes rule to get the effect you're look for in terms of fading in and out movement, etc...
let currentIndex = 0;
const hello = ['Hello', 'Bonjour', 'Hola'];
function randomIndex() {
return ~~(Math.random() * hello.length);
};
window.setInterval(function() {
let newIndex = randomIndex();
while (newIndex === currentIndex) newIndex = randomIndex();
currentIndex = newIndex;
document.getElementById("switch").textContent = hello[currentIndex];
}, 2300);
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
section {
text-align: center;
}
section h1 {
font-size: 100px;
font-weight: 420;
position: absolute;
top: 5vh;
left: 50vh;
opacity: 0;
animation: fade-in 2.3s ease-in-out forwards infinite;
}
#keyframes fade-in {
0% {
opacity: 0;
top: 20vh;
}
70%,
90% {
opacity: 1;
top: 5vh;
}
100% {
opacity: 0;
top: 5vh;
}
}
<body>
<section>
<h1 id="switch">Hello</h1>
</section>
</body>

As the greeting is not really semantic (you don't for example want it read out every few seconds to a screen reader) you could put it instead on the body (or another suitable element, depending on exactly the structure you want) in a pseudo before element. That way it is basically decoration, not meaning.
Also, to avoid timing issues, where a setInterval may get out of step with the timing of the keyframes animation, you can sense the animationend event and then set a timout for the next 300ms and then reset the animation to run again.
let currentIndex = 0;
const hello = ['Hello', 'Bonjour', 'Hola'];
function randomIndex() {
return ~~(Math.random() * hello.length);
};
function getNext() {
let newIndex = randomIndex();
while (newIndex === currentIndex) newIndex = randomIndex();
currentIndex = newIndex;
document.body.style.setProperty('--greeting', "'" + hello[currentIndex] + "'");
document.body.style.setProperty('--animationname', 'none');
setTimeout(function () {
document.body.style.setProperty('--animationname', 'move');
}, 300);
}
document.body.addEventListener('animationend',getNext);
body {
--greeting: 'Hello';
--animationname: move;
}
body::before {
content: var(--greeting);
animation-duration: 2s;
animation-iteration-count: 1;
animation-name: var(--animationname);
position: absolute;
top: 100%;
font-size: 100px;
font-weight: 420;
position: absolute;
left: 200px;
animation-timing-function: linear;
animation-fill-mode: forwards;
}
#keyframes move {
0% {
top: 100%;
opacity: 0;
}
50% {
top: 50%;
opacity: 1;
}
100% {
top: 0%;
opacity: 0;
}
}
Obviously you'll want to change the timings, positionings etc to be exactly what you want.

Related

How to make an image fade in and another fade out with one button click?

I'm trying to fade in the blue square with the first click of the button. And then on the second click, the blue square fades out and the red one fades in.
As you can see when you test it, it doesn't work that way. I don't know where I am wrong and If anyone can show me how to fix it I'd appreciate it.
var currentscene = 0;
function next() {
currentscene++;
if (currentscene = 1) {
var element = document.getElementById("blue");
element.classList.add("fade-in");
}
if (currentscene = 2) {
var element = document.getElementById("blue");
element.classList.add("fade-out");
var element = document.getElementById("red");
element.classList.add("fade-in");
}
}
.squareblue {
height: 50px;
width: 50px;
top: 50px;
background-color: blue;
position: absolute;
opacity: 0;
animation-fill-mode: forwards;
}
.squarered {
height: 50px;
width: 50px;
top: 100px;
background-color: red;
position: absolute;
opacity: 0;
animation-fill-mode: forwards;
}
.fade-out {
animation: fadeOut ease 2s
}
#keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.fade-in {
animation: fadeIn ease 2s
}
#keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
<div2 id="blue" class="squareblue"></div2>
<div2 id="red" class="squarered"></div2>
<button class="button" onclick="next()">next</button>
A few mistakes, and a few things to improve.
Inside your if conditionals, you were assigning the value of 1 and 2 to the variable currentscene instead of using the comparison operator ==. I added the remainder operator to be able to continue the loop indefinitely.
Instead of grabbing the element from the dom each loop, I just defined the elements at the top, and continued to reference the save variable.
instead of using a css keyframes animation, I used the css transition property to add animation to the changing of opacity.
If you have any questions, please ask 🚀
let currentscene = 0;
const blue = document.getElementById("blue");;
const red = document.getElementById("red");;
function next() {
currentscene++;
if (currentscene % 2 == 0) {
blue.classList.remove("opaque");
red.classList.add("opaque");
}
else if (currentscene % 2 == 1) {
red.classList.remove("opaque");
blue.classList.add("opaque");
}
}
.squareblue,
.squarered {
height: 50px;
width: 50px;
position: absolute;
opacity: 0;
animation-fill-mode: forwards;
transition: 1s;
}
.squareblue {
top: 50px;
background-color: blue;
}
.squarered {
top: 100px;
background-color: red;
}
.opaque {
opacity: 1;
}
button {user-select: none}
<div2 id="blue" class="squareblue"></div2>
<div2 id="red" class="squarered"></div2>
<button class="button" onclick="next()">next</button>

Create "bobblehead" animation jquery

A bobblehead effect would be a "U" animation shape in my mind with slightly shorter stems.
I've tried using various arcs/semi-circles to create a bobblehead effect but nothing is working correctly.
I must use transform with translate due to it being an SVG. I am also using animejs but I cannot see a method to achieve this on that library either. jQuery animation steps seems the most simple?
This is the effect I'm looking to achieve:
[![enter image description here][1]][1]
Using this code:
function loopBobble() {
var end = 180;
$({
counter: 0
}).animate({
counter: end
},{
duration: 1000,
easing: "swing",
step: function(t, fx) {
var a = t / 60; // from degrees to radians
var x = Math.cos(a) * 10;
var y = Math.sin(a) * 10;
$('#bobble').attr('style', 'transform: translateX(' + x + 'px) translateY(' + y + 'px);');
if (t == end) {
loopBobble();
}
}
});
}
loopBobble();
The best I am able to achieve with the creepy face is this result:
[![enter image description here][2]][2]
Is my approach correct? I would have assumed a "U" shape animation would be built into animejs or jquery. I cannot find much online. I am no mathematician
How about css only?
.head {
background-color: #FA0;
border-radius: 50%;
width: 100px;
height: 100px;
left: 0px;
top: 0px;
position: absolute;
animation-name: xOffset;
animation-duration: 1s;
animation:
xOffset 1s ease-in-out infinite,
yOffset .5s ease-in-out infinite;
}
#keyframes xOffset {
50% { left: 50px; }
100% { left: 0px; }
}
#keyframes yOffset {
50% { top: 25px; }
100% { top: 0px; }
}
<div class="head"></div>
transform: translate-Version
You'll have to add a wrapper in your csv to apply separated easing-times on x and y. Otherwise different easing-times are not possible using transform since transform is animated as a whole.
.head {
background-color: #FA0;
border-radius: 50%;
width: 100px;
height: 100px;
animation-name: xOffset;
animation-duration: 1s;
animation: xOffset 1s ease-in-out infinite;
}
.wrapper {
animation: yOffset .5s ease-in-out infinite;
}
#keyframes xOffset {
50% { transform: translateX(50px); }
100% { transform: translateX(0px); }
}
#keyframes yOffset {
50% { transform: translateY(25px); }
100% { transform: translateY(0px); }
}
<div class="wrapper">
<div class="head"></div>
</div>

How to stop the animation in the middle of it properly

Here is a simple pulsating animation using CSS keyframes.
The problem is if I want to discard or remove the animation in the middle of it, the animation stops with a jerking movement (a sudden jump to its initial state as you can see in the snippet).
I want the animation to go back to its initial state smoothly even if you stop it in the middle of the animation.
JQuery and TweenMax are accepted.
Is there any way to do that?
let test = document.getElementById("test");
setTimeout(function(){
test.classList.add("addAniamte");
}, 2000)
setTimeout(function(){
test.classList.remove("addAniamte");
test.classList.add("removeAniamte");
console.log('stoping aniamtion!');
}, 4500)
#-webkit-keyframes pulse {
0% {
-webkit-transform: scale(1, 1);
}
50% {
-webkit-transform: scale(3, 3);
}
100% {
-webkit-transform: scale(1, 1);
};
}
#test {
background: red;
width: 20px;
height: 20px;
margin: 60px;
}
.addAniamte{
-webkit-animation: pulse 1s linear infinite;
animation: pulse 1s linear infinite;
}
.removeAniamte{
-webkit-animation: none;
animation:none;
}
<div id="test"></div>
This is quite easy to do with GSAP! Here's how to do something like this in GSAP 3.
var tween = gsap.from("#test", {duration: 1, scale: 0.33, repeat: -1, yoyo: true, paused: true});
function startAnimation() {
tween.play();
}
function stopAnimation() {
tween.pause();
gsap.to(tween, {duration: 0.5, progress: 0});
}
#test {
background: red;
width: 60px;
height: 60px;
margin: 60px;
}
<div id="test"></div>
<button onclick="startAnimation()">Start</button>
<button onclick="stopAnimation()">Stop</button>
<script src="https://cdn.jsdelivr.net/npm/gsap#3.0.4/dist/gsap.min.js"></script>
Without GSAP (or at least a JS-only approach), it's incredibly messy and error prone as the other answer shows. In fact, I had my own question which lead to a CSS-Tricks article on the subject.
I have tried to make the javascript completely dynamic by using getComputedStyle. To stop animation smoothly, we need to have a watcher variable, which will count the duration of each animation cycle. (tempAnimationWatcher).
Add animation start event to element.
Calculate single animation duration (testAnimationTime) and initiate tempAnimationWatcherInterval to watch animation cycle time tempAnimationWatcher
if stopAnimation, stop animation after remaining css time. (testAnimationTime - tempAnimationWatcher)
NOTE: the testAnimationTime calculations are based on consideration that the css animation time is written in seconds. see line testAnimationTime = parseInt(animationDurationInCss.substring(0, animationDurationInCss.length - 1)) * 1000;, this is removing last char and converting it in milliseconds.
const test = document.getElementById("test");
let tempAnimationWatcher = 0;
let testAnimationTime = 0;
let tempAnimationWatcherInterval;
test.addEventListener('animationstart', function (e) {
console.log('animation starts')
const animationDurationInCss = getComputedStyle(test).animationDuration;
testAnimationTime = parseInt(animationDurationInCss.substring(0, animationDurationInCss.length - 1)) * 1000;
tempAnimationWatcher = 0;
tempAnimationWatcherInterval = setInterval(() => {
tempAnimationWatcher += 10;
if (tempAnimationWatcher >= testAnimationTime) tempAnimationWatcher = 0;
}, 10);
});
function startAnimation() {
test.classList.add("addAniamte");
}
function stopAnimation() {
clearInterval(tempAnimationWatcherInterval);
setTimeout(() => {
test.classList.remove("addAniamte");
console.log("stoping aniamtion!");
}, (testAnimationTime - tempAnimationWatcher));
}
startAnimation();
#keyframes pulse {
0% {
-webkit-transform: scale(1, 1);
}
50% {
-webkit-transform: scale(3, 3);
}
100% {
-webkit-transform: scale(1, 1);
}
;
}
#test {
background: red;
width: 20px;
height: 20px;
margin: 60px;
}
.addAniamte {
animation: pulse 2s linear infinite;
}
<div id="test"></div>
<hr>
<button onclick="startAnimation()">Start</button>
<button onclick="stopAnimation()">Stop</button>

Vanilla JavaScript, interact with a single DOM element which is part of a class group without affecting the others of the group

I'm trying to create a number of items (clouds in this case) that once reached the rightmost side of the browser, thir position is reset to 0 (leftmost side of the screen) or less and the loop should continue.
Each cloud's position should be reset independently from the others, but in my case what is happening is that everytime a cloud reaches the target, they all get reset and I can't figure out why.
I have a series of DOM elements:
<div class="cloud" id="c1"></div>
<div class="cloud" id="c2"></div>
<div class="cloud" id="c3"></div>
In my JS file I have an array containing all 3 DOM elements:
var clouds = document.querySelectorAll(".cloud");
I have first loop to setup some css attributes:
for(let i=0; i < clouds.length; ++i){
setUp(clouds[i]);
}
function setUp(item){
item.style.transform = `translate3d(0px, 0px, 0px)`;
}
And then I loop through it running this method:
function increment(item){
let xVal = Number(item.style.transform.split('px')[0].split('(')[1]);
let newVal = xVal >= window.innerWidth ? 0 : xVal + 1;
item.style.transform = `translate3d(${newVal}px, 0px, 0px)`;
item.style.background = 'red';
}
setInterval(function(){
clouds.forEach(increment);
},700)
CORRECTION:
As pointed out by #AlexWayne, it was a minor issue that was solved by separating the positions of the items in the setUp function.
In the form of:
item.style.transform = `translate3d(${i * 100}px, 0px, 0px)`;
as shown here https://jsfiddle.net/36m1oatv/14/ .
Although it is strange the reason why a similar version as the following
https://jsfiddle.net/kwucnht9/1/ doesn't work.
Hello and welcome to stackoverflow!
I figured you might like a pure CSS solution since most things you are doing in your javascript was manipulating the css anyway.
Let me know if you need further assistance.
#keyframes example {
0% {
transform: translateX(0);
}
100% {
transform: translateX(100vh);
}
}
.cloud {
background-color: grey;
width: 50px;
height: 50px;
margin: 10px;
transition: transform .7s ease-in-out;
animation-name: example;
animation-duration: 4s;
animation-iteration-count: infinite;
}
.cloud:nth-child(1) {
background-color: tomato;
animation-delay: 0.5s;
}
.cloud:nth-child(2) {
background-color: hotpink;
animation-delay: 1s;
}
.cloud:nth-child(3) {
background-color: greenyellow;
animation-delay: 1.5s;
}
<div class="cloud" id="c1"></div>
<div class="cloud" id="c2"></div>
<div class="cloud" id="c3"></div>
The reason your clouds all reset at the same time is because each cloud is being transformed identically each time.
function setUp(item) {
item.style.transform = `translate3d(0px, 0px, 0px)`; // set all clouds to 0 on the x-axis.
}
setInterval(function(){
clouds.forEach(increment); // Every 700ms, shift each cloud along the x-axis by 1px.
},700)
Because your initial offset positioning is set identically by setUp, the translate3d(${newVal}px, 0px, 0px) is identical for all clouds. If you were to change setUp so that each cloud is transformed on the x-axis by a different initial amount then your routine would work.
(function() {
var clouds = document.querySelectorAll(".cloud");
for (let i = 0; i < clouds.length; ++i) {
setUp(clouds[i]);
}
function setUp(item) {
var rando = Math.random() * 250; // set a random spot between 0 and 250px.
item.style.transform = `translate3d(${rando}px, 0px, 0px)`;
}
function increment(item) {
let xVal = Number(item.style.transform.split('px')[0].split('(')[1]);
let newVal = xVal >= window.innerWidth/3 ? 0 : xVal + 5;
item.style.transform = `translate3d(${newVal}px, 0px, 0px)`;
item.style.background = 'red';
}
setInterval(function() {
clouds.forEach(increment);
}, 100)
})();
.cloud {
border: 1px solid black;
width: 200px;
height: 25px;
}
.cloud:nth-child(0){
position: absolute;
top: 280px;
left: 80px;
}
.cloud:nth-child(1){
position: absolute;
top: 75px;
left: 206px;
}
.cloud:nth-child(2){
position: absolute;
top: 150px;
left: -12px;
}
<div class="cloud" id="c1"></div>
<div class="cloud" id="c2"></div>
<div class="cloud" id="c3"></div>
The reason your clouds won't snap back to the left edge is because you have the left edge positioned absolutely, so they only reset to their normal, non-offset, positions.

Vanilla Javascript slider not working

There are 3 css classes/phases.
The active class is the current background that is show.
The unactive class is given to the previous background. And it makes the background slide out to the left of the screen.
The transport class is given to the background after it has received the unactive class. The transport class moves the background back to the right of the screen.
The slider totally ignores the unactive class for some reason. The background never slides to the left of the screen.
var slides = document.getElementsByClassName('bg');
var i = 0;
// When page loads show first background
(function() {
slides[i].className += ' active';
i++;
})();
function changeSlide() {
// Slide previous slide to the left of the screen
if(i === 0) {
slides[2].className = 'bg unactive';
}
else {
slides[i - 1].className = 'bg unactive';
}
// Slide the current slide in from the right of the screen
slides[i].className += ' active';
// Make the slide go back to the right of the screen
if(i === 0) {
slides[2].className = 'bg transport';
slides[2].className = 'bg';
}
else {
slides[i - 1].className = 'bg transport';
slides[i - 1].className = 'bg';
}
i++
if(i === slides.length) {
i = 0;
}
}
setInterval(changeSlide, 2000);
* {
margin: 0;
padding: 0;
}
html, body, .bg {
width: 100%;
height: 100%;
}
.bg {
position: absolute;
left: 100%;
background-color: tan;
-webkit-transition: all 0.5s linear;
-o-transition: all 0.5s linear;
-moz-transition: all 0.5s linear;
transition: all 0.5s linear;
}
/* The background that is shown */
.active {
position: absolute;
left: 0;
}
/* Make the background go to the left of the screen */
.unactive {
position: absolute;
left: -100%;
}
/* Hide the background and make it go back to the right of the screen */
.transport {
display: none;
position: absolute;
left: 100%;
z-index: -1;
}
<!-- background 1 -->
<div class="bg" style="background-image: url(https://upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Birnbaum_am_Lerchenberg_retouched.jpg/310px-Birnbaum_am_Lerchenberg_retouched.jpg)">
</div>
<!-- background 2 -->
<div class="bg" style="background-image: url(https://cdn.pixabay.com/photo/2015/03/29/01/54/tree-696839_960_720.jpg)">
</div>
<!-- background 3 -->
<div class="bg" style="background-image: url(http://www.slate.com/content/dam/slate/articles/health_and_science/science/2017/06/170621_SCI_TreePlantingHoax.jpg.CROP.promo-xlarge2.jpg)"></div>
Check out this codepen. I have the same code above except I commented out a block of javascript code. Watch the slides go in and out. That is how I want it. But I want the slider to infinitely repeat and never stop.
https://codepen.io/anon/pen/aVoNNd
You are overwriting unactive with slides[ ].className = 'bg'; a few lines below (same with transport), therefore its never applied.
I also had to change some z-index values and remove a few things to make it work. (See the comments in the code)
var slides = document.getElementsByClassName('bg');
var i = 0;
// When page loads show first background
(function() {
slides[i].className += ' active';
i++;
})();
function changeSlide() {
// Slide previous slide to the left of the screen
if(i === 0) {
slides[slides.length-1].className = 'bg unactive';//Changed 2 to slides.length-1 to avoid hardcoding values
}
else {
slides[i - 1].className = 'bg unactive';
}
// Slide the current slide in from the right of the screen
slides[i].className = 'bg active';// removed += to override transport
// Make the slide go back to the right of the screen
// prepare NEXT slide
if(i === slides.length-1) {
slides[0].className = 'bg transport';
//slides[2].className = 'bg'; // dont override transport
}
else {
slides[i + 1].className = 'bg transport';
//slides[i - 1].className = 'bg'; // dont override transport
}
i++
if(i === slides.length) {
i = 0;
}
}
setInterval(changeSlide, 2000);
* {
margin: 0;
padding: 0;
}
html, body, .bg {
width: 100%;
height: 100%;
}
.bg {
position: absolute;
left: 100%;
background-color: tan;
-webkit-transition: all 0.5s linear;
-o-transition: all 0.5s linear;
-moz-transition: all 0.5s linear;
transition: all 0.5s linear;
}
/* The background that is shown */
.active {
position: absolute;
left: 0;
}
/* Make the background go to the left of the screen */
.unactive {
position: absolute;
left: -100%;
z-index: -1; /*added*/
}
/* Hide the background and make it go back to the right of the screen */
.transport {
/*display: none;*/
position: absolute;
left: 100%;
z-index: -2; /*changed to -2*/
}
<!-- background 1 -->
<div class="bg" style="background-image: url(https://upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Birnbaum_am_Lerchenberg_retouched.jpg/310px-Birnbaum_am_Lerchenberg_retouched.jpg)">
</div>
<!-- background 2 -->
<div class="bg" style="background-image: url(https://cdn.pixabay.com/photo/2015/03/29/01/54/tree-696839_960_720.jpg)">
</div>
<!-- background 3 -->
<div class="bg" style="background-image: url(http://www.slate.com/content/dam/slate/articles/health_and_science/science/2017/06/170621_SCI_TreePlantingHoax.jpg.CROP.promo-xlarge2.jpg)"></div>

Categories