Slide animation for text in container infinitely - javascript

I am working on slide animation for text in container infinitely.
However, I found it not working smoothly. When the text reaches the end, it seems like restarting instead of looping the text.
What I want is the text keep sliding to the right continuously.
How to solve it?
App.js
import "./styles.css";
export default function App() {
return (
<div className="App">
<div class="slide-right">
<h2>
<span>A</span>
<span>A</span>
<span>A</span>
<span>A</span>
<span>A</span>
<span>A</span>
</h2>
</div>
</div>
);
}
Styles.css
.App {
font-family: sans-serif;
text-align: center;
}
.slide-right {
border: 1px solid grey;
width: 150px;
overflow: hidden;
}
.slide-right h2 {
animation: infinite slide-right 2s linear;
transform: translateX(-100%);
text-align: right;
}
.slide-right h2 span {
margin-right: 10px;
}
#keyframes slide-right {
to {
transform: translateX(0);
}
}
Codesandbox:
https://codesandbox.io/s/hidden-bird-qy3jy?file=/src/styles.css

Change your "slide-right" keyframes from transform: translateX(0); to transform: translateX(100%);

To get a continuous flow this snippet has two copies of the spans.
Initially the h2 is translated -50% so only the beginning of the second half is showing.
The animation moves it to the right so it ends up with the beginning of the first half showing.
Then on the repeat the beginning of the second half takes the place of the beginning of the first half, so it all looks continuous.
.App {
font-family: sans-serif;
text-align: center;
}
.slide-right {
border: 1px solid grey;
width: 150px;
overflow: hidden;
}
.slide-right h2 {
animation: infinite slide-right 2s linear;
transform: translateX(-50%);
display: inline-block;
}
.slide-right h2 span {
margin-right: 10px;
}
#keyframes slide-right {
to {
transform: translateX(0);
}
}
<div class="App"><div class="slide-right"><h2><span>A</span><span>A</span><span>A</span><span>A</span><span>A</span><span>A</span><span>A</span><span>A</span><span>A</span><span>A</span><span>A</span><span>A</span></h2></div></div>
Note: to avoid problems with wrapping of text the spans are laid out continuously (as in the codesandbox in the question).

#keyframes slide-right {
to {
transform: translateX(100%);
}
}
Use this instead of translateX(0)
Double the number of A to make the effect continuous.

Related

Text color in CSS Animation doesn't update with rest of content when 'theme toggle' is clicked on iOS

I have text that scrolls horizontally in a marquee style effect. This works great.
In addition to this I have a 'toggle' that when clicked adds the class .dark-mode to the html tag which basically inverts all the colours. So by default it's white page, black text. When toggled, black page, white text.
This works perfectly on my desktop browser. However I've noticed on iOS Safari and Chrome the text within the CSS Animation does not update with the rest of the copy. I haven't been able to test if that's the case on Android as well.
I've included the code for this but also put it on a CodePen here incase it's easier to view for people: https://codepen.io/moy/pen/eYjryXN
What's additional strange, if I scroll down the page then back up the text DOES change colour? Which indicates to me maybe once the animation is running, it leaving the viewport and re-entering somehow renders it again and corrects the colour?
I thought adding:
document.querySelector(‘.marquee’).offsetWidth;
Might force a 'reflow' of the page but it didn't do anything. Really appreciate some help on this. Strange one!
const html = document.querySelector('html');
const button = document.querySelector('.contrast__link');
button.addEventListener('click', e => {
e.preventDefault();
html.classList.toggle('dark-mode');
});
:root {
--color-primary: black;
--color-secondary: white;
--spacing: 24px;
}
.dark-mode {
--color-primary: white;
--color-secondary: black;
}
body {
background: var(--color-secondary);
color: var(--color-primary);
margin: 0 auto;
padding: 48px 24px;
max-width: 800px;
}
.marquee {
margin: 0 calc(var(--spacing) * -1);
margin-bottom: 120px;
}
.marquee h1 {
display: flex;
flex: 1 1 auto;
flex-wrap: nowrap;
overflow: hidden;
}
.marquee span {
-webkit-animation: 8s linear infinite 0s forwards running marquee;
animation: 8s linear infinite 0s forwards running marquee;
box-sizing: border-box;
flex: 1 0 50%;
padding: 0 var(--spacing);
white-space: nowrap;
text-align: center;
}
/**
* The looping animation for the marquee.
*/
#-webkit-keyframes marquee {
from {
transform: translateX(0);
}
to {
transform: translateX(-100%);
}
}
#keyframes marquee {
from {
transform: translateX(0);
}
to {
transform: translateX(-100%);
}
}
<div class="grid__item">
<div class="hgroup">
<div class="marquee">
<h1 class="page-title">
<span>Hello... is it me you're looking 404?</span>
<span>Hello... is it me you're looking 404?</span>
<span>Hello... is it me you're looking 404?</span>
<span>Hello... is it me you're looking 404?</span>
</h1>
</div>
</div>
<p class="contrast">Toggle Here</p>
</div>

