KeyframesProgressBar.js (3425B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const { 8 createRef, 9 PureComponent, 10 } = require("resource://devtools/client/shared/vendor/react.mjs"); 11 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js"); 12 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs"); 13 14 class KeyframesProgressBar extends PureComponent { 15 static get propTypes() { 16 return { 17 addAnimationsCurrentTimeListener: PropTypes.func.isRequired, 18 animation: PropTypes.object.isRequired, 19 getAnimationsCurrentTime: PropTypes.func.isRequired, 20 removeAnimationsCurrentTimeListener: PropTypes.func.isRequired, 21 simulateAnimationForKeyframesProgressBar: PropTypes.func.isRequired, 22 timeScale: PropTypes.object.isRequired, 23 }; 24 } 25 26 constructor(props) { 27 super(props); 28 29 this._progressBarRef = createRef(); 30 31 this.onCurrentTimeUpdated = this.onCurrentTimeUpdated.bind(this); 32 } 33 34 componentDidMount() { 35 const { addAnimationsCurrentTimeListener } = this.props; 36 37 this.setupAnimation(this.props); 38 addAnimationsCurrentTimeListener(this.onCurrentTimeUpdated); 39 } 40 41 // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507 42 UNSAFE_componentWillReceiveProps(nextProps) { 43 const { animation, getAnimationsCurrentTime, timeScale } = nextProps; 44 45 this.setupAnimation(nextProps); 46 this.updateOffset(getAnimationsCurrentTime(), animation, timeScale); 47 } 48 49 componentWillUnmount() { 50 const { removeAnimationsCurrentTimeListener } = this.props; 51 52 removeAnimationsCurrentTimeListener(this.onCurrentTimeUpdated); 53 this.simulatedAnimation = null; 54 } 55 56 onCurrentTimeUpdated(currentTime) { 57 const { animation, timeScale } = this.props; 58 this.updateOffset(currentTime, animation, timeScale); 59 } 60 61 updateOffset(currentTime, animation, timeScale) { 62 const { createdTime, playbackRate } = animation.state; 63 64 const time = 65 (timeScale.minStartTime + currentTime - createdTime) * playbackRate; 66 67 if (isNaN(time)) { 68 // Setting an invalid currentTime will throw so bail out if time is not a number for 69 // any reason. 70 return; 71 } 72 73 this.simulatedAnimation.currentTime = time; 74 const position = 75 this.simulatedAnimation.effect.getComputedTiming().progress; 76 77 // onCurrentTimeUpdated is bound to requestAnimationFrame. 78 // As to update the component too frequently has performance issue if React controlled, 79 // update raw component directly. See Bug 1699039. 80 this._progressBarRef.current.style.marginInlineStart = `${position * 100}%`; 81 } 82 83 setupAnimation(props) { 84 const { animation, simulateAnimationForKeyframesProgressBar } = props; 85 86 if (this.simulatedAnimation) { 87 this.simulatedAnimation.cancel(); 88 } 89 90 const timing = Object.assign({}, animation.state, { 91 iterations: animation.state.iterationCount || Infinity, 92 }); 93 94 this.simulatedAnimation = simulateAnimationForKeyframesProgressBar(timing); 95 } 96 97 render() { 98 return dom.div( 99 { className: "keyframes-progress-bar-area" }, 100 dom.div({ 101 className: "indication-bar keyframes-progress-bar", 102 ref: this._progressBarRef, 103 }) 104 ); 105 } 106 } 107 108 module.exports = KeyframesProgressBar;