У меня есть значок шестеренки, который я хочу повернуть по часовой стрелке на 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);
}
}
Потому что переход только из одного состояния в другое. Если я перешел от 180 до 0 (а не от 180 до 359 до 0), он вращается против часовой стрелки, а не по часовой стрелке.






Если ваш значок симметричен, вы можете рассмотреть 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>Иногда он вращается по часовой стрелке, иногда против часовой стрелки.
@Gerard, если вы отпустите наведение до 1 секунды ... но в случае, если вы будете использовать класс, этого не произойдет, если, конечно, вы не удалите класс быстрее, чем переход
Вы упомянули, что используете 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 вы можете использовать взлом 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>Вы можете использовать переход для одного из движений. Но не для обоих, потому что тогда один из них идет в обратном направлении.
И вы не можете использовать анимацию для начального состояния из-за вашей проблемы номер один.
Это оставляет нам неактивное состояние, находящееся на 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, если этот или какой-либо ответ решил ваш вопрос, подумайте о принимая это, нажав галочку. Это указывает широкому сообществу на то, что вы нашли решение, и дает некоторую репутацию как автору, так и вам. Это не обязательно.
Это еще один ответ. Поскольку у меня нет винтика, я использую треугольник
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" />Поскольку вы используете React, вы можете предпочесть использовать Component. Сохраняя использование <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,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QA/wD/AP+gvaeTAAAAB3RJTUUH4AMCCik6uOTA9gAACo5JREFUaN7NmmlsXcUVx39zl7f7xeuLHTuLEyeBEELixEnK1jZSA2oaqFEpNG0RREj9gErph7ZfCqrUfqGVytJFKh9ooQKpoqUlEAhQpQmhShscEweUNImd3Usc29hvX+670w9z3+pnYydu6Hm60n1vZs75/8+cmTkz8wRXIbJrO8gsCL28BKTQ0aQqkCILZCcpEFmwTUTHa1eMQcwadE8npDNO63xzF9ACXOc8S4B6wOeUx4ER4CzwH+e5CKRLUAgL0f7W/46A7PoKCAlSA92ArNUKbAXuBNYBjYD7U9SkgCHgQ2AP8A6WOIMhC6A2vD63BOSRuyAjndoCkMuBB4F7gbbZOqJYNdALvAL8ATiV/9UUiLW7rp6APLQNWm+Hc+8DBIAHgMeA5VcIeio5CTwDvAhESWjgsxHrp++NaQmoQSpBEyBZAfwM6ASMOQafEwv4K/Bjh5ACOU1ITUlAfrC9uPRW4FlUnF8L6Qa+B7yfQzlVT1QkILvuBmnnSrcAv0PF+rWUXuA7SPaiAbZEdLzx6QTk4e0KPBrAZlRMznW8z1ROosbcvxVagVhfOrC1SU1k/uclwFOfIXiAFQ6GxQqbnFShhIDsvjv36kYNpM2fIficfA54HKQbQB7eNjUBzl9yeoBOYMdnjbxIdoD4KgBWVUlBfgzI7m1gawAhYBewqbRWrqrMkZx7md7Ov4C7gWGEjVi/GyjugYX9ubdOoKO4ZTiSYmAozMhojFgsTdZ21gZNXPkanMPq6MnaklgszchojIGhMOFIqrz2RgcbmAOlnGXXthyXasf7t6lSwfhEguf/1M3FoQhul47f56Kh1sfCpnm0Lall4YIgXp9LDbCZ9oxQuhPxNBcGwpw6O8aFwQlGxuLE4mlS6SwtjUF23reO6nne4sF7ANgOTJAFsel1Z0UVWs54B9BebCuVshgejROOpkHC8EiCM+cn6Do6iM9r0NIUpGNNM+tWNzEv6Kk4U5SCF0yEk3R/PEDX0QEuDIZJJCxsO18MAi6PxUilrfLW7U5PvIuTwRdSAgkItgL+wm+Shjo/D967lvP9E0RjacbG41wajTE0HCWRtDh5eoy+c2Mc7L7AnV9o46brG9F0Mbk3BNhZSc+xQfbs7+Vc/zjZrALsces0hgLMr/NTW+0j4HexuHkeDbX+cof4gS8B75aF0HaAKuBNVNpQKprIk5S25PJojN+8eIjB4Wh+S2Db4PMabL19GVtvW4Zplm5y0pks7x7o4533+ognLDQt7yOaQgEeeWAjDXV+RPG4siv25gFgGxARG14vmUYXAMsqdrst1SNAIrk8FmOibJBpGiSSFrv3nuLIsaECaccBPceG2L33JIlkAXxOwpEUl8diykOiyF5lWeZgLQshWATUVGyiCdIpixN9Ixw60s+J0yMkkhkkTsokQNcFmibwug1cpj5JhWnq+DwmiZSFbUuyWamiQ0A8meGFPx9h5dJ6Nq5tZuWyelxuYyoSNQ7WE+UEFlC+mxJg25KTp0bY+88zHO+7TCKhtrYet0ZDnZ+WpiAtjUHqanx4PQZVATfN86tKjduSG1eGeHTnZiLRFPFkhtFPEvQPhbkwEGZkLMbYeIqD3f0cOT7EqrYGttyylBWttSqkSnl4gKZKPVBD8awuIB7PsGd/LwcOnSMSzSAE1FZ7uGFFA+tuaGJJSzVVARdCL4uJCp7TdY1FLdWl1bI2kWiasxfH+fDjQY6dGuaTiRSHPxri1Nkxbu1YxJ2fb8PnM4tJCKC2EgFPqfcFH50YZs++Xmwb5gVddKxp5paORTQ3VqHpWmHut2e4AJTV04RgXtDNTTc0cuPKEP1DYd7vOk9XzwATkTR79vfS0hhkY3tL+WyUj5RiAqXHHhLm1/lpXVhNwO/ijtvbWN5aq7w9/SCbnUhASjRNsLClmvubgrSvXsA77/USjacJ1fsrLZB2JQKRUsWSJQur+e5DmzANDbfbVF6YK+BT9JAQgpVt9SxpmYdl2fhzq3yphCsRGEbtSUv2uwG/K++laya2xO0ycLup5H3LwQqUptMXgOik6tcQ9wxtR1GHYhUJDPD/LwMO1nICYgQ4WtEREjKWM27yafRVptJ5syhdOb2AZdnTRWwPcDn3xYl3CSq29gL3UQYtnbZ4dc9xbClpaQzSUOenOughGHDjdukYhubkMDNkZEtsW2JZNsm0RSSaYjycZPSTBBcHw2iaoPOO63C7Jx0/SeAfQDYHUdXQDchmcQrPoTb0edENjdHxBN0fX8I0wDQ0vB6TqoCbgN8k4HdR5Xfh9brweQxMQ0fXNcehAiklGcsmlbZIpiziyQyRWIpoNE00liEaS5FIWViWTcaC9tXz0Q2tEvVzDkawMgUCYt3f1EGWpA+N3cAjxa0MU6dtcS1Hj18CVDilIynGwymWmi2Ymp9LdpiYPUGGDBksbGykMwpVii/QpY4pTLzCQ7VWRUJq9Ftj+SgCMHRoW1yLYeiVZr43gD4Asfmt4hByNAgpgRdQh7ah4o5bfV2IvQfPMD6RVFWdjccqbxubvetIyXT+Scs0GWlhSbU2akJgYOASJm7hwq258Ag3hxI9XIgOFHbBEqqDHlavDFFBLqHOqEpYFQhkLZUTSw6jiZeA7xcISJrnB9m0tpm39/dNCkqByIPLebzSCJd52xKBVnGW3Li2mebGqkrefwkpDiNkSaqeDzSx6c3cbGADv6JsRhKaYMvNrSxbXJPf/lUCKJHYSOwKH5n/TBbbhtaF1Wy5uRWhTYr/HuDXCGkDiPbC6VxpzUgEbB2QZ4CfABPFvVBT7eNrX15FqN43JYkrEduGUJ3SXVvjK/f+uIPlDBhwujTnLCEgvrgPNBtnW/Qa8CSQKSaxbEkd3+pcQ1PIPyckbBsaG/x8s3MNy5fWl4PPAE+isUtFZBbx9VemJgAgNuS6R7OBp1HhVMhUpeT65SEevn89N65sQNOufDXThGD1inoevr+dVStC5eCzju1nsFX2WcBWhHcq5c5GH9StzBOo83pXoaUgGksx8nYNntNN6u5sNiIFidYBGu4YJxBwl4NPoW5rfoqTn011yaFNaaAQa1EQjwM/omgJR0oCfhf1dd4rS/gkNNT7CAQmpcuXlS3xRA58edzPiICKtZGctRSp5NOoA98DUFihhNeavfcBhHTaFlPiPWAHWuYZkOrYI56YFPfFMu1dl9hwUGnu2g5uD8DfUdPrQ8BOkCuMhjTClEhrdmNBmBIjlM59PQk8D/weGMY2Hfufft06Y6vyg7vUuJZCdbkQSxHcY0f1e8ZebVprDbu9M9YmwZyfitd0DvZogexfkLyK1M4gbLVIZbOIjt0zc8Ss3AbIw3c5L86GviEdHPl527etcfMXgHeGahJGTeYH9T/s/SPDZthJY0CKWV1yw3SDeCrG63dBu3P+G7BJ7KsPp3r9z6GugtIzUJEGfpk65X8usa8+jM9Jqto3zBo8zMGWZHDHY44i6ZGIncCjqLutct0SdZr2LILfI0kKoPHlp6/K/lVfWGezGrpuIxFJKeRvhRRvoq5m16P+OwHqvxGHgb0gzyIVNzkHK/lcbAoVwm88BgikM6UausTKChPAkK6MJXLRpUw2vfzUnNj9L3pEG6cjy84VAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE4LTA5LTE2VDAxOjA4OjMzLTA3OjAw8+YZHgAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNi0wMy0wMlQxODo0MTo1OC0wODowMK3Wv28AAAAASUVORK5CYII=';
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>
почему бы не использовать простой переход?