]> cat aescling's git repositories - mastodon.git/commitdiff
[Glitch] Replace fav icon animation with CSS
authorThibG <thib@sitedethib.com>
Thu, 24 Oct 2019 20:47:48 +0000 (22:47 +0200)
committerThibaut Girka <thib@sitedethib.com>
Sun, 27 Oct 2019 20:10:24 +0000 (21:10 +0100)
Port 3a929dbedd31ea67723746bdf387e22e66e247cd to glitch-soc

And extend that to collapse button

Signed-off-by: Thibaut Girka <thib@sitedethib.com>
app/javascript/flavours/glitch/components/icon_button.js
app/javascript/flavours/glitch/components/status_icons.js
app/javascript/flavours/glitch/styles/components/index.scss
app/javascript/flavours/glitch/styles/components/status.scss

index 6247f457139bc75ade69d38226945afad2acc2c7..e134d0a39c0c6d80cac2ad549252435632a90034 100644 (file)
@@ -24,7 +24,6 @@ export default class IconButton extends React.PureComponent {
     disabled: PropTypes.bool,
     inverted: PropTypes.bool,
     animate: PropTypes.bool,
-    flip: PropTypes.bool,
     overlay: PropTypes.bool,
     tabIndex: PropTypes.string,
     label: PropTypes.string,
@@ -39,6 +38,21 @@ export default class IconButton extends React.PureComponent {
     tabIndex: '0',
   };
 
+  state = {
+    activate: false,
+    deactivate: false,
+  }
+
+  componentWillReceiveProps (nextProps) {
+    if (!nextProps.animate) return;
+
+    if (this.props.active && !nextProps.active) {
+      this.setState({ activate: false, deactivate: true });
+    } else if (!this.props.active && nextProps.active) {
+      this.setState({ activate: true, deactivate: false });
+    }
+  }
+
   handleClick = (e) =>  {
     e.preventDefault();
 
@@ -81,86 +95,49 @@ export default class IconButton extends React.PureComponent {
 
     const {
       active,
-      animate,
       className,
       disabled,
       expanded,
       icon,
       inverted,
-      flip,
       overlay,
       pressed,
       tabIndex,
       title,
     } = this.props;
 
+    const {
+      activate,
+      deactivate,
+    } = this.state;
+
     const classes = classNames(className, 'icon-button', {
       active,
       disabled,
       inverted,
+      activate,
+      deactivate,
       overlayed: overlay,
     });
 
-    const flipDeg = flip ? -180 : -360;
-    const rotateDeg = active ? flipDeg : 0;
-
-    const motionDefaultStyle = {
-      rotate: rotateDeg,
-    };
-
-    const springOpts = {
-      stiffness: this.props.flip ? 60 : 120,
-      damping: 7,
-    };
-    const motionStyle = {
-      rotate: animate ? spring(rotateDeg, springOpts) : 0,
-    };
-
-    if (!animate) {
-      // Perf optimization: avoid unnecessary <Motion> components unless
-      // we actually need to animate.
-      return (
-        <button
-          aria-label={title}
-          aria-pressed={pressed}
-          aria-expanded={expanded}
-          title={title}
-          className={classes}
-          onClick={this.handleClick}
-          onMouseDown={this.handleMouseDown}
-          onKeyDown={this.handleKeyDown}
-          onKeyPress={this.handleKeyPress}
-          style={style}
-          tabIndex={tabIndex}
-          disabled={disabled}
-        >
-          <Icon id={icon} fixedWidth aria-hidden='true' />
-        </button>
-      );
-    }
-
     return (
-      <Motion defaultStyle={motionDefaultStyle} style={motionStyle}>
-        {({ rotate }) =>
-          (<button
-            aria-label={title}
-            aria-pressed={pressed}
-            aria-expanded={expanded}
-            title={title}
-            className={classes}
-            onClick={this.handleClick}
-            onMouseDown={this.handleMouseDown}
-            onKeyDown={this.handleKeyDown}
-            onKeyPress={this.handleKeyPress}
-            style={style}
-            tabIndex={tabIndex}
-            disabled={disabled}
-          >
-            <Icon id={icon} style={{ transform: `rotate(${rotate}deg)` }} fixedWidth aria-hidden='true' />
-            {this.props.label}
-          </button>)
-        }
-      </Motion>
+      <button
+        aria-label={title}
+        aria-pressed={pressed}
+        aria-expanded={expanded}
+        title={title}
+        className={classes}
+        onClick={this.handleClick}
+        onMouseDown={this.handleMouseDown}
+        onKeyDown={this.handleKeyDown}
+        onKeyPress={this.handleKeyPress}
+        style={style}
+        tabIndex={tabIndex}
+        disabled={disabled}
+      >
+        <Icon id={icon} fixedWidth aria-hidden='true' />
+        {this.props.label}
+      </button>
     );
   }
 
index d99b25e25be1624075e6a6d38455e4ae884d9b67..f4d0a74051cc13c6d4b7102be2b22789bbe415cf 100644 (file)
@@ -103,7 +103,7 @@ class StatusIcons extends React.PureComponent {
         {collapsible ? (
           <IconButton
             className='status__collapse-button'
-            animate flip
+            animate
             active={collapsed}
             title={
               collapsed ?
index 055a494e74c8d23a291a369a261ad9e560c4e93d..ef76cea5ec401e35aa1524fb131d27d9f61d132d 100644 (file)
   color: $red-bookmark;
 }
 
+.no-reduce-motion .icon-button.star-icon {
+  &.activate {
+    & > .fa-star {
+      animation: spring-rotate-in 1s linear;
+    }
+  }
+
+  &.deactivate {
+    & > .fa-star {
+      animation: spring-rotate-out 1s linear;
+    }
+  }
+}
+
 .notification__display-name {
   color: inherit;
   font-weight: 500;
   animation: loader-figure 1.15s infinite cubic-bezier(0.215, 0.610, 0.355, 1.000);
 }
 
+@keyframes spring-rotate-in {
+  0% {
+    transform: rotate(0deg);
+  }
+
+  30% {
+    transform: rotate(-484.8deg);
+  }
+
+  60% {
+    transform: rotate(-316.7deg);
+  }
+
+  90% {
+    transform: rotate(-375deg);
+  }
+
+  100% {
+    transform: rotate(-360deg);
+  }
+}
+
+@keyframes spring-rotate-out {
+  0% {
+    transform: rotate(-360deg);
+  }
+
+  30% {
+    transform: rotate(124.8deg);
+  }
+
+  60% {
+    transform: rotate(-43.27deg);
+  }
+
+  90% {
+    transform: rotate(15deg);
+  }
+
+  100% {
+    transform: rotate(0deg);
+  }
+}
+
 @keyframes loader-figure {
   0% {
     width: 0;
index a4d9983f2717e710502561658d6e88b3a33c8701..77d67576b9b84e61516d3c7a02d908c56e573d6a 100644 (file)
@@ -1,3 +1,47 @@
+@keyframes spring-flip-in {
+  0% {
+    transform: rotate(0deg);
+  }
+
+  30% {
+    transform: rotate(-242.4deg);
+  }
+
+  60% {
+    transform: rotate(-158.35deg);
+  }
+
+  90% {
+    transform: rotate(-187.5deg);
+  }
+
+  100% {
+    transform: rotate(-180deg);
+  }
+}
+
+@keyframes spring-flip-out {
+  0% {
+    transform: rotate(-180deg);
+  }
+
+  30% {
+    transform: rotate(62.4deg);
+  }
+
+  60% {
+    transform: rotate(-21.635deg);
+  }
+
+  90% {
+    transform: rotate(7.5deg);
+  }
+
+  100% {
+    transform: rotate(0deg);
+  }
+}
+
 .status__content--with-action {
   cursor: pointer;
 }
     padding-left: 2px;
     padding-right: 2px;
   }
+
+  .status__collapse-button.active > .fa-angle-double-up {
+    transform: rotate(-180deg);
+  }
+}
+
+.no-reduce-motion .status__collapse-button {
+  &.activate {
+    & > .fa-angle-double-up {
+      animation: spring-flip-in 1s linear;
+    }
+  }
+
+  &.deactivate {
+    & > .fa-angle-double-up {
+      animation: spring-flip-out 1s linear;
+    }
+  }
 }
 
 .status__info__account {