improve animation performance of countup by reffing element directly (vs state update)

This commit is contained in:
Danny 2018-06-04 14:34:44 +02:00
parent c2e63a2dd3
commit 13a1222f54

View File

@ -4,40 +4,31 @@ import { h, Component } from 'preact';
import * as numbers from '../lib/numbers.js'; import * as numbers from '../lib/numbers.js';
import { bind } from 'decko'; import { bind } from 'decko';
const duration = 1000;
const easeOutQuint = function (t) { return 1+(--t)*t*t*t*t };
class CountWidget extends Component { class CountWidget extends Component {
constructor(props) {
super(props)
this.state = {
value: "-"
}
}
componentWillReceiveProps(newProps, newState) { componentWillReceiveProps(newProps, newState) {
if(newProps.value == this.props.value) { if(newProps.value == this.props.value) {
return; return;
} }
this.countUp(newProps.value); this.countUp(this.props.value || 0, newProps.value);
} }
// TODO: Move to component of its own // TODO: Move to component of its own
@bind @bind
countUp(toValue) { countUp(fromValue, toValue) {
const duration = 1000; const format = this.formatValue.bind(this);
const easeOutQuint = function (t) { return 1+(--t)*t*t*t*t }; const startValue = isFinite(fromValue) ? fromValue : 0;
const setState = this.setState.bind(this); const numberEl = this.numberEl;
const startValue = isFinite(this.state.value) ? this.state.value : 0;
const diff = toValue - startValue; const diff = toValue - startValue;
let startTime = performance.now(); let startTime = performance.now();
const tick = function(t) { const tick = function(t) {
let progress = Math.min(( t - startTime ) / duration, 1); let progress = Math.min(( t - startTime ) / duration, 1);
let newValue = startValue + (easeOutQuint(progress) * diff); let newValue = startValue + (easeOutQuint(progress) * diff);
setState({ numberEl.textContent = format(newValue)
value: newValue,
})
if(progress < 1) { if(progress < 1) {
window.requestAnimationFrame(tick); window.requestAnimationFrame(tick);
@ -47,30 +38,35 @@ class CountWidget extends Component {
window.requestAnimationFrame(tick); window.requestAnimationFrame(tick);
} }
render(props, state) { @bind
formatValue(value) {
let formattedValue = "-"; let formattedValue = "-";
if(isFinite(state.value)) { if(isFinite(value)) {
switch(props.format) { switch(this.props.format) {
case "percentage": case "percentage":
formattedValue = numbers.formatPercentage(state.value) formattedValue = numbers.formatPercentage(value)
break; break;
default: default:
case "number": case "number":
formattedValue = numbers.formatPretty(Math.round(state.value)) formattedValue = numbers.formatPretty(Math.round(value))
break; break;
case "duration": case "duration":
formattedValue = numbers.formatDuration(state.value) formattedValue = numbers.formatDuration(value)
break; break;
} }
} }
return formattedValue;
}
render(props, state) {
return ( return (
<div class={"totals-detail " + ( props.loading ? "loading" : '')}> <div class={"totals-detail " + ( props.loading ? "loading" : '')}>
<div class="total-heading">{props.title}</div> <div class="total-heading">{props.title}</div>
<div class="total-numbers">{formattedValue}</div> <div class="total-numbers" ref={(e) => { this.numberEl = e; }}>{this.formatValue(props.value)}</div>
</div> </div>
) )
} }