Вращаясь по часовой стрелке 180 затем 360 - PullRequest
0 голосов
/ 06 сентября 2018

У меня есть значок cog, который я хочу вращать по часовой стрелке на 180 градусов при активации (class = "cards__cog cards__cog-active"), а затем вращать еще 180 градусов по часовой стрелке, чтобы вернуться в свое деактивированное состояние (class = "cards__cog cards__cog-inactive"). Я делаю это с React, реагируя на изменение состояния при щелчке по зубцу.

Ниже работает, но мои проблемы:

1) Он анимируется при загрузке страницы (что имеет смысл, поскольку у него есть класс cards__cog-inactive, но какова альтернатива?).

2) Это уродливо и должен быть более легкий путь.

Спасибо

.cards {
    &__cog {
            position: absolute;
            right: 20px;
            top: 20px;
            width: 10vh;
            cursor: pointer;

            &-active {
                 animation: rotate180 1s ease;
                animation-fill-mode: forwards;
            }
            &-inactive {
                animation: rotate180to359to0 1s ease;
                animation-fill-mode: forwards;
            }
       }
}


    @keyframes rotate180 {
      0% {
        transform: rotate(0deg);
      }
      100% {
        transform: rotate(180deg);
      }
    }

    @keyframes rotate180to359to0 {
      0% {
        transform: rotate(180deg);
      }
      99% {
        transform: rotate(359deg);
      }
      100% {
        transform: rotate(0deg);
      }
    }

Ответы [ 5 ]

0 голосов
/ 16 сентября 2018

Поскольку вы используете React, вы можете предпочесть использовать Компонент. Продолжая использовать <button> и стиль анимации, вы можете отделить постоянное состояние кнопки от состояния перехода, чтобы анимация появлялась только при нажатии, а не при перезагрузке.

// Image src https://upload.wikimedia.org/wikipedia/commons/thumb/9/97/Twemoji_1f61d.svg/200px-Twemoji_1f61d.svg.png
const SMILEY_PNG = 'data:image/png;base64,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';

class AnimatedIcon extends React.Component {
  constructor() {
    super();
    this.state = {
      active: false,
      transitionStyle: {}
    };
  }

  render() {
    // On click set new state and add transition styling
    const onclk = () => {
      this.setState({
        active: !this.state.active,
        transitionStyle: this.state.active ? {animation: 'rotate-from-180 1s ease'} : {animation: 'rotate-to-180 1s ease'}
      });
    };
    
    return (
      <button onClick={onclk} className={this.state.active ? 'active' : 'inactive'}>
          <img src={SMILEY_PNG} style={this.state.transitionStyle} />
          ClickMe 
      </button>
    );
  }
}

ReactDOM.render( <AnimatedIcon /> , document.getElementById('root'));
button {
  margin: 10px;
  font-size: 20pt;
  color: red;
}

button img {
  padding: 10px;
  vertical-align: middle;
}

.active {
  color: green;
}

.active img {
  transform: rotate(180deg);
}

@keyframes rotate-to-180 {
  from { transform: rotate(0deg); }
  to   { transform: rotate(180deg); }
}

@keyframes rotate-from-180 {
  from { transform: rotate(180deg); }
  to   { transform: rotate(360deg); }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
0 голосов
/ 12 сентября 2018

Это еще один ответ. Так как у меня нет винтика, я использую треугольник

toggle.addEventListener("click", () => {
cog.className = (cog.className == "" || cog.className == "inactive") ? "active" : "inactive"
});
#cog{
  margin:0 auto;
  width:108px;
  outline:1px solid;
  transform-origin: 54px 108px;
}

#triangle{
  outline:1px solid;
  border:25px solid transparent;
  border-bottom:100px solid green;
  width:0px;
  height:1px;
  position:relative;
  margin:auto;
}

#triangle::before{
  content:"";
  width:16px; 
  height:16px;
  background:red;
  display:block;
  position:absolute;
  bottom:-108px;
  left:-8px;
  border-radius:50%;
}


 @keyframes rotate1 {
      100% {
        transform: rotate(180deg);
      }
    }

 @keyframes rotate2 {
       0% {
        transform: rotate(180deg);
      }
      100% {
        transform: rotate(360deg);
      }
    }

#cog.active{animation: rotate1 1s ease;
            animation-fill-mode: forwards;}

#cog.inactive{animation: rotate2 1s ease;
            animation-fill-mode: forwards;}
<div id="cog" class="">
  <div id="triangle"></div>
</div>


<input type="button" value="toggle-class" id="toggle" />
0 голосов
/ 10 сентября 2018

Вы упомянули, что используете React. Учитывая это, вы можете попытаться переместить логику вращения из css в сам компонент. Для этого вам не понадобятся дополнительные классы, keyframes и т. Д. В этом сценарии необходимы только комбо transition и transform.

class Cog extends React.Component {
  state = {
    isActive: false,
    togglesCount: 0
  }
  
  get rotationValue () {
    return `${this.state.togglesCount * 180}deg`
  }
  
  get cogStyle () {
    return {
      transition: 'transform 1s',
      transform: `rotateZ(${this.rotationValue})`
    }
  }
  
  toggle = () => {
    this.setState(s => ({ 
      isActive: !s.isActive,
      togglesCount: ++s.togglesCount
    }))
  }
  
  render() {
    return (
      <button onClick={this.toggle}>
        <i className="fas fa-cog fa-3x" style={this.cogStyle}/>
      </button>
    ) 
  }
}


ReactDOM.render(<Cog />, document.getElementById('root'))
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css">
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root" />

Для решения css-only вы можете использовать transition и rotateY(-180deg) взломать родительский элемент:

.icon { 
  display: inline-block; 
}

.icon__inner {
  transition: transform 1s;
}

input:checked + .icon {
  transform: rotateY(-180deg);
}

input:checked + .icon .icon__inner {
  transform: rotateZ(-180deg);
}
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css">

<input type='checkbox' />

<div class="icon">
  <i class="icon__inner fas fa-cog fa-3x"></i>
</div>
0 голосов
/ 10 сентября 2018

Вы можете использовать переход для одного из движений. Но не для обоих, потому что тогда один из них идет назад.

И вы не можете использовать анимацию для начального состояния из-за вашей проблемы номер один.

Это оставляет нас с неактивным состоянием, находящимся на 360 градусов, так что он может переходить от 180 до 360 правильным образом, и анимацией для перехода от 0 до 180 для перехода от неактивного к активному

function change () {

	var elem = document.getElementById("test");
	elem.classList.toggle('active');
}
.test {
  width: 200px;
  height: 100px;
  border: solid 4px red;
  margin: 20px;
  transform: rotate(360deg);
  transition: transform 1s;
}

.active {
  animation: activate 1s;
  transform: rotate(180deg);
}

@keyframes activate {
  from {transform: rotate(0deg);}
  to {transform: rotate(180deg);}
}
<div class="test" id="test">TEST</div>
<button onclick="change();">change</button>
0 голосов
/ 10 сентября 2018

Если ваша иконка симметрична, вы можете рассмотреть 2 элемента и переход, как это.

var e = document.querySelector('.box');
e.addEventListener('click',function() {
  e.classList.toggle('active');
})
.box {
  display:inline-block;
  margin:20px;
  transition:0s .5s;
}
.box > i {
  transition:.5s;
  color:red;
  display:block;
}
.box.active {
  transform:scaleX(-1);
}

.box.active i{
  transform:rotate(180deg);
}
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css">
<div class="box">
  <i class="fas fa-cog fa-7x"></i>
</div>
...