How to stop a text animation from moving the logo when the text appears

I am making a mental health website. on the homepage of the website I have chosen to make a text that is animated as opposed to being static, just to make the website more lively and appealing.
this is what it looks like when the text appears.
The horizontal overflow is hidden, so the logo on the side is out of the page because the text has stretched.
How can I fix this?
// sets the interval for which the function will run, in this case 8 seconds, (8000)
setInterval(function() {
// grab all elements with class 'sub-head' and stores it in the elems const.
const elems = document.querySelectorAll('.sub-head')
// loop through the found elements
elems.forEach(e => {
// check if the element has a class 'inactive', if there is one, remove it
if (e.classList.contains('inactive')) e.classList.remove('inactive')
// if not, add it. This is how it creates a loop.
else e.classList.add('inactive');
});
}, 8000)
/* The animation text*/
.intro {
display: inline-flex;
overflow: hidden;
white-space: nowrap;
}
.intro1 {
animation: showup 7s;
font-family: Arial, Helvetica, sans-serif;
font-size: 40px;
color: purple;
}
.intro2 {
width: 0px;
animation: reveal 7s infinite;
}
.inactive {
display: none;
}
.sub-head {
margin-left: -355px;
animation: slidein 7s infinite;
font-size: 30px;
font-family: Arial, Helvetica, sans-serif;
}
#keyframes showup {
0% {
opacity: 0;
}
20% {
opacity: .4;
}
80% {
opacity: .8;
}
100% {
opacity: 1;
}
}
#keyframes slidein {
0% {
margin-left: -800px;
}
20% {
margin-left: -800px;
}
35% {
margin-left: 0px;
}
100% {
margin-left: 0px;
}
}
#keyframes reveal {
0% {
opacity: 0;
width: 0px;
}
20% {
opacity: 1;
width: 0px;
}
30% {
width: 800px;
}
80% {
opacity: 1;
}
100% {
opacity: 0;
width: 800px;
}
}
<div class="first-box">
<div class="intro intro1">Welcome!</div>
<div class="intro intro2">
<span class="sub-head "> We care about you</span>
<span class="sub-head inactive">becuase you matter</span>
<!-- lol dramatic effect-->
</div>
</div>
I think the problem was you're setting so much width that the image/logo will also be affected by the width that you have set.
SOLUTION:
Set a static width on your .intro2 class for example 300px or reasonable width that will only fit your static content.
.intro2 {
width: 300px;
animation: reveal 7s infinite;
}
You can check on this pen https://codepen.io/Preygremmer15/pen/MWoJBzv

Triggering two different animations onClick?

I am trying to trigger two different animations by adding and removing two different css classes with two different css animations by using javascript to do so. However, the div does not preserve the previous animated state, and I need it to do so because of how I designed the UI. I am using forwards in the animations property, but the div performs the animation when it is clicked and then goes back to previous state and then performs the other animation.
const projects = document.getElementById('projects')
projects.addEventListener('click', () => {
if(projects.classList.contains('projects-animation') == true){
projects.classList.remove('projects-animation')
projects.classList.add('projects-animation2')
}
else{
console.log('animation2')
console.log(projects.classList)
projects.classList.remove('projects-animation2')
projects.classList.add('projects-animation')
}
})
.projects-container{
position: relative;
z-index: 1;
display: flex;
align-items: center;
grid-area: projects;
padding: 20px;
font-size: 100px;
background-color: hsl(4, 7%, 45%);
cursor: pointer;
}
.projects{
position: relative;
height: fit-content;
}
.projects-animation{
animation: projects 1s ease-in-out backwards;
}
.projects-animation2{
animation: projects2 1s ease-in-out backwards;
}
#keyframes projects {
100%{
transform: translate(50%,-250%);
}
}
#keyframes projects2 {
100%{
transform: translate(-50%, 250%);
}
}
<div class="projects-container">
<div id = "projects" class="projects">Projects</div>
</div>
Please check this one,
1: You need to add 0% as starting point for the second animation,
2: I have used smaller value as larger value moving the child div out of view port, if you want to keep the values then make the parent div large or position it accordingly
const projects = document.getElementById('projects')
projects.addEventListener('click', () => {
if(projects.classList.contains('projects-animation') == true){
projects.classList.remove('projects-animation')
projects.classList.add('projects-animation2')
}
else{
console.log('animation2')
console.log(projects.classList)
projects.classList.remove('projects-animation2')
projects.classList.add('projects-animation')
}
})
.projects-container{
position: relative;
z-index: 1;
display: flex;
align-items: center;
grid-area: projects;
padding: 20px;
font-size: 100px;
background-color: hsl(4, 7%, 45%);
cursor: pointer;
}
.projects{
position: relative;
height: fit-content;
}
.projects-animation{
animation: projects 1s ease-in-out forwards;
}
.projects-animation2{
animation: projects2 1s ease-in-out forwards;
}
#keyframes projects {
100%{
transform: translate(10%,-10%);
}
}
#keyframes projects2 {
0%{
transform: translate(10%,-10%);
}
100%{
transform: translate(0%, 0%);
}
}
<div class="projects-container">
<div id = "projects" class="projects">Projects</div>
</div>

