顺时针旋转180度然后360度。

3
我有一个齿轮图标,当被点击激活(class="cards__cog cards__cog-active")时,我希望它顺时针旋转180度,再顺时针旋转另外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);
      }
    }

1
为什么不使用简单的转换? - Temani Afif
因为转换只是从一个状态到另一个状态的变化。如果我从180度转到0度(而不是从180度转到359度再到0度),它会逆时针旋转而不是顺时针旋转。 - Atrag
5个回答

8
你可以使用过渡效果来完成其中一种动作。但是,不能同时使用过渡和动画效果,因为这样会导致其中一个方向回退。
而且,由于问题一的存在,您不能在初始状态下使用动画效果。
因此,我们将非活动状态设置为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>


嗨@Atrag,如果这个或任何答案解决了你的问题,请考虑通过点击复选标记接受它来表达你的认可。这向更广泛的社区表示你已经找到了解决方案,并为回答者和自己赢得了一些声誉。没有义务这样做。 - chridam

6

您提到正在使用React。考虑到这一点,您可以尝试将旋转逻辑从css移动到组件本身中。这样做,您就不需要额外的类、keyframes等。在这种情况下,只需要transitiontransform组合即可。

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的解决方案,您可以在父元素上使用transitionrotateY(-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>


5
如果你的图标是对称的,你可以考虑使用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>


1
有时它顺时针旋转,有时逆时针旋转。 - Gerard
@Gerard 如果你在1秒之前释放了悬停...但是如果你使用一个类,除非你比转换更快地删除这个类,否则这种情况不会发生。 - Temani Afif

2
这是另一个答案。由于我没有齿轮,所以我使用三角形。

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

由于您正在使用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>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接