Próbuję wdrożyć nieskończone kręcące się koło od dołu do góry. Do tej pory otrzymałem: przykład kołowrotka

Co działa:

  1. Obraca się od dołu do góry
  2. Zaczyna się ponownie od pierwszej struny po osiągnięciu końca

Czego chcę / co jest nieprawidłowe:
Chcę, aby animacja była nieskończona, innymi słowy, nie powinno być przejścia. Gdy animacja osiągnie ostatni element, nie może przeskoczyć z powrotem do pierwszego, ale płynnie przymocować ją do dołu, aby utworzyć trwającą animację od dołu do góry .

Jak mogę zaimplementować takie zachowanie? Żeby osiągnąć dany efekt użyłem js - czy potrzebuję trochę więcej js, czy mogę wrócić do zwykłej animacji css?

class Spinner extends Component {

    state = {
      active: 0
    };

    interval;

    componentDidMount() {
        this.interval = setInterval(() => {
            this.setState({active: this.state.active === this.props.content.length - 1 ? 0 : this.state.active + 1});
        }, 1000);
    }

    componentWillUnmount() {
        clearInterval(this.interval);
    }

    setPosition = () => {
        const n = 100 / this.props.content.length;
        return n * this.state.active * -1;
    };

    render() {
        const style = {
            transform: 'translateY(' + this.setPosition() + '%)'
        };

        return (
            <div className="spinner--list d-inline-block align-self-start">
                <ul className="p-0 m-0" style={style}>
                    {
                        this.props.content.map((item, index) => {
                            return (
                                <li key={index}>
                                    <h1>{item}</h1>
                                </li>
                            );
                        })
                    }
                </ul>
            </div>
        );
    }

}
.spinner--list {
    max-height: calc((10px + 2vw) * 1.5);
    overflow: hidden;
}

.spinner--list ul {
    list-style-type: none;
    transition: transform 1s;
    will-change: transform;
}

.spinner--container h1 {
    font-size: calc(10px + 2vw);
    line-height: 1.5;
    margin: 0;
}
3
Tom 25 marzec 2020, 03:47

2 odpowiedzi

Najlepsza odpowiedź

Oto rozwiązanie bez ul i z automatycznym dostosowaniem do zmiennej liczby tekstów, które chcesz obrócić. Rozwiązanie wykorzystuje keyframes i małą sztuczkę / iluzję w CSS do stworzenia nieskończonej wirującej animacji tylko na dwóch span -s. Po każdej iteracji animacji, tekst każdego zakresu zmiany za pomocą JS. Zmieniony tekst zależy od atrybutu data-texts w kodzie HTML. W swoim kodzie ReactJS możesz po prostu wprowadzić wszystkie teksty, które chcesz umieścić w tych data-texts atrybutach.

const spinnerTexts = document.querySelectorAll('[class^="spinner__text--"]')
const texts = spinnerTexts[0].dataset.texts.split(',')
const textPositions = [0, 1]

function initializeSpinnerTexts() {
  spinnerTexts.forEach((spinnerText, index) => {
    // Initialize the spinner texts' text
    spinnerText.innerText = texts[textPositions[index]]
    
    // Change text after every animation iteration
    spinnerText.addEventListener('animationiteration', e => {
      e.target.innerText = texts[++textPositions[index] % texts.length]
    })
  })
}

window.onload = initializeSpinnerTexts
* {
  margin: 0;
  padding: 0;
}

.spinner {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  overflow: hidden;
}

.spinner__text {
  position: relative;
  display: inline-block;
/*  width has to be set to be >= largest spinning text width  */
  width: 50px;
}

.spinner__text--top,
.spinner__text--bottom {
  display: inline-block;
  animation: 1s ease 1.05s infinite running none;
}

.spinner__text--top {
  animation-name: spinTop;
}

/* Bottom text spinner has to be configured so that it is positioned
right at the same position as the top one */
.spinner__text--bottom {
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0;
  animation-name: spinBottom;
}

@keyframes spinTop {
  from { transform: translateY(0%); }
  to { transform: translateY(-100%); }
}