CSS transition div width from center

I am working on a little menu animation, nothing groundbreaking but just as an experiment. This is what I currently have:
HTML
<div class="menu">
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
</div>
SCSS
div.menu {
width: 24px;
height: 24px;
position: relative;
margin: 48px;
cursor: pointer;
div.bar {
display: block;
width: 100%;
height: 2px;
background-color: #444;
position: absolute;
transition: all 0.25s ease-in-out;
&:nth-child(1) {
}
&:nth-child(2) {
top: 11px;
}
&:nth-child(3) {
top: 11px;
}
&:nth-child(4) {
bottom: 0;
}
}
&.active {
div.bar {
&:nth-child(1) {
width: 0;
}
&:nth-child(2) {
transform: rotate(-45deg);
}
&:nth-child(3) {
transform: rotate(45deg);
}
&:nth-child(4) {
width: 0;
}
}
}
}
JAVASCRIPT
var menu = document.querySelector('.menu');
menu.addEventListener('click', function(){
menu.classList.toggle('active');
});
And this is a pen of it in action:
https://codepen.io/mikehdesign/pen/eWJKKN
Currently, when the menu is active the top and bottom div.bar reduce their width to 0 to the left. I would like to adjust this so they reduce their width to the center. I have tried messing with margins for them but had no luck, if anyone could shed some light or suggest a different approach if needed that would be great.
Mike
You can use transform-origin to do this:
with respect to your dimensions it would be 12px up and down(+ and -) as in the code below:
&.active {
div.bar {
&:nth-child(1) {
transform-origin:12px 12px;
transform: scale(0);
}
&:nth-child(2) {
transform: rotate(-45deg);
}
&:nth-child(3) {
transform: rotate(45deg);
}
&:nth-child(4) {
transform-origin:12px -12px;
transform: scale(0);
}
}
}
Check this out: JsFiddle Link
I would use the pseudo elements to split up the transform animation.
Demo (no script version)
HTML (note only three bars)
<input type="checkbox" id="toggle-menu" hidden>
<label for="toggle-menu" class="menu">
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
</label>
SCSS
// two step transition
// as translate and rotation can't (yet) be animated individually
// we use the pseudo elements to split up the animation
//
// - bar elements will handle the vertical transform
// - :after elements will handle rotation/scale
// variables to control transition time and delay
$transition-time: 300ms;
$transition-delay: 300ms;
.menu {
width: 24px;
height: 24px;
position: relative;
cursor: pointer;
display: block;
}
.bar {
height: 2px;
position: absolute;
top: 50%;
width:100%;
// Entering `hamburger` state
// 1) add a delay on bars to wait for the :after elements to rotate/scale back
// 2) the :after elements have no delay
transition: $transition-time $transition-delay; // 1
&:after {
content:'';
display:table;
background: black;
position: inherit; width: inherit; height:inherit;
transition: $transition-time; // 2
}
// transform the bars into hamburger
&:nth-child(1){ transform: translateY(-8px); }
&:nth-child(3){ transform: translateY(8px);}
}
// when toggle-menu checkbox is checked transform to `X`
[id="toggle-menu"]:checked ~ .menu {
.bar {
// Entering `X` state
// 1) to animate bars to the center we simply remove the transform
// 2) as we are now animating backwards we switch the transition
// on the bars and their :after elements
transform: none; // 1
transition: $transition-time; // 2
&:after { transition: $transition-time $transition-delay } // 2
// rotate the top and bottom :after elements
&:nth-child(1):after{ transform: rotate(-45deg); }
&:nth-child(3):after{ transform: rotate(45deg);}
// hide the middle :after by scaling to zero
// (when all bars are at the center)
&:nth-child(2):after{ transform: scale(0); }
}
}

Animate one at a time with ng-repeat

I am trying to use ngRepeat to load an image and play it's associated tone, then move the image from the center of the circle to a specific position on a circle, and proceed with the doing the same thing with the next image. I got the images to display and move one by one using ng-enter-stagger, however the images have different positions so when I change it to to use a different class for each repetition, ng-enter-stagger does not work.
How can I go about loading one image, moving it to the proper position, hiding the image, then proceeding with the next image?
I have created a plunkr but the animation does not work in it https://plnkr.co/edit/DddST6JsemsCKKf3mQ6N?p=preview.
An example of what I want to do is the Learn the sounds part of this (http://www.absolutepitchstudy.com/animalgame/) click either Start Control or Start Animal Game
The data looks like this:
"ImageTones":[{"CPosition":"deg60","Image":{"ImageFileName":"Alligator.png","ImageId":1},"Tone":{"ToneFileName":"C3.mp4","ToneId":1}},
{"CPosition":"deg0","Image":{"ImageFileName":"Cow.png","ImageId":4},"Tone":{"ToneFileName":"B5.mp4","ToneId":2}},
{"CPosition":"deg270","Image":{"ImageFileName":"Bird.png","ImageId":3},"Tone":{"ToneFileName":"E3.mp4","ToneId":3}}]
Html page:
<div class="circle-container">
<div ng-repeat="it in model.imageTones" class="it.CPosition">
<img ng-src="../Content/Game/Animals/{{it.Image.ImageFileName}}"/>
<!--Audio tag goes here-->
</div>
</div>
My CSS (I may be able to fix this to not have as many classes, just am unsure how)
.circle-container {
position: relative;
width: 38em;
height: 38em;
padding: 2.8em;
/*2.8em = 2em*1.4 (2em = half the width of a link with img, 1.4 = sqrt(2))*/
border: dashed 1px;
border-radius: 80%;
margin: -5.25em auto 0;
}
.circle-container div {
display: block;
position: absolute;
top: 50%;
left: 50%;
width: 4em;
height: 4em;
margin: -2em;
}
.circle-container div.ng-enter {
transition: 5s linear all;
opacity: 0;
}
.circle-container div.ng-enter-stagger {
/* this will have a 100ms delay between each successive leave animation */
transition-delay: 5.0s;
/* As of 1.4.4, this must always be set: it signals ngAnimate
to not accidentally inherit a delay property from another CSS class */
transition-duration: 0s;
}
.circle-container div.ng-enter.ng-enter-active {
/* standard transition styles */
opacity:1;
}
.deg0.ng-enter-active {
transform: translate(19em);
}
.deg30.ng-enter-active {
transform: rotate(30deg) translate(19em) rotate(-30deg);
}
.deg60.ng-enter-active {
transform: rotate(60deg) translate(19em) rotate(-60deg);
}
.deg90.ng-enter-active {
transform: rotate(90deg) translate(19em) rotate(-90deg);
transition: transform 5s;
}
.deg120.ng-enter-active {
transform: rotate(120deg) translate(19em) rotate(-120deg);
}
.deg150.ng-enter-active {
transform: rotate(150deg) translate(19em) rotate(-150deg);
}
.deg180.ng-enter-active {
transform: rotate(180deg) translate(19em) rotate(-180deg);
}
.deg210.ng-enter-active {
transform: rotate(210deg) translate(19em) rotate(-210deg);
}
.deg240.ng-enter-active {
transform: rotate(240deg) translate(19em) rotate(-240deg);
}
.deg270.ng-enter-active {
transform: rotate(270deg) translate(19em) rotate(-270deg);
}
.deg300.ng-enter-active {
transform: rotate(300deg) translate(19em) rotate(-300deg);
}
.deg330.ng-enter-active {
transform: rotate(330deg) translate(19em) rotate(-330deg);
}
There's a couple of errors to look at 1st, To get a value of a class from an angular item, it's ng-class you should be looking for:
<div ng-repeat="it in model.imageTones" ng-class="it.CPosition" ng-if="!it.hidden" >
<img ng-src="http://www.absolutepitchstudy.com/animalgame/content/images/{{it.Image.ImageFileName}}" />
</div>
Then in you style sheet there seems to be something wrong with the CSS, so I removed a class that wasn't being used:
.deg60{
transform: rotate(60deg) translate(19em) rotate(-60deg);
}
Although to hide stuff you may want that back.
The updated plunk with the work so far is at:
plunky
Now it's being rendered in the right place, you can use $timeout, ng-click or someother method to alter the class definition in your model. The position of the graphic should automatically update.
What method were you going to use?

Categories