#javascript
#javascript
Вопрос:
допустим, мы хотим анимировать подсчет от 10 до 100, используя CountUp.js мы должны сделать что-то вроде этого:
Вопрос в том, как я могу разрешить обещание после завершения подсчета.
createMainCountUp(); // initiate once
function createMainCountUp(){
const div = document.createElement("div");
div.id = "percentCounterTarget";
document.body.appendChild(div);
}
promisedFunc(); // this is a function I want
async function promisedFunc() {
console.log('counting has started...');
await Counting(); // I want to resolve this function when counting has finished
console.log('counting has finished!');
}
// here is the main function to start counting
function Counting(){
dial = new CountUp('percentCounterTarget', 10, 100, 4, 5);
dial.start();
//easing dial function
function CountUp(target, startVal, endVal, decimals, duration, options) {
var self = this;
// default options
self.options = {
useEasing: true, // toggle easing
useGrouping: true, // 1,000,000 vs 1000000
separator: ',', // character to use as a separator
decimal: '.', // character to use as a decimal
easingFn: fastEase, //normalEase, //slowEase, // optional custom easing function
formattingFn: formatNumber, // optional custom formatting function, default is formatNumber above
prefix: '', // optional text before the result
suffix: '', // optional text after the result
numerals: [] // optionally pass an array of custom numerals for 0-9
};
// extend default options with passed options object
if (options amp;amp; typeof options === 'object') {
for (var key in self.options) {
if (options.hasOwnProperty(key) amp;amp; options[key] !== null) {
self.options[key] = options[key];
}
}
}
if (self.options.separator === '') {
self.options.useGrouping = false;
} else {
// ensure the separator is a string (formatNumber assumes this)
self.options.separator = '' self.options.separator;
}
// make sure requestAnimationFrame and cancelAnimationFrame are defined
// polyfill for browsers without native support
// by Opera engineer Erik Möller
var lastTime = 0;
var vendors = ['webkit', 'moz', 'ms', 'o'];
for (var x = 0; x < vendors.length amp;amp; !window.requestAnimationFrame; x) {
window.requestAnimationFrame = window[vendors[x] 'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x] 'CancelAnimationFrame'] || window[vendors[x] 'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() {
callback(currTime timeToCall);
}, timeToCall);
lastTime = currTime timeToCall;
return id;
};
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}
function formatNumber(num) {
var neg = (num < 0),
x, x1, x2, x3, i, len;
num = Math.abs(num).toFixed(self.decimals);
num = '';
x = num.split('.');
x1 = x[0];
x2 = x.length > 1 ? self.options.decimal x[1] : '';
if (self.options.useGrouping) {
x3 = '';
for (i = 0, len = x1.length; i < len; i) {
if (i !== 0 amp;amp; ((i % 3) === 0)) {
x3 = self.options.separator x3;
}
x3 = x1[len - i - 1] x3;
}
x1 = x3;
}
// optional numeral substitution
if (self.options.numerals.length) {
x1 = x1.replace(/[0-9]/g, function(w) {
return self.options.numerals[ w];
})
x2 = x2.replace(/[0-9]/g, function(w) {
return self.options.numerals[ w];
})
}
return (neg ? '-' : '') self.options.prefix x1 x2 self.options.suffix;
}
//Ease functions
function fastEase(t, b, c, d) {
return c * (-Math.pow(2, -10 * t / d) 1) * 1024 / 1023 b;
}
function normalEase(t, b, c, d) {
var ts = (t /= d) * t;
var tc = ts * t;
return b c * (tc * ts -5 * ts * ts 10 * tc -10 * ts 5 * t);
}
function slowEase(t, b, c, d) {
var ts = (t /= d) * t;
var tc = ts * t;
return b c * (tc -3 * ts 3 * t);
}
function ensureNumber(n) {
return (typeof n === 'number' amp;amp; !isNaN(n));
}
self.initialize = function() {
if (self.initialized) return true;
self.error = '';
self.d = (typeof target === 'string') ? document.getElementById(target) : target;
if (!self.d) {
self.error = '[CountUp] target is null or undefined'
return false;
}
self.startVal = Number(startVal);
self.endVal = Number(endVal);
// error checks
if (ensureNumber(self.startVal) amp;amp; ensureNumber(self.endVal)) {
self.decimals = Math.max(0, decimals || 0);
self.dec = Math.pow(10, self.decimals);
self.duration = Number(duration) * 1000 || 2000;
self.countDown = (self.startVal > self.endVal);
self.frameVal = self.startVal;
self.initialized = true;
return true;
} else {
self.error = '[CountUp] startVal (' startVal ') or endVal (' endVal ') is not a number';
return false;
}
};
// Print value to target
self.printValue = function(value) {
var result = self.options.formattingFn(value);
console.log(value);
};
self.count = function(timestamp) {
if (!self.startTime) {
self.startTime = timestamp;
}
self.timestamp = timestamp;
var progress = timestamp - self.startTime;
self.remaining = self.duration - progress;
// to ease or not to ease
if (self.options.useEasing) {
if (self.countDown) {
self.frameVal = self.startVal - self.options.easingFn(progress, 0, self.startVal - self.endVal, self.duration);
} else {
self.frameVal = self.options.easingFn(progress, self.startVal, self.endVal - self.startVal, self.duration);
}
} else {
if (self.countDown) {
self.frameVal = self.startVal - ((self.startVal - self.endVal) * (progress / self.duration));
} else {
self.frameVal = self.startVal (self.endVal - self.startVal) * (progress / self.duration);
}
}
// don't go past endVal since progress can exceed duration in the last frame
if (self.countDown) {
self.frameVal = (self.frameVal < self.endVal) ? self.endVal : self.frameVal;
} else {
self.frameVal = (self.frameVal > self.endVal) ? self.endVal : self.frameVal;
}
// decimal
self.frameVal = Math.round(self.frameVal * self.dec) / self.dec;
// format and print value
self.printValue(self.frameVal);
// whether to continue
if (progress < self.duration) {
self.rAF = requestAnimationFrame(self.count);
} else {
if (self.callback) self.callback();
}
};
// start your animation
self.start = function(callback) {
if (!self.initialize()) return;
self.callback = callback;
self.rAF = requestAnimationFrame(self.count);
};
// pass a new endVal and start animation
self.update = function(newEndVal) {
if (!self.initialize()) return;
newEndVal = Number(newEndVal);
if (!ensureNumber(newEndVal)) {
self.error = '[CountUp] update() - new endVal is not a number: ' newEndVal;
return;
}
self.error = '';
if (newEndVal === self.frameVal) return;
cancelAnimationFrame(self.rAF);
self.paused = false;
delete self.startTime;
self.startVal = self.frameVal;
self.endVal = newEndVal;
self.countDown = (self.startVal > self.endVal);
self.rAF = requestAnimationFrame(self.count);
};
// format startVal on initialization
if (self.initialize()) self.printValue(self.startVal);
}
}
Примечание: фрагмент StackOverflow неправильно отображает журналы. код работает нормально.
Ответ №1:
Поскольку CountUp.start()
можно использовать функцию обратного вызова, вы могли бы создать и вернуть обещание, которое вызывает resolve
функцию после завершения обратного отсчета.
function counting() {
return new Promise((resolve, reject) => {
const dial = new CountUp(...);
dial.start(resolve);
});
}
Редактировать: после завершения обратного отсчета CountUp вызовет функцию обратного вызова, в которую вы передаете .start()
. Вы можете увидеть это в копии CountUp.js
, которую вы опубликовали в своем фрагменте — в комментарии «следует ли продолжать» в self.count
, он вызывает функцию обратного вызова после завершения обратного отсчета. Мы можем использовать это в обещании, вызвав resolve
после завершения обратного отсчета.