Animation duration relative to width using calc?
Asked Answered
C

3

10

Is it possible for animation-duration to be set in relation to the element's width using calc, when the element is dynamically added and so its width not known before page load?

I have several 'news tickers', with texts that translate horizontally from right to left infinitely using this keyframes:

@keyframes ticker {
  0% {
    transform: translateX(1%);
    visibility: visible;
  }
  100% {
    transform: translateX(-101%);
  }
}

And this CSS:

  animation-name: ticker;
  animation-duration: 10s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;

The percentages in translateX refer to the element being translated. So 101% is 1% more than the element's own width.

Because of this, the width of the element affects the translation speed. Wider elements go faster in order to perform the animation in 10 seconds, while short elements go slower.

Here is a working example that respects some layout needs of my working scenario:

.grid{
  display: grid;
  grid-template-columns: 1fr;
  overflow-y: auto;
  overflow-x: hidden;
  font-size: 5em;
  width: 40vw;
  padding:0;
  margin:0;
}
.ticker {
  margin: 0;
  overflow: hidden;
  display: inline-block;
}

.wrapper {
  display: inline-block;
  white-space: nowrap;
  animation-name: ticker;
  animation-duration: 10s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  padding-left: 100%;
}

.wrapper a {
  margin-right: 3em;
}

@keyframes ticker {
  0% {
    transform: translateX(1%);
    visibility: visible;
  }
  100% {
    transform: translateX(-101%);
  }
}
<ul class="grid">
  <li class="ticker">
    <div class="wrapper">
      <a href="">This now</a>
      <a href="">Stunning example</a>
    </div>
    <div class="wrapper">
      <a href="">Pan</a>
    </div> 
    <div class="wrapper">
      <a href="">Terribly known or unknown or perhaps just new</a>
      <a href="">In times of war every hole is a trench</a>
    </div>
  </li>
</ul>

I would like to provide a value for animation-duration that is somehow relative to the element's width, so I can make all tickers move at the same speed regardless of width.

I thought perhaps I can use calc() to multiply or divide the 10s by something that refers to the element's width. Unfortunately here the element's width is dynamic, as it is defined by a string within the element, and the string is not known before page load.

Thank you

Chancelor answered 15/5, 2019 at 22:45 Comment(4)
can you add a full working example. Probably you are tackling the problem the wrong way and there is an easier way to do thisUnexceptional
done, @TemaniAfifChancelor
This is a great question and I was wondering exactly the same. I'm using font-size based on the screen width and the element containing this font-size is animated and its length depends on the dynamic font-size, so I need exactly the same.Sarthe
Out of curiosity, have you ~considered necromancy~ tried good old HTML marquee tag for your use-case? developer.mozilla.org/en-US/docs/Web/HTML/Element/marqueeCentro
U
4

I would change your structure to make sure all the divs will have the same width. You simply need to keep the wrapper block element and remove display:grid to allow the inline-block behavior. Also replace padding with margin.

.grid{
  overflow-y: auto;
  overflow-x: hidden;
  font-size: 5em;
  width: 40vw;
  padding:0;
  margin:0;
}
.ticker {
  margin: 0;
  display: inline-block;
  margin-left:100%;
}

.wrapper {
  white-space: nowrap;
  animation-name: ticker;
  animation-duration: 10s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

.wrapper a {
  margin-right: 3em;
}

@keyframes ticker {
  0% {
    transform: translateX(1%);
    visibility: visible;
  }
  100% {
    transform: translateX(-101%);
  }
}
<ul class="grid">
  <li class="ticker">
    <div class="wrapper">
      <a href="">This now</a>
      <a href="">Stunning example</a>
    </div>
    <div class="wrapper">
      <a href="">Pan</a>
    </div> 
    <div class="wrapper">
      <a href="">Terribly known or unknown or perhaps just new</a>
      <a href="">In times of war every hole is a trench</a>
    </div>
  </li>
</ul>
Unexceptional answered 16/5, 2019 at 0:55 Comment(1)
Do you think you could make it work with a constant line of text (single white-space between repetitions, never empty space, full vw)?Sarthe
M
1

You can achieve the desired result by adding some javascript to the mix. Since we need to keep the animation-duration property dependent on width of the individual wrappers we can achieve that by following js code.

Note: you still need to change the logic as per your use-case but this will help you get access to the width of individual wrapper

// select all the wrappers
const wrappers = document.querySelectorAll('.wrapper')
// traverse through the response
for (wrapper of Object.values(wrappers)) {
  // add your logic to give animationDuration based on the width
  // width of wrapper can be obtained by - wrapper.getBoundingClientRect().width
  wrapper.style.animationDuration = `${wrapper.getBoundingClientRect().width / 100}s`
}
.grid{
  display: grid;
  grid-template-columns: 1fr;
  overflow-y: auto;
  overflow-x: hidden;
  font-size: 5em;
  width: 40vw;
  padding:0;
  margin:0;
}
.ticker {
  margin: 0;
  overflow: hidden;
  display: inline-block;
}

.wrapper {
  display: inline-block;
  white-space: nowrap;
  animation-name: ticker;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  padding-left: 100%;
}

.wrapper a {
  margin-right: 3em;
}

@keyframes ticker {
  0% {
    transform: translateX(1%);
    visibility: visible;
  }
  100% {
    transform: translateX(-101%);
  }
}
<ul class="grid">
  <li class="ticker">
    <div class="wrapper">
      <a href="">This now</a>
      <a href="">Stunning example</a>
    </div>
    <div class="wrapper">
      <a href="">Pan</a>
    </div> 
    <div class="wrapper">
      <a href="">Terribly known or unknown or perhaps just new</a>
      <a href="">In times of war every hole is a trench</a>
    </div>
  </li>
</ul>
Malapert answered 20/2 at 2:32 Comment(0)
G
1

You can change .grid { grid-template-columns: 1fr; } to .grid { grid-template-columns: 100%; } and also add .ticker { width: min-content; }.
Reworked your code a bit:

.grid {
  display: grid;
  grid-template-columns: 100%;
  font-size: 5em;
  max-width: 40vw;
  list-style: none;
  overflow: hidden;
  margin: 0;
  padding: 0;
}
.ticker {
  white-space: nowrap;
  width: min-content;
  padding-left: 100%;
  animation: ticker 10s linear infinite;
}

@keyframes ticker {
  to {
    transform: translateX(-100%);
  }
}

/* debug */
.wrapper {
  border: dashed 1px blue;
}
<ul class="grid">
  <li class="ticker">
    <div class="wrapper">
      <a href="">This now</a>
      <a href="">Stunning example</a>
    </div>
    <div class="wrapper">
      <a href="">Pan</a>
    </div> 
    <div class="wrapper">
      <a href="">Terribly known or unknown or perhaps just new</a>
      <a href="">In times of war every hole is a trench</a>
    </div>
  </li>
</ul>
Gamaliel answered 23/2 at 22:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.