@keyframes spinBottom {
  from {
    opacity: 1;
    transform: translateY(100%);
  }
  to {
    opacity: 1;
    transform: translateY(0%);
  }
}
<div class="spinner">
  This is a 
  <!-- Uses two spans to create an illusion of infinite spinning -->
  <div class="spinner__text">
    <span class="spinner__text--top" data-texts="test., bug., fail."></span>
    <span class="spinner__text--bottom" data-texts="text., bug., fail."></span> 
  </div>
</div>

Aktualizacja

Nie możesz dodać opóźnienia między każdą iteracją animacji bezpośrednio przy użyciu keyframes. Możesz jednak dostosować wartości keyframes i wartość animation-duration tak, aby wyglądało na to, że pomiędzy każdą iteracją występuje opóźnienie; w rzeczywistości robisz to tak, że nic się nie dzieje pod koniec każdej animacji. Oto przykład poniżej (podobny do powyższego, ale z niewielkimi zmianami animation-duration i keyframes).

const spinnerTexts = document.querySelectorAll('[class^="spinner__text--"]')
const texts = spinnerTexts[0].dataset.texts.split(',')
const textPositions = [0, 1]

function initializeSpinnerTexts() {
  spinnerTexts.forEach((spinnerText, index) => {
    // Initialize the spinner texts' text
    spinnerText.innerText = texts[textPositions[index]]
    
    // Change text after every animation iteration
    spinnerText.addEventListener('animationiteration', e => {
      e.target.innerText = texts[++textPositions[index] % texts.length]
    })
  })
}

window.onload = initializeSpinnerTexts
* {
  margin: 0;
  padding: 0;
}

.spinner {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  overflow: hidden;
}

.spinner__text {
  position: relative;
  display: inline-block;
  width: 50px;
}

.spinner__text--top,
.spinner__text--bottom {
  display: inline-block;
  /* Changed animation duration */
  animation: 5s ease 2s infinite running none;
}

.spinner__text--top {
  animation-name: spinTop;
}

.spinner__text--bottom {
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0;
  animation-name: spinBottom;
}

@keyframes spinTop {
  0% { transform: translateY(0%); }
  /* Spin finishes after 20% of animation duration (1s) */
  20% { transform: translateY(-100%); }
  /* Nothing from 20% until 100% of animation duration (4s) */
  /* This makes it looks like there's a delay between each animation */
  100% { transform: translateY(-100%); }
}

@keyframes spinBottom {
  /* Similar to spinTop's logic */
  0% {
    opacity: 1;
    transform: translateY(100%);
  }
  20% {
    opacity: 1;
    transform: translateY(0%);
  }
  100% { 
    opacity: 1;
    transform: translateY(0%); 
  }
}
<div class="spinner">
  This is a 
  <!-- Uses two spans to create an illusion of infinite spinning -->
  <div class="spinner__text">
    <span class="spinner__text--top" data-texts="test., bug., fail."></span>
    <span class="spinner__text--bottom" data-texts="text., bug., fail."></span> 
  </div>
</div>
1
Richard 26 marzec 2020, 08:27

Rozwiązanie CSS firmy @keyframes

body { 
  color: black;
  padding: 0;
  margin: 0;
}

.text {
  padding: 20px 0;
  display: flex;
  position: relative;
}

.item-1, 
.item-2, 
.item-3 {
  position: absolute;
  padding: 20px 0;
  margin: 0;
  margin-left: 5px;

  animation-duration: 4s;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
}

.item-1{
  animation-name: anim-1;
}

.item-2{
  animation-name: anim-2;
}

.item-3{
  animation-name: anim-3;
}

@keyframes anim-1 {
  0%, 8.3% { top: -50%; opacity: 0; }
  8.3%,25% { top: 0%; opacity: 1; }
  33.33%, 100% { top: 50%; opacity: 0; }
}

@keyframes anim-2 {
  0%, 33.33% { top: -50%; opacity: 0; }
  41.63%, 58.29% { top: 0%; opacity: 1; }
  66.66%, 100% { top: 50%; opacity: 0; }
}

@keyframes anim-3 {
  0%, 66.66% { top: -50%; opacity: 0; }
  74.96%, 91.62% { top: 0%; opacity: 1; }
  100% { top: 50%; opacity: 0; }
}
<div class="text">This is a 
  <div class="items">
    <p class="item-1">test.</p>
    <p class="item-2">bug.</p>
    <p class="item-3">fail.</p>
  </div>
</div>
1
rangerz 25 marzec 2020, 02:54