diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles
index 1c0a3d1254..d9177bebb5 100644
--- a/.eslintignore.errorfiles
+++ b/.eslintignore.errorfiles
@@ -1,7 +1,7 @@
# autogenerated file: run scripts/generate-eslint-error-ignore-file to update.
src/Markdown.js
-src/Velociraptor.js
+src/NodeAnimator.js
src/components/structures/RoomDirectory.js
src/components/views/rooms/MemberList.js
src/ratelimitedfunc.js
diff --git a/package.json b/package.json
index 07975eafd7..7c190c68bf 100644
--- a/package.json
+++ b/package.json
@@ -102,7 +102,6 @@
"tar-js": "^0.3.0",
"text-encoding-utf-8": "^1.0.2",
"url": "^0.11.0",
- "velocity-animate": "^2.0.6",
"what-input": "^5.2.10",
"zxcvbn": "^4.4.2"
},
diff --git a/res/css/_common.scss b/res/css/_common.scss
index 87fa4578b1..d6f85edb86 100644
--- a/res/css/_common.scss
+++ b/res/css/_common.scss
@@ -28,6 +28,16 @@ $MessageTimestamp_width_hover: calc($MessageTimestamp_width - 2 * $EventTile_e2e
:root {
font-size: 10px;
+
+ --transition-short: .1s;
+ --transition-standard: .3s;
+}
+
+@media (prefers-reduced-motion) {
+ :root {
+ --transition-short: 0;
+ --transition-standard: 0;
+ }
}
html {
diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss
index 50adaea305..2b3e179c54 100644
--- a/res/css/views/rooms/_EventTile.scss
+++ b/res/css/views/rooms/_EventTile.scss
@@ -283,6 +283,10 @@ $left-gutter: 64px;
display: inline-block;
height: $font-14px;
width: $font-14px;
+
+ transition:
+ left var(--transition-short) ease-out,
+ top var(--transition-standard) ease-out;
}
.mx_EventTile_readAvatarRemainder {
diff --git a/src/Velociraptor.js b/src/NodeAnimator.js
similarity index 59%
rename from src/Velociraptor.js
rename to src/NodeAnimator.js
index 2da54babe5..8456e6e9fd 100644
--- a/src/Velociraptor.js
+++ b/src/NodeAnimator.js
@@ -1,16 +1,15 @@
import React from "react";
import ReactDom from "react-dom";
-import Velocity from "velocity-animate";
import PropTypes from 'prop-types';
/**
- * The Velociraptor contains components and animates transitions with velocity.
+ * The NodeAnimator contains components and animates transitions.
* It will only pick up direct changes to properties ('left', currently), and so
* will not work for animating positional changes where the position is implicit
* from DOM order. This makes it a lot simpler and lighter: if you need fully
* automatic positional animation, look at react-shuffle or similar libraries.
*/
-export default class Velociraptor extends React.Component {
+export default class NodeAnimator extends React.Component {
static propTypes = {
// either a list of child nodes, or a single child.
children: PropTypes.any,
@@ -20,14 +19,10 @@ export default class Velociraptor extends React.Component {
// a list of state objects to apply to each child node in turn
startStyles: PropTypes.array,
-
- // a list of transition options from the corresponding startStyle
- enterTransitionOpts: PropTypes.array,
};
static defaultProps = {
startStyles: [],
- enterTransitionOpts: [],
};
constructor(props) {
@@ -41,6 +36,18 @@ export default class Velociraptor extends React.Component {
this._updateChildren(this.props.children);
}
+ /**
+ *
+ * @param {HTMLElement} node element to apply styles to
+ * @param {object} styles a key/value pair of CSS properties
+ * @returns {void}
+ */
+ _applyStyles(node, styles) {
+ Object.entries(styles).forEach(([property, value]) => {
+ node.style[property] = value;
+ });
+ }
+
_updateChildren(newChildren) {
const oldChildren = this.children || {};
this.children = {};
@@ -50,17 +57,8 @@ export default class Velociraptor extends React.Component {
const oldNode = ReactDom.findDOMNode(this.nodes[old.key]);
if (oldNode && oldNode.style.left !== c.props.style.left) {
- Velocity(oldNode, { left: c.props.style.left }, this.props.transition).then(() => {
- // special case visibility because it's nonsensical to animate an invisible element
- // so we always hidden->visible pre-transition and visible->hidden after
- if (oldNode.style.visibility === 'visible' && c.props.style.visibility === 'hidden') {
- oldNode.style.visibility = c.props.style.visibility;
- }
- });
- //console.log("translation: "+oldNode.style.left+" -> "+c.props.style.left);
- }
- if (oldNode && oldNode.style.visibility === 'hidden' && c.props.style.visibility === 'visible') {
- oldNode.style.visibility = c.props.style.visibility;
+ this._applyStyles(oldNode, { left: c.props.style.left });
+ // console.log("translation: "+oldNode.style.left+" -> "+c.props.style.left);
}
// clone the old element with the props (and children) of the new element
// so prop updates are still received by the children.
@@ -94,33 +92,22 @@ export default class Velociraptor extends React.Component {
this.props.startStyles.length > 0
) {
const startStyles = this.props.startStyles;
- const transitionOpts = this.props.enterTransitionOpts;
const domNode = ReactDom.findDOMNode(node);
// start from startStyle 1: 0 is the one we gave it
// to start with, so now we animate 1 etc.
- for (var i = 1; i < startStyles.length; ++i) {
- Velocity(domNode, startStyles[i], transitionOpts[i-1]);
- /*
- console.log("start:",
- JSON.stringify(transitionOpts[i-1]),
- "->",
- JSON.stringify(startStyles[i]),
- );
- */
+ for (let i = 1; i < startStyles.length; ++i) {
+ this._applyStyles(domNode, startStyles[i]);
+ // console.log("start:"
+ // JSON.stringify(startStyles[i]),
+ // );
}
// and then we animate to the resting state
- Velocity(domNode, restingStyle,
- transitionOpts[i-1])
- .then(() => {
- // once we've reached the resting state, hide the element if
- // appropriate
- domNode.style.visibility = restingStyle.visibility;
- });
+ setTimeout(() => {
+ this._applyStyles(domNode, restingStyle);
+ }, 0);
// console.log("enter:",
- // JSON.stringify(transitionOpts[i-1]),
- // "->",
// JSON.stringify(restingStyle));
}
this.nodes[k] = node;
@@ -128,9 +115,7 @@ export default class Velociraptor extends React.Component {
render() {
return (
-
- { Object.values(this.children) }
-
+ <>{ Object.values(this.children) }>
);
}
}
diff --git a/src/VelocityBounce.js b/src/VelocityBounce.js
deleted file mode 100644
index ffbf7de829..0000000000
--- a/src/VelocityBounce.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import Velocity from "velocity-animate";
-
-// courtesy of https://github.com/julianshapiro/velocity/issues/283
-// We only use easeOutBounce (easeInBounce is just sort of nonsensical)
-function bounce( p ) {
- let pow2;
- let bounce = 4;
-
- while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {
- // just sets pow2
- }
- return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
-}
-
-Velocity.Easings.easeOutBounce = function(p) {
- return 1 - bounce(1 - p);
-};
diff --git a/src/components/views/rooms/ReadReceiptMarker.js b/src/components/views/rooms/ReadReceiptMarker.js
index 7473aac7cd..709e6a0db0 100644
--- a/src/components/views/rooms/ReadReceiptMarker.js
+++ b/src/components/views/rooms/ReadReceiptMarker.js
@@ -17,22 +17,13 @@ limitations under the License.
import React, {createRef} from 'react';
import PropTypes from 'prop-types';
-import '../../../VelocityBounce';
import { _t } from '../../../languageHandler';
import {formatDate} from '../../../DateUtils';
-import Velociraptor from "../../../Velociraptor";
+import NodeAnimator from "../../../NodeAnimator";
import * as sdk from "../../../index";
import {toPx} from "../../../utils/units";
import {replaceableComponent} from "../../../utils/replaceableComponent";
-let bounce = false;
-try {
- if (global.localStorage) {
- bounce = global.localStorage.getItem('avatar_bounce') == 'true';
- }
-} catch (e) {
-}
-
@replaceableComponent("views.rooms.ReadReceiptMarker")
export default class ReadReceiptMarker extends React.PureComponent {
static propTypes = {
@@ -115,7 +106,18 @@ export default class ReadReceiptMarker extends React.PureComponent {
// we've already done our display - nothing more to do.
return;
}
+ this._animateMarker();
+ }
+ componentDidUpdate(prevProps) {
+ const differentLeftOffset = prevProps.leftOffset !== this.props.leftOffset;
+ const visibilityChanged = prevProps.hidden !== this.props.hidden;
+ if (differentLeftOffset || visibilityChanged) {
+ this._animateMarker();
+ }
+ }
+
+ _animateMarker() {
// treat new RRs as though they were off the top of the screen
let oldTop = -15;
@@ -139,42 +141,18 @@ export default class ReadReceiptMarker extends React.PureComponent {
}
const startStyles = [];
- const enterTransitionOpts = [];
if (oldInfo && oldInfo.left) {
// start at the old height and in the old h pos
-
startStyles.push({ top: startTopOffset+"px",
left: toPx(oldInfo.left) });
-
- const reorderTransitionOpts = {
- duration: 100,
- easing: 'easeOut',
- };
-
- enterTransitionOpts.push(reorderTransitionOpts);
}
- // then shift to the rightmost column,
- // and then it will drop down to its resting position
- //
- // XXX: We use a small left value to trick velocity-animate into actually animating.
- // This is a very annoying bug where if it thinks there's no change to `left` then it'll
- // skip applying it, thus making our read receipt at +14px instead of +0px like it
- // should be. This does cause a tiny amount of drift for read receipts, however with a
- // value so small it's not perceived by a user.
- // Note: Any smaller values (or trying to interchange units) might cause read receipts to
- // fail to fall down or cause gaps.
- startStyles.push({ top: startTopOffset+'px', left: '1px' });
- enterTransitionOpts.push({
- duration: bounce ? Math.min(Math.log(Math.abs(startTopOffset)) * 200, 3000) : 300,
- easing: bounce ? 'easeOutBounce' : 'easeOutCubic',
- });
+ startStyles.push({ top: startTopOffset+'px', left: '0' });
this.setState({
suppressDisplay: false,
startStyles: startStyles,
- enterTransitionOpts: enterTransitionOpts,
});
}
@@ -187,7 +165,6 @@ export default class ReadReceiptMarker extends React.PureComponent {
const style = {
left: toPx(this.props.leftOffset),
top: '0px',
- visibility: this.props.hidden ? 'hidden' : 'visible',
};
let title;
@@ -210,9 +187,8 @@ export default class ReadReceiptMarker extends React.PureComponent {
}
return (
-
+
-
+
);
}
}
diff --git a/yarn.lock b/yarn.lock
index cfae85608a..66329cfa89 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8144,11 +8144,6 @@ validate-npm-package-license@^3.0.1:
spdx-correct "^3.0.0"
spdx-expression-parse "^3.0.0"
-velocity-animate@^2.0.6:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/velocity-animate/-/velocity-animate-2.0.6.tgz#1811ca14df7fbbef05740256f6cec0fd1b76575f"
- integrity sha512-tU+/UtSo3GkIjEfk2KM4e24DvpgX0+FzfLr7XqNwm9BCvZUtbCHPq/AFutx/Mkp2bXlUS9EcX8yxu8XmzAv2Kw==
-
verror@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"