Add animated, bouncing/fading ellipses to indicate activity
authorCarl Worth <cworth@cworth.org>
Sun, 28 Jun 2020 17:46:00 +0000 (10:46 -0700)
committerCarl Worth <cworth@cworth.org>
Sun, 28 Jun 2020 17:46:00 +0000 (10:46 -0700)
So that all users can easily see if other players are actively
participating in the answering/judging phase or if they have gone
totally idle.

empathy/empathy.css
empathy/empathy.jsx

index 704b54a5ac1759a1be929c4123a3c59cd8f343d4..9692274e3a7edd7eaaa4e4976d966e0c91df4cae 100644 (file)
     background-color: var(--accent-color-bright);
     color: var(--text-fg-on-accent-bright);
 }
+
+@keyframes bounce {
+    0% {
+        transform: translateY(0);
+        animation-timing-function: cubic-bezier(0.333, 0.667, 0.667, 1);
+    }
+    20% {
+        transform: translateY(-5px);
+        animation-timing-function: cubic-bezier(0.333, 0, 0,667, 0.333);
+    }
+    40% {
+        transform: translateY(0);
+    }
+    100% {
+        transform: translateY(0);
+    }
+}
+
+.typing span {
+    font-size: 150%;
+    line-height: 0;
+
+    display: inline-block;
+    animation-name: bounce;
+    animation-duration: 2s;
+    animation-iteration-count: infinite;
+    animation-timing-function: linear;
+}
+
+.typing span:nth-child(2) {
+    animation-delay: .2s;
+}
+
+.typing span:nth-child(3) {
+    animation-delay: .4s;
+}
+
+.typing.active span {
+    opacity: 1.0;
+}
+
+.typing.idle span {
+    opacity: 0.0;
+    transition-property: opacity;
+    transition-duration: 6s;
+    transition-delay: 3s;
+}
index 163b977cbb8c474560a4dfa9cf24c1a03926e56b..143acd930824a564af6a7153cbbd3b37006a54fc 100644 (file)
@@ -585,9 +585,14 @@ class Ambiguities extends React.PureComponent {
                 <li
                   key={player}
                 >
-                  {player}
-                  {this.props.players_judging[player] ?
-                   <span className="typing"/> : null }
+                  {player}{' '}
+                  <span className=
+                  {this.props.players_judging[player].active ?
+                   "typing active"
+                   :
+                   "typing idle"}>
+                    <span>{'.'}</span><span>{'.'}</span><span>{'.'}</span>
+                  </span>
                 </li>
               );
             })}
@@ -760,9 +765,14 @@ class ActivePrompt extends React.PureComponent {
                 <li
                   key={player}
                 >
-                  {player}
-                  {this.props.players_answering[player] ?
-                   <span className="typing"/> : null }
+                  {player}{' '}
+                  <span className=
+                  {this.props.players_answering[player].active ?
+                   "typing active"
+                   :
+                   "typing idle"}>
+                    <span>{'.'}</span><span>{'.'}</span><span>{'.'}</span>
+                  </span>
                 </li>
               );
             })}
@@ -954,12 +964,29 @@ class Game extends React.PureComponent {
   }
 
   set_player_answering(player) {
+    /* Set the player as actively answering now. */
     this.setState({
       players_answering: {
         ...this.state.players_answering,
         [player]: {active: true}
       }
     });
+    /* And arrange to have them marked idle very shortly.
+     *
+     * Note: This timeout is intentionally very, very short. We only
+     * need it long enough that the browser has latched onto the state
+     * change to "active" above. We actually use a CSS transition
+     * delay to control the user-perceptible length of time after
+     * which an active player appears inactive.
+     */
+    setTimeout(() => {
+      this.setState({
+        players_answering: {
+          ...this.state.players_answering,
+          [player]: {active: false}
+        }
+      });
+    }, 100);
   }
 
   set_answering_idle(value) {
@@ -1019,12 +1046,30 @@ class Game extends React.PureComponent {
   }
 
   set_player_judging(player) {
+    /* Set the player as actively judging now. */
     this.setState({
       players_judging: {
         ...this.state.players_judging,
         [player]: {active: true}
       }
     });
+    /* And arrange to have them marked idle very shortly.
+     *
+     * Note: This timeout is intentionally very, very short. We only
+     * need it long enough that the browser has latched onto the state
+     * change to "active" above. We actually use a CSS transition
+     * delay to control the user-perceptible length of time after
+     * which an active player appears inactive.
+     */
+    setTimeout(() => {
+      this.setState({
+        players_judging: {
+          ...this.state.players_judging,
+          [player]: {active: false}
+        }
+      });
+    }, 100);
+
   }
 
   set_judging_idle(value) {