Страницы

Поиск по вопросам

Показаны сообщения с ярлыком таймер. Показать все сообщения
Показаны сообщения с ярлыком таймер. Показать все сообщения

воскресенье, 12 января 2020 г.

Как имитировать тормозной браузер, искажающий JS timeout / interval?

#javascript #таймер


Пишу таймер, который тикает раз в секунду, и должен это делать с точностью до 1/200
секунды. Даже если браузер тупит и комп слабый.

Поэтому ориентируюсь не на setTimeout( func, 1000), а на более частые вызовы и системное
время в миллисекундах.

Вопрос – как тестировать? Надо как-то имитировать разную степень тормознутости системы,
чтобы оценить поведение скрипта в разных условиях. И записывать в консоль отклонения
в миллисекундах от идеального времени каждого тика.



Upd. В этом алгоритме я внутри каждой секунды несколько раз запускаю setTimeout()
на время, меньшее 1000, и зависящее от расхождения фактического и ожидаемого времени
очередного запуска, и времени оставшегося до «конца» секунды.

Гипотеза: тормоза системы не меняются значительно в «окне» одной секунды. Тогда,
выполнив несколько коротких (200-500ms) предварительных setTimeout(), можно чуть правильнее
предположить лаг следующего, и точнее попасть в нужное время

Upd. 2 нашёл интересный совет – тестировать в виртуальной машине, в которой урезать
ресурсы.
    


Ответы

Ответ 1



!function() { var delta = 100; // Интервал будет плюс-минус 100 милисекунд var ost = setTimeout setTimeout = function(f, time) { time += (Math.random() * 2 - 1) * delta ost(f, time) } }() Но, вообще говоря, ваше решение довольно странное. Более частые вызовы приведут лишь к большей загрузки и так тормознутой системы, и не помогут отмерить интервалы точнее. Браузер же "тупит" не просто так - а потому что процессор занят чем-то еще. И если в некоторый момент времени процессор занят - то скрипт проснется невовремя независимо от того, на 20 миллисекунд скрипт засыпал или на 1000.

Ответ 2



Возможно вам стоит загрузить систему параллельно дополнительными вычислениями на основе тех же таумаутов. Например что-то вроде этого function noise() { var count = Math.random() * 1000000; // какая-то нагрузка for(var i=0;i

Ответ 3



Если на браузере chrome переключиться в режим просмотр кода (инспектор), переходите в режим телефона/планшета и выбираете скорость 50 КВ/s. Тормоза обеспечены. Скриншот с красными стрелками http://prnt.sc/8x4el4

среда, 25 декабря 2019 г.

Как отловить изменения переменной в питоне?

#python #таймер


У меня есть приложение для игры в камень-ножницы-бумага. Для выбора своей фигуры
игроку дается 10 секунд, которые отсчитываются в следующем цикле. Мне нужно прервать
цикл когда игрок выбрал фигуру, или если по истечении времени он этого не сделал —
засчитать поражение. Как мне отловить ввод? Или может есть вариант лучше?

import time
global x;
for i in range(10, 0, -1):
    time.sleep(1)

    


Ответы

Ответ 1



Вы можете использовать select: import sys, select timeout = 10 i, o, e = select.select( [sys.stdin], [], [], timeout) if (i): print("Вы ввели", sys.stdin.readline()) else: print("Вы ничего не ввели") select будет ожидать ввод из sys.stdin, а когда он последует завершит свою работу. После чего мы из stdin и прочитаем то, что туда ввели.

Ответ 2



import queue x = queue.Queue(maxsize=1) try: i = x.get(timeout=10) print('выбор', i) except queue.Empty: print('поражение')

воскресенье, 22 декабря 2019 г.

Многозадачность компьютеров

#многопоточность #таймер #ос


Насколько я понимаю, процессы в компьютере выполняются не совсем параллельно, а на
самом деле быстро переключаются между собой. Но в таком случае время на завершение
всех процессов все равно остается таким же, как если бы процессы работали последовательно.
Правильно ли я понимаю, что главная фишка этой как-бы "параллельности" в том что мы
можем не дожидаться пока некий процесс закончится а перейти на другой остановив предыдущие?
Какие еще преимущества у такого принципа "параллельности"?

В таком случае, если принцип как-бы "параллельности" верен, то разве в часах на компьютере
не должно накапливаться отставание?
    


Ответы

Ответ 1



Ответ зависит от того, что делают ваши процессы или потоки (давайте для простоты дальше будем говорить о потоках, которые в оригинале зовутся thread). Потоки будут выполняться действительно одновременно, если у вас есть больше одного ядра. Но в вопросе, как я понимаю, интересует ситуация, когда ядро одно. В этом случае некоторые потоки все равно могут выполняться параллельно. Например, пока жесткий диск читает данные или сетевая карта получает пакеты, то операционная система может переключиться на другой поток, и в таких задачах будет заметен выигрыш. Если вы отправили некоторые данные на звуковую карту, то можете пока заняться другими делами, пока она не проиграет полученный участок звука. Если же у вас несколько потоков, которые заняты в основном расчетами, которые занимают ядро процессора, то в этом случае выигрыша от распараллеливания не будет, и даже будет замедление, т.к. переключение между потоками - это не очень дешевая операция.

Ответ 2



Насколько я понимаю, процессы в компьютере выполняются не совсем параллельно, а на самом деле быстро переключаются между собой. В современных многозадачных операционных системах это действительно так, но в общем случае это зависит от операционной системы. Но в таком случае время на завершение всех процессов все равно остается таким же, как если бы процессы работали последовательно. Да, если вся полезная нагрузка процессов выполняется именно на процессоре. А это далеко не всегда так. Различный ввод-вывод приводит к простою процессора. Если во время этого простоя выполнять код другого процесса, можно сэкономить время. Правда, это не учитывает расходы времени на т. н. context switch ("переключение контекста", переход управления из процесса в процесс), так что чисто ЦПУшные задачи при "распараллеливании" окажутся даже медленнее. Правильно ли я понимаю, что главная фишка этой как-бы "параллельности" в том что мы можем не дожидаться пока некий процесс закончится а перейти на другой остановив предыдущие? Да, я выше примерно это и описал :) Другие процессы могут быть и не "силой" остановлены. Они могут остановиться сами в ожидании результата от ядра ОС. Какие еще преимущества у такого принципа "параллельности"? Вообще это не "параллельность" как таковая, но хорошего перевода правильного термина, concurrency, я пока не слышал. "Конкурентность" разве что. Concurrency имеет место, когда код структурирован не для последовательного выполнения, а содержит определённые "разветвления" и "соединения", разные ветки которых могут (потенциально) исполняться параллельно. Однако не обязаны. И описанный вами механизм как раз позволяет "конкурентному" коду ужиться на одном процессоре. Достаточно часто конкурентный код используется в рамках одного процесса (в разных потоках [thread]), в котором поддерживается какой-то интерфейс пользователя (UI), а из него запускается какая-то рабочая нагрузка. Они оформляются конкурентно, чтобы интерфейсом можно было пользоваться даже когда программа занята какой-то работой. Однако это "преимущество" строится скорее на способе определения понятия "процесс". Скажем, в Linux различий между "процессом" и "потоком" минимум: у них всех есть собственные "process ID", хотя снаружи их не всегда видно, т. к. перечислители процессов стараются отдельные потоки не показывать, за ненадобностью. Но этот же принцип встречается и в несколько неожиданной форме: интерпретаторы некоторых языков, в частности Ruby (MRI), JS (V8) и Python (CPython), реализованы с применением Global Interpreter Lock, и из-за этого интерпретатор в каждый момент времени может заниматься выполнением только одного потока кода. Это существенно упрощает реализацию интерпретатора и взаимодействующего с ним кода (расширений на С, например), хотя и ставит дополнительные ограничения. В таком случае, если принцип как-бы "параллельности" верен, то разве в часах на компьютере не должно накапливаться отставание? Да нет, там всё в порядке, поддержкой часов обычно занимается отдельное устройство (RTC с батарейкой), а не процесс. Иначе как бы часы шли, когда компьютер выключен? Впрочем, это легко выяснить. К примеру, на всех моделях Raspberry Pi часы при каждом включении приходится выставлять заново. Там реализована поддержка часов при включенном устройстве, но отдельного источника питания для них нет. Похожим образом будет себя вести и обычный настольный компьютер, если извлечь из материнской платы батарейку.

пятница, 20 декабря 2019 г.

Как вызвать функцию по завершении таймера?

#таймер #cpp #функции


Я удивлён, что не нашёл ничего вменяемого по этому вопросу. Но всё же, как в с++
вызвать некую функцию по завершении таймера? Вопрос достаточно банальный однако. Подозреваю,
что тут не обойдётся без boost::bind.
Нужен аналог данного:
QTimer::singleShot(100, this, SLOT(onTimeExpired()));
    


Ответы

Ответ 1



Озадачали Вы меня. Вот тут на досуге набросал что-то. #pragma once #include #include namespace MyTimer { class Timer { private: boost::shared_ptr thr; template class _func { unsigned int dur; Callable& cb; public: _func (long long _dur, const Callable& _cb): dur (_dur), cb((Callable&) _cb) {} void operator() () { boost::this_thread::sleep(boost::posix_time::milliseconds(dur)); cb(); } }; public: template Timer (long long _dur, const Callable& _cb) { _func ff (_dur, _cb); thr.reset (new boost::thread(ff)); } ~Timer() {} Timer (const Timer& tm) { this->thr = tm.thr; } Timer operator= (const Timer& tm) { if (this != &tm) this->thr = tm.thr; return *this; } void interrupt () { thr->interrupt(); } }; } Это все оформляется в виде хедера. Используется очень просто. MyTimer::Timer Tm1 (5000, func); Первый параметр - время ожидания в миллисекундах, второй - вызываемый объект. Может быть простая функция, может быть функтор. Главное, не имеет параметров и возвращает void. Можно прервать ожидание с помощью метода interrupt() Испытывал в лине, gcc4.6 и 4.7 и icpc 12, boost 1.49 и 1.51

Ответ 2



Простейший вариант : #include void sighandler(int signum) { // Что-то делаем по прерыванию от таймера }; int main () { // Устанавливаем обработчик прерывания signal(SIGALRM, sighandler); // Задаём интервал 5 секунд alarm(5); }

Ответ 3



Вы функцию через заданное время хотите вызвать? Если в *nix, то посмотрите на man-ы timer_create, timer_delete, timer_settime, timer_gettime. Это "POSIX (interval) timers" В убунте (думаю в других тоже) в man timer_create есть пример. Можно одновременно делать несколько таймеров, которые могут быть "одноразовыми" (one short) или переодически срабатывающими. Время можно задавать как реальное, так и виртуальное (время процесса или потока). Интервал времени задается в секундах и наносекудах(!) (IMHO этим нано верить нельзя, но миллисекунды в моих тестах отрабатывались). Если интересно, могу выложить кусок кода, правда в нем вместо вызова функции посылается сигнал определенному потоку (что делать по истечении таймера - сигнал или вызов функции, задается в timer_create).

Ответ 4



Я честно не знаю "аналога", но если уж совсем ничего, то к вопросу: как в с++ вызвать некую функцию по завершении таймера? могу предложить вот такой вот недокод ;) : #include #include /* _beginthread, _endthread */ #include "Windows.h" typedef void(*run_func)(); class singleShot { public: singleShot(_time_out, run_func _func, bool run_now = true) : time_out(_time_out), func(_func) { if (run_now) run(); } static void thread_func( void *ch ) { singleShot* obj = static_cast(ch); Sleep(obj->time_out); obj->func(); _endthread(); } void run() {_beginthread(thread_func, 0, (void*)this);} void set_params(_time_out, run_func _func) {time_out=_time_out; func=_func;} int time_out; run_func func; }; void MyTimeOutFunc(); singleShot SecondUse(0, 0, false); int main(int argc, char* argv[]) { singleShot FirstUse(1000, MyTimeOutFunc); //можно и так использовать, главное не дать //удалиться объекту до выполнения функции : Sleep(2000); //Лучьше так: SecondUse.set_params(2000, MyTimeOutFunc); SecondUse.run(); system("Pause"); return 0; } void MyTimeOutFunc() { printf("\nHello world!!!\n"); }

суббота, 7 декабря 2019 г.

Как правильно внести изменения в скрипт таймера обратного отсчета времени

#javascript #jquery #дата #время #таймер


Всех приветствую! Прошу помощи. Есть скрипт таймера обратного отсчета времени до
определенной даты. Помогите, пожалуйста, внести в него изменения. Есть несколько моментов:


Необходимо добавить в скрипт таймера количество месяцев и количество лет.
Второй момент, не могу разобраться, когда таймер заканчивает отсчет, блок становится
пустым. Как сделать так, чтобы по окончании таймера, в этом блоке появлялся другой блок?
И еще один момент: скрипт ориентирован не на нашего брата, АМ и РМ, не совсем это
удобно, возможно ли это изменить, а также поменять местами в первой строке скрипта
число и месяц? Очень буду благодарен всем за любую помощь!


скрипт:

var end = new Date('12/01/2018 12:00 AM');

var _second = 1000;
var _minute = _second * 60;
var _hour = _minute * 60;
var _day = _hour * 24;
var timer;

function showRemaining() {
  var now = new Date();
  var distance = end - now;
  if (distance < 0) {
    clearInterval(timer);
    return;
  }
  var days = Math.floor(distance / _day);
  var hours = Math.floor((distance % _day) / _hour);
  var minutes = Math.floor((distance % _hour) / _minute);
  var seconds = Math.floor((distance % _minute) / _second);

  var daysLabel = pluralize(days, 'Дней');
  var hoursLabel = pluralize(hours, 'Часов');
  var minutesLabel = pluralize(minutes, 'Минут');
  var secondsLabel = pluralize(seconds, 'Секунд');

  var days = leadingZero(days);
  var hours = leadingZero(hours);
  var minutes = leadingZero(minutes);
  var seconds = leadingZero(seconds);

  function pluralize(number, text) {
    if(number === 1) {
      return text;
    } else {
      return text+''
    }
  }

  function leadingZero(number) {
    if(number < 10) {
      return '0'+number;
    } else {
      return number;
    }
  }

  $('.chart__bar--days .chart__bar-number').html(days);
  $('.chart__bar--days').css('height', ((days/360)*100)+'%');
  $('.chart__bar--days .chart__bar-label').html(daysLabel);

  $('.chart__bar--hours .chart__bar-number').html(hours);
  $('.chart__bar--hours').css('height', ((hours/24)*100)+'%');
  $('.chart__bar--hours .chart__bar-label').html(hoursLabel);

  $('.chart__bar--minutes .chart__bar-number').html(minutes);
  $('.chart__bar--minutes').css('height', ((minutes/60)*100)+'%');
  $('.chart__bar--minutes .chart__bar-label').html(minutesLabel);

  $('.chart__bar--seconds .chart__bar-number').html(seconds);
  $('.chart__bar--seconds').css('height', ((seconds/60)*100)+'%');
  $('.chart__bar--seconds .chart__bar-label').html(secondsLabel);
}

timer = setInterval(showRemaining, 1000);


html:

css: .widget { position: absolute; top: 0; bottom: 0; left: 0; right: 0; height: 100%; width: 100%; } .widget__foreground { position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; } .widget--countdown__main { position: absolute; width: 50%; left: 50px; top: 120px; font-family: PT Serif Caption,Georgia,Times New Roman,Times,serif; color: #000000; text-shadow: #fff 1px 0px, #fff 1px 1px, #fff 0px 1px, #fff -1px 1px, #fff -1px 0px, #fff -1px -1px, #fff 0px -1px, #fff 1px -1px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px; } .widget--countdown__title { font-size: 36px; line-height: 42px; font-weight: 700; margin: 0 50px 20px 0; } .widget--countdown__supporting-text { margin-bottom: 25px; line-height: 17px; width: 50%; } .widget--countdown__chart { height: 100%; width: 50%; max-width: 470px; position: absolute; right: 50px; } .chart { position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; display: flex; justify-content: space-between; } .chart__bar { width: 23%; position: relative; display: inline-block; align-self: flex-end; -webkit-transition: all linear .2s; -moz-transition: all linear .2s; -ms-transition: all linear .2s; -o-transition: all linear .2s; transition: all linear .2s; } .chart__bar-content { position: absolute; bottom: 20px; width: 100%; text-align: center; } .chart__bar-number { font-size: 36px; font-weight: 300; } .chart__bar-label { text-transform: uppercase; } .chart__bar--days { background-color: rgba(0, 191, 228, .90); box-shadow: 0px 0px 40px -5px rgba(0, 191, 228, .90); } .chart__bar--hours { background-color: rgba(249, 108, 207, .90); box-shadow: 0px 0px 40px -5px rgba(249, 108, 207, .90); } .chart__bar--minutes { background-color: rgba(0, 207, 167, .90); box-shadow: 0px 0px 40px -5px rgba(0, 207, 167, .90); } .chart__bar--seconds { background-color: rgba(238, 138, 56, .90); box-shadow: 0px 0px 40px -5px rgba(238, 138, 56, .90); } .chart__bar--days,.chart__bar--hours,.chart__bar--minutes,.chart__bar--seconds { background-image: url(pattern-timer.png); background-position: bottom right; background-repeat: repeat; }


Ответы

Ответ 1



upd.2 обновил ф-ю calendiff, чтобы правильно считала случаи, когда надо «отмотать» назад не один, а два месяца, для компенсации смещения в днях. Напр. чтобы компенсировать -31 день, добавить дни предыдущего одного месяца (30 дней) не достаточно, нужно заехать в пред-предыдущий месяц. upd. Как выяснилось, надо всё-таки считать точную «календарную» дистанцию между двух дат: и от 28 января до 28 февраля (31 день), и от 28 февраля до 28 марта (28 дней) – в обоих случаях ровно 1 месяц. Поэтому написал функцию для такого «человеческого» подсчёта разницы между двумя датами. Поскольку разметка повторяется, отличается лишь названием класса, вынес её генерацию в функцию. jQuery элементы достаточно найти один раз, и затем обращаться к сохранённым, вместо того, чтобы при каждом вызове заново искать в DOM-дереве. Добавил функцию для склонения существительных, в зависимости от числа (День, Дня, Дней). В стили добавил цвета для колонок года и месяца. // допустимые форматы времени: // https://tools.ietf.org/html/rfc2822#page-14 // https://www.w3.org/TR/NOTE-datetime // Напр. 1 декабря 2018, полдень по Москве: var end = new Date('2018-12-01T12:00+03:00') ,offset = (new Date().getTimezoneOffset())*6E4 ,timer ,$el ,key ,html = '' ; // generate markup html = ['years','months','days','hours','minutes','seconds'] .reduce( function(p,c){ return p += makeHtml(c);}, ''); $('.widget--countdown__chart > .chart').html(html); $el = { // найти один раз и запомнить Y: { n: $('.chart__bar--years .chart__bar-number'), l: $('.chart__bar--years .chart__bar-label'), b: $('.chart__bar--years') } ,M: { n: $('.chart__bar--months .chart__bar-number'), l: $('.chart__bar--months .chart__bar-label'), b: $('.chart__bar--months') } ,D: { n: $('.chart__bar--days .chart__bar-number'), l: $('.chart__bar--days .chart__bar-label'), b: $('.chart__bar--days') } ,h: { n: $('.chart__bar--hours .chart__bar-number'), l: $('.chart__bar--hours .chart__bar-label'), b: $('.chart__bar--hours') } ,m: { n: $('.chart__bar--minutes .chart__bar-number'), l: $('.chart__bar--minutes .chart__bar-label'), b: $('.chart__bar--minutes') } ,s: { n: $('.chart__bar--seconds .chart__bar-number'), l: $('.chart__bar--seconds .chart__bar-label'), b: $('.chart__bar--seconds') } } /** * генерирует html одного блока */ function makeHtml(key) { return [ '
', '
', '
', '
', '
', '
', ].join("\n").replace(/%KEY%/g, key); } function showRemaining() { var start = new Date(), r; if( end - start < 0) return timeHasCome(); r = calendiff( start, end); render( 'Y', r.years, ['Год','Года','Лет'], 3); // сколько максимум лет может быть? render( 'M', r.months, ['Месяц','Месяца','Месяцев'], 12); render( 'D', r.days, ['День','Дня','Дней'], 31); render( 'h', r.hours, ['Час','Часа','Часов'], 24); render( 'm', r.minutes, ['Минута','Минуты','Минут'], 60); render( 's', r.seconds, ['Секунда','Секунды','Секунд'], 60); } /** * Когда отсчёт закончился, или время уже прошло */ function timeHasCome() { if(timer) { window.clearInterval( timer); timer = undefined; } $('.widget.widget--countdown').html('
Баста, карапузики!
'); } function render( key, n, s, max) { $el[key].n.text( ('0' + n).slice(-2)); $el[key].l.text( getNumEnding(n, s)); $el[key].b.css('height', ((n / max) * 100) + '%'); } /** * Calendar difference between two dates * @param Date object dateIn start date * @param Date object dateOut end date * @return object with int properties * "years", "months", "days", "hours", "minutes" and "seconds" * * https://github.com/sergiks/calendiff.js * * @author Sergei Sokolov */ function calendiff( dateIn, dateOut) { var out = { years : 0 ,months : 0 ,days : 0 ,hours : 0 ,minutes : 0 ,seconds : 0 } ,sign = 1 ,diff = 0 ,proto ,monthsShift ,prop ; // check input proto = Object.prototype.toString.call(dateIn); if( proto !== '[object Date]') { dateIn = new Date( dateIn); if( isNaN( dateIn.getTime())) throw 'Incorrect "In" date format'; } proto = Object.prototype.toString.call(dateOut); if( proto !== '[object Date]') { dateOut = new Date( dateOut); if( isNaN( dateOut.getTime())) throw 'Incorrect "Out" date format'; } // check numeric difference diff = dateOut.getTime() - dateIn.getTime(); if( diff === 0) { return out; } else if( diff < 0) { sign = -1; dateOut = [dateIn, dateIn = dateOut][0]; // swap the dates } // calculate human-readable difference out.seconds += dateOut.getSeconds() - dateIn.getSeconds(); if( out.seconds < 0) { out.seconds += 60; out.minutes--; } out.minutes += dateOut.getMinutes() - dateIn.getMinutes(); if( out.minutes < 0) { out.minutes += 60; out.hours--; } out.hours += dateOut.getHours() - dateIn.getHours(); if( out.hours < 0) { out.hours += 24; out.days--; } // complex part: a month can have various number of days // when entering with a negative number of days, up to -31, // it might take up to two months shift back // should the preceding month only have 28, 29 or 30 days. out.days += dateOut.getDate() - dateIn.getDate(); while( out.days < 0) { monthsShift = 0; out.days += new Date( dateOut.getFullYear(), dateOut.getMonth() - monthsShift, 0).getDate(); monthsShift++; out.months--; } out.months += dateOut.getMonth() - dateIn.getMonth(); if( out.months < 0) { out.months += 12; out.years--; } out.years += dateOut.getFullYear() - dateIn.getFullYear(); // negative difference case if( sign < 0) for( prop in out) if( out[prop]) out[prop] *= -1; // avoid -0 values return out; } /** * Функция возвращает окончание для множественного числа слова на основании числа и массива окончаний * param iNumber Integer Число на основе которого нужно сформировать окончание * param aEndings Array Массив слов или окончаний для чисел (1, 4, 5), * например ['яблоко', 'яблока', 'яблок'] * return String */ function getNumEnding(iNumber, aEndings) { var sEnding, i; iNumber = iNumber % 100; if (iNumber>=11 && iNumber<=19) { sEnding=aEndings[2]; } else { i = iNumber % 10; switch (i) { case (1): sEnding = aEndings[0]; break; case (2): case (3): case (4): sEnding = aEndings[1]; break; default: sEnding = aEndings[2]; } } return sEnding; } timer = setInterval(showRemaining, 200); .widget { position: absolute; top: 0; bottom: 0; left: 0; right: 0; height: 100%; width: 100%; } .widget__foreground { position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; } .widget--countdown__main { position: absolute; width: 50%; left: 50px; top: 120px; font-family: PT Serif Caption,Georgia,Times New Roman,Times,serif; color: #000000; text-shadow: #fff 1px 0px, #fff 1px 1px, #fff 0px 1px, #fff -1px 1px, #fff -1px 0px, #fff -1px -1px, #fff 0px -1px, #fff 1px -1px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px; } .widget--countdown__title { font-size: 36px; line-height: 42px; font-weight: 700; margin: 0 50px 20px 0; } .widget--countdown__supporting-text { margin-bottom: 25px; line-height: 17px; width: 50%; } .widget--countdown__chart { height: 100%; width: 50%; max-width: 470px; position: absolute; right: 50px; } .chart { position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; display: flex; justify-content: space-between; } .chart__bar { width: 23%; position: relative; display: inline-block; align-self: flex-end; -webkit-transition: all linear .2s; -moz-transition: all linear .2s; -ms-transition: all linear .2s; -o-transition: all linear .2s; transition: all linear .2s; } .chart__bar-content { position: absolute; bottom: 20px; width: 100%; text-align: center; } .chart__bar-number { font-size: 36px; font-weight: 300; } .chart__bar-label { text-transform: uppercase; } .chart__bar--years { background-color: rgba(228, 191, 0, .90); box-shadow: 0px 0px 40px -5px rgba(228, 191, 0, .90); } .chart__bar--months { background-color: rgba(207, 108, 249, .90); box-shadow: 0px 0px 40px -5px rgba(207, 108, 249, .90); } .chart__bar--days { background-color: rgba(0, 191, 228, .90); box-shadow: 0px 0px 40px -5px rgba(0, 191, 228, .90); } .chart__bar--hours { background-color: rgba(249, 108, 207, .90); box-shadow: 0px 0px 40px -5px rgba(249, 108, 207, .90); } .chart__bar--minutes { background-color: rgba(0, 207, 167, .90); box-shadow: 0px 0px 40px -5px rgba(0, 207, 167, .90); } .chart__bar--seconds { background-color: rgba(238, 138, 56, .90); box-shadow: 0px 0px 40px -5px rgba(238, 138, 56, .90); } .chart__bar--days,.chart__bar--hours,.chart__bar--minutes,.chart__bar--seconds { background-image: url(pattern-timer.png); background-position: bottom right; background-repeat: repeat; }


Ответ 2



Сначала разберемся с терминологией. Терминология Полный год - двенадцать полных месяцев. Полный месяц - количество полных дней, равное количеству календарных дней этого месяца с учетом високосного года. Полный день - 24 полных часа. Полный час - 60 полных минут. Полная минута - 60 полных секунд. Полная секунда - 1000 миллисекунд. Алгоритм Рассмотрим алгоритм вычисления значений счетчика на примере. Допустим сегодня 2 января 2015 года время 00:01. Событие наступит 15 апреля 2016 года в 00:01. Мы выбрали одинаковое время для упрощения понимания алгоритма. Посчитаем количество полных месяцев. Начиная с февраля 2015 до марта 2016 включительно будет 14 полных месецев. Мы не стали учитывать январь 2015 и апрель 2016, так как эти месяцы не полные. Вычислим количество полных дней в этих месяцах. В январе 2015 29 полных дней и 1 неполный подолжительностью 23:59, в апреле 2016 - 14 полных и 1 неполный подолжительностью 00:01. В сумме за два месяца 44 полных дня 29 + 14 + 1. Это много, здесь, наверняка, есть еще один полный месяц. Но как определить сколько в нем должно быть полных дней? А столько, сколько в текущем месяце календарных дней. И это, пожалуй, самое сложное для понимания. Почему именно количество дней в текущем месяце? Дело в нашем восприятии месяца. Если сегодня 12-е, то для нас "через месяц" это 12-е следующего месяца. Если в текущем месяце N дней, то количество дней с 12-го до 12-го следующего месяца будет (N - 12) + 12, где (N - 12) количество дней до окончания текущего месяца, а 12 - количество дней до 12-го числа следующего месяца. Рассмотрим пару примеров (во всех примерах считаем что время 00:00): Если сегодня 31-е января, то до 28-го февраля осталось 28 дней. 1 день в январе, 27 дней в феврале. В текущем месяце 31 день. До 28 февраля 0 месяцев 28 дней. А если до 1-го марта? Тогда февраль является полным месяцем. Нам надо подсчитать количество дней между январем и мартом, как будто за январем сразу идет март. Получается 1 день в январе и 0 дней в марте. До 1-го марта 1 месяц и 1 день. Похоже на правду? Если же сегодня 1-е февраля (не високосный год), то до 31-го марта 28 дней в феврале и 30 в марте, итого 58 дней. Вычитаем февраль 58 - 28, получаем 1 месяц и 30 дней. Если же сегодня 1-е февраля (високосный год), то до 31-го марта 29 дней в феврале и 30 в марте, итого 59 дней. Вычитаем февраль 59 - 29, получаем 1 месяц и 30 дней. В последних примерах получается интересная ситуация, когда один месяц меньше, чем оставшееся количесвто дней. Но это нормально, так мы воспринимаем месяцы. Итак у нас 44 полных дня. Сейчас январь. В январе 31 день. Извлекаем еще один полный месяц 44 - 31, и остается 13 полных дней. Итого полных месяцев у нас 14 + 1 = 15, а это 1 полный год и 3 полных месяца. А общий результат 1 год 3 месяца 13 дней (время по нулям, так как оно одинаковое) Время Со временем есть проблема, которая называется Daylight saving time DST сложная тема, не будем ее касаться в рамках этой задачи. Попытаемся обойти DST, выдав локальное время за UTC. Тогда будет необходим механизм синхронизации времени, при котором возможны ситуации, когда счетчик при отсчете обратного времени по завершению последней секунды последней минуты часа либо не изменит час, либо уменьшит его на 2. Примечания к коду Я вставил в код комментария, призванные помочь пониманию алгоритма. Код не оптимизирован. Где-то производительность была проигнорирована в угоду ясности. Не стал заниматься падежами, чтобы не усложнять код. Создал функцию timeOver(), которая вызовется по окончанию времени. Но она пока только выводит alert, так как не понятно, что должно быть по истечении времени. Надо додумать отображение высоты счетчиков и их стили. Код JSFiddle и здесь: // Дата и время собятия var end = new Date(Date.parse("2017-02-21 22:45")); end = moveToUTC(end); var now = moveToUTC(new Date()); // Вынес поиск элементов HTML, чтобы не искать их в DOM в каждой итерации var $years = $('.chart__bar--years'), $yearsLabel = $years.find('.chart__bar-label'), $yearsNumber = $years.find('.chart__bar-number'), $months = $('.chart__bar--months'), $monthsLabel = $months.find('.chart__bar-label'), $monthsNumber = $months.find('.chart__bar-number'), $days = $('.chart__bar--days'), $daysLabel = $days.find('.chart__bar-label'), $daysNumber = $days.find('.chart__bar-number'), $hours = $('.chart__bar--hours'), $hoursLabel = $hours.find('.chart__bar-label'), $hoursNumber = $hours.find('.chart__bar-number'), $minutes = $('.chart__bar--minutes'), $minutesLabel = $minutes.find('.chart__bar-label'), $minutesNumber = $minutes.find('.chart__bar-number'), $seconds = $('.chart__bar--seconds'), $secondsLabel = $seconds.find('.chart__bar-label'), $secondsNumber = $seconds.find('.chart__bar-number'); var timer; function showRemaining() { if (now.getTime() >= end.getTime()) { clearInterval(timer); timeOver(); return; } now.setSeconds(now.getUTCSeconds() + 1); if (now.getUTCSeconds() % (60 * 60)) { // каждый час синхронизация времени now = moveToUTC(new Date()) } var counters = calcDistance(end, now); var years = counters['years']; var months = counters['months']; var days = counters['days']; var hours = counters['hours']; var minutes = counters['minutes']; var seconds = counters['seconds']; var yearsLabel = pluralize(years, 'Лет'); var monthsLabel = pluralize(days, 'Месяцев'); var daysLabel = pluralize(days, 'Дней'); var hoursLabel = pluralize(hours, 'Часов'); var minutesLabel = pluralize(minutes, 'Минут'); var secondsLabel = pluralize(seconds, 'Секунд'); var months = leadingZero(months); var days = leadingZero(days); var hours = leadingZero(hours); var minutes = leadingZero(minutes); var seconds = leadingZero(seconds); function pluralize(number, text) { if (number === 1) { return text; } else { return text + '' } } function leadingZero(number) { if (number < 10) { return '0' + number; } else { return number; } } $yearsNumber.html(years); //$years.css('height', ((years / ???) * 100) + '%'); // не понятно на что делить $years.css('height', 0); // пусть будет 0 $yearsLabel.html(yearsLabel); $monthsNumber.html(months); $months.css('height', ((months / 12) * 100) + '%'); $monthsLabel.html(monthsLabel); $daysNumber.html(days); $days.css('height', ((days / 30) * 100) + '%'); // примерно 30, хотя и не правильно $daysLabel.html(daysLabel); $hoursNumber.html(hours); $hours.css('height', ((hours / 24) * 100) + '%'); $hoursLabel.html(hoursLabel); $minutesNumber.html(minutes); $minutes.css('height', ((minutes / 60) * 100) + '%'); $minutesLabel.html(minutesLabel); $secondsNumber.html(seconds); $seconds.css('height', ((seconds / 60) * 100) + '%'); $secondsLabel.html(secondsLabel); } function timeOver() { alert('Время вышло'); } // Вернет количество дней в казанном месяце указанного года function daysInMonth(date) { return new Date(date.getUTCFullYear(), date.getUTCMonth(), 0).getUTCDate(); } // Вернет количество секунд прошедших с начала дня function secondsFromDayStart(date) { return date.getUTCSeconds() + 60 * date.getUTCMinutes() + 60 * 60 * date.getUTCHours(); } // Возвращает дату, смещенную в UTC // Например, на входе 2015-02-15 00:00 в локальном времени, на выходе 2015-02-15 00:00 в UTC function moveToUTC(date) { return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds())); } function calcDistance(end, now) { var secondsInDay = 60 * 60 * 24; // 60 секунд в 60 минутах в 24 часах var yearsCount = 0, monthsCount = 0, daysCount = 0, // количество полных дней hoursCount = 0, minutesCount = 0, secondsCount = 0; // Сначала разберемся со временем // Вычисляем общее количиство секунд до назначенной даты. secondsCount = secondsFromDayStart(end) - secondsFromDayStart(now); // (1) secondsFromDayStart(end) - количество секунд с начала дня события до наступления события // (2) secondsFromDayStart(now) - количество секунд, прошедших сначала сегодняшнего дня // Если secondsCount имеет отрицательное значение, // то это тот случай, когда сегодня мы уже завершили полный день и перешли к отсчету очередного дня if (secondsCount < 0) { // тогда нам нужно забрать один день, // так как дальнейшая логика основывается на том, что (1) больше либо равно (2) daysCount -= 1; // и добавить количество секунд в дне // (так как secondsCount имеет отрицательное значение, то secondsCount + secondsInDay будет всегда меньше полного дня) secondsCount += secondsInDay; } // В secondsCount сейчас находится общее количество секунд. // Разабьем это значение на компоненты: hoursCount = secondsCount / 3600 >> 0; minutesCount = (secondsCount / 60 >> 0) % 60; secondsCount = secondsCount % 60 // Займемся годом // Определим количество полных лет, в которых мы точно будем уверены yearsCount = end.getUTCFullYear() - now.getUTCFullYear() - 1; // Если окончание в 2017-м, а сегодня 2015-й, то между датами точно есть один полный год. // Если окончание в 2016-м, а сегодня 2015-й, то между датами может и не быть полного года. // Если года одинаковые, то yearsCount будет равен -1. Мы восстановим справедливость позже. // yearsCount посчитан, но не до конца // Определим количество полных месяцев monthCount = 11 - now.getUTCMonth() + end.getUTCMonth(); // Так как в js месяцы начинаются с нуля (январь = 0, февраль = 1, ...), // то end.getMonth() вернет количество прошедших полных месяцев с начала года, // а выражение 11 - now.getMonth() вернет количество полных месяцев до завершения года. // monthCount посчитан, но тоже не до конца // Займемся количеством дней // Запомним в переменной количество дней в текущем месяце, оно пригодится несколько раз var daysInCurrentMonth = daysInMonth(now); daysCount += daysInCurrentMonth - now.getUTCDate() + end.getUTCDate(); // daysInCurrentMonth - now.getDate() - количество дней до конца текущего месяца // end.getDate() - количество дней с начала месяца до дня события // Минимальное значение daysCount равняется 0, если сегодня 31-е, дата события 1-е число, // и при вычислении времени мы одолжили один день // Максимальное значени daysCount равняется 61, // в случае если сегодня 1-е число, в этом месяце 31 день, дата события 31-е число, // и при вычислении времени мы не брали один день // Извлекаем еще один полный месяц, если он есть monthCount += daysCount / daysInCurrentMonth >> 0; daysCount = daysCount % daysInCurrentMonth; // Извлекаем еще один полный год, если он есть yearsCount += monthCount / 12 >> 0; monthCount = monthCount % 12; return { years: yearsCount, months: monthCount, days: daysCount, hours: hoursCount, minutes: minutesCount, seconds: secondsCount }; } timer = setInterval(showRemaining, 1000); .widget { position: absolute; top: 0; bottom: 0; left: 0; right: 0; height: 100%; width: 100%; } .widget__foreground { position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; } .widget--countdown__main { position: absolute; width: 50%; left: 50px; top: 120px; font-family: PT Serif Caption, Georgia, Times New Roman, Times, serif; color: #000000; text-shadow: #fff 1px 0px, #fff 1px 1px, #fff 0px 1px, #fff -1px 1px, #fff -1px 0px, #fff -1px -1px, #fff 0px -1px, #fff 1px -1px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px; } .widget--countdown__title { font-size: 36px; line-height: 42px; font-weight: 700; margin: 0 50px 20px 0; } .widget--countdown__supporting-text { margin-bottom: 25px; line-height: 17px; width: 50%; } .widget--countdown__chart { height: 100%; width: 50%; max-width: 470px; position: absolute; right: 50px; } .chart { position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; display: flex; justify-content: space-between; } .chart__bar { width: 23%; position: relative; display: inline-block; align-self: flex-end; -webkit-transition: all linear .2s; -moz-transition: all linear .2s; -ms-transition: all linear .2s; -o-transition: all linear .2s; transition: all linear .2s; } .chart__bar-content { position: absolute; bottom: 20px; width: 100%; text-align: center; } .chart__bar-number { font-size: 36px; font-weight: 300; } .chart__bar-label { text-transform: uppercase; } .chart__bar--months { background-color: red; box-shadow: 0px 0px 40px -5px rgba(0, 191, 228, .90); } .chart__bar--days { background-color: rgba(0, 191, 228, .90); box-shadow: 0px 0px 40px -5px rgba(0, 191, 228, .90); } .chart__bar--hours { background-color: rgba(249, 108, 207, .90); box-shadow: 0px 0px 40px -5px rgba(249, 108, 207, .90); } .chart__bar--minutes { background-color: rgba(0, 207, 167, .90); box-shadow: 0px 0px 40px -5px rgba(0, 207, 167, .90); } .chart__bar--seconds { background-color: rgba(238, 138, 56, .90); box-shadow: 0px 0px 40px -5px rgba(238, 138, 56, .90); } .chart__bar--days, .chart__bar--hours, .chart__bar--minutes, .chart__bar--seconds { background-image: url(pattern-timer.png); background-position: bottom right; background-repeat: repeat; }


Ответ 3



Описал в комментариях как работает скрипт и дополнил его. //Устанавливаем дату окончания var end = new Date('12/01/2018 12:00 AM'); //Задаём константы var _second = 1000; var _minute = _second * 60; var _hour = _minute * 60; var _day = _hour * 24; var _month = _day * 30; var _year = _month * 12; //Для того что бы вывести диаграмму остатка количества лет нужно общее количество — в данно случае ставлю 2 var _year_all = 2; var timer; //Функция которая вызывается при окончании отсчёта function eventStopTimer(){ return false; } function showRemaining() { var now = new Date(); var distance = end - now; if (distance < 0) { eventStopTimer(); clearInterval(timer); return; } // Находим количество var years = Math.floor(distance / _year); var months = Math.floor((distance % _year) / _month); var days = Math.floor((distance % _months) / _day); var hours = Math.floor((distance % _day) / _hour); var minutes = Math.floor((distance % _hour) / _minute); var seconds = Math.floor((distance % _minute) / _second); // В английской версии тут склоняли добавлением «S» var yearsLabel = pluralize(years, 'Дней'); var monthsLabel = pluralize(months, 'Часов'); var daysLabel = pluralize(days, 'Дней'); var hoursLabel = pluralize(hours, 'Часов'); var minutesLabel = pluralize(minutes, 'Минут'); var secondsLabel = pluralize(seconds, 'Секунд'); //Добавляем лидирующий ноль var days = leadingZero(days); var hours = leadingZero(hours); var minutes = leadingZero(minutes); var seconds = leadingZero(seconds); //Функция для склонения (неправильная) function pluralize(number, text) { if(number === 1) { return text; } else { return text+'' } } //Функция добавления лидирующего нуля function leadingZero(number) { if(number < 10) { return '0'+number; } else { return number; } } $('.chart__bar--years .chart__bar-number').html(years); $('.chart__bar--years').css('height', ((years/_year_all)*100)+'%'); $('.chart__bar--years .chart__bar-label').html(yearsLabel); $('.chart__bar--months .chart__bar-number').html(months); $('.chart__bar--months').css('height', ((months/12)*100)+'%'); $('.chart__bar--months .chart__bar-label').html(monthsLabel); $('.chart__bar--days .chart__bar-number').html(days); $('.chart__bar--days').css('height', ((days/360)*100)+'%'); $('.chart__bar--days .chart__bar-label').html(daysLabel); $('.chart__bar--hours .chart__bar-number').html(hours); $('.chart__bar--hours').css('height', ((hours/24)*100)+'%'); $('.chart__bar--hours .chart__bar-label').html(hoursLabel); $('.chart__bar--minutes .chart__bar-number').html(minutes); $('.chart__bar--minutes').css('height', ((minutes/60)*100)+'%'); $('.chart__bar--minutes .chart__bar-label').html(minutesLabel); $('.chart__bar--seconds .chart__bar-number').html(seconds); $('.chart__bar--seconds').css('height', ((seconds/60)*100)+'%'); $('.chart__bar--seconds .chart__bar-label').html(secondsLabel); } timer = setInterval(showRemaining, 1000); В html нужно добавить блоки


понедельник, 17 июня 2019 г.

Таймер при наведении на блок и обратный таймер после выхода курсора из области

Доброго времени суток. Несколько часов "бьюсь" головой и думаю, как сделать, чтобы при наведении на блок запускался таймер, а после вывода курсора из блока запускался обратный Т.е нужно Чтобы при наведении на блок через секунду добавился класс number_1, через две number_2 и т.д. (это все легко решается с помощью setTimeout) Но нужно чтобы если человек вывел курсор за область блока, начинался обратный отсчет Т.е если последним было number_4, он начинал удалить классы, через 1 секунду number_4, через 2 number_3 и т.д


Ответ

Пожалуйста - https://jsfiddle.net/knvnckbz/4/
var div = document.querySelector('div'), addTimeout = null, removeTimeout = null, classNames = []; div.addEventListener('mouseover', addClass); div.addEventListener('mouseout', removeClass); function addClass() { clearTimeout(removeTimeout); var className = 'number_' + (classNames.length); div.classList.add(className); classNames.push(className); addTimeout = setTimeout(addClass, 1000); } function removeClass() { clearTimeout(addTimeout); var className = classNames.pop(); if (!className) return; div.classList.remove(className); removeTimeout = setTimeout(removeClass, 1000); } div { width: 100px; height: 100px; background: black; } .number_1 { background: red; } .number_2 { background: blue; } .number_3 { background: yellow; }


среда, 12 июня 2019 г.

Как реализовать таймер с асинхронным вызовом?

Интересует как реализовать таймер с асинхронным вызовом через промежуток времени на си. Вот, допустим, таймер на javascript задается:
setTimeout(func, 1000);
или на boost::asio:
boost::asio::deadline_timer t(io, boost::posix_time::seconds(5)); t.async_wait(&print);
Интересует как такое же реализовать на си. Т.е. задаем таймер, делаем полезную работу, а через некоторое время срабатывает сигнал или как-то по-другому асинхронно оповещаем. Просто тупо делать задержку это не то, а именно вызвать через какой-то промежуток времени, не приостанавливая работу до этого момента. Хотя бы сам принцип, что использовать, eventfd, сигналы или что-то еще.


Ответ

Вы правильно думаете, используйте сигналы, вызванные с помощью alarm() или setitimer(). Системный вызов setitimer() является обобщением вызова alarm()
/* Обработчик сигнала*/ void catch_alarm(int sig) { printf("Do stuff..."); }
/* Устанавливаем обработчик для SIGALRM */ signal (SIGALRM, catch_alarm);
/* Ставим таймер на 2 секунды */ alarm(2);
Вообще, каждый процесс имеет три независимых таймера:
Таймер реального времени, отсчитывающий прошедшее время. Этот таймер отправляет сигнал SIGALRM процессу, когда он истекает. Виртуальный таймер, который измеряет только время выполнения самого процесса. Этот таймер отправляет сигнал SIGVTALRM процессу, когда он истекает. Таймер профилирования, который измеряет время выполнения самого процесса и время, проведённое в ожидании завершения системных вызовов. Этот таймер отправляет сигнал SIGPROF процессу, когда он истекает.
Вы можете установить только один таймер каждого типа в любой момент времени. Если вы установите таймер, который еще не истек, этот таймер просто сбрасывается на новое значение.

пятница, 9 ноября 2018 г.

Как вызвать функцию по завершении таймера?

Я удивлён, что не нашёл ничего вменяемого по этому вопросу. Но всё же, как в с++ вызвать некую функцию по завершении таймера? Вопрос достаточно банальный однако. Подозреваю, что тут не обойдётся без boost::bind. Нужен аналог данного: QTimer::singleShot(100, this, SLOT(onTimeExpired()));


Ответ

Озадачали Вы меня. Вот тут на досуге набросал что-то. #pragma once #include #include
namespace MyTimer {
class Timer { private: boost::shared_ptr thr;
template class _func { unsigned int dur; Callable& cb;
public: _func (long long _dur, const Callable& _cb): dur (_dur), cb((Callable&) _cb) {} void operator() () { boost::this_thread::sleep(boost::posix_time::milliseconds(dur)); cb(); } };
public: template Timer (long long _dur, const Callable& _cb) { _func ff (_dur, _cb); thr.reset (new boost::thread(ff)); }
~Timer() {}
Timer (const Timer& tm) { this->thr = tm.thr; }
Timer operator= (const Timer& tm) { if (this != &tm) this->thr = tm.thr; return *this; }
void interrupt () { thr->interrupt(); } };
} Это все оформляется в виде хедера. Используется очень просто. MyTimer::Timer Tm1 (5000, func); Первый параметр - время ожидания в миллисекундах, второй - вызываемый объект. Может быть простая функция, может быть функтор. Главное, не имеет параметров и возвращает void. Можно прервать ожидание с помощью метода interrupt() Испытывал в лине, gcc4.6 и 4.7 и icpc 12, boost 1.49 и 1.51

среда, 10 октября 2018 г.

Как правильно внести изменения в скрипт таймера обратного отсчета времени

Всех приветствую! Прошу помощи. Есть скрипт таймера обратного отсчета времени до определенной даты. Помогите, пожалуйста, внести в него изменения. Есть несколько моментов:
Необходимо добавить в скрипт таймера количество месяцев и количество лет. Второй момент, не могу разобраться, когда таймер заканчивает отсчет, блок становится пустым. Как сделать так, чтобы по окончании таймера, в этом блоке появлялся другой блок? И еще один момент: скрипт ориентирован не на нашего брата, АМ и РМ, не совсем это удобно, возможно ли это изменить, а также поменять местами в первой строке скрипта число и месяц? Очень буду благодарен всем за любую помощь!
скрипт:
var end = new Date('12/01/2018 12:00 AM');
var _second = 1000; var _minute = _second * 60; var _hour = _minute * 60; var _day = _hour * 24; var timer;
function showRemaining() { var now = new Date(); var distance = end - now; if (distance < 0) { clearInterval(timer); return; } var days = Math.floor(distance / _day); var hours = Math.floor((distance % _day) / _hour); var minutes = Math.floor((distance % _hour) / _minute); var seconds = Math.floor((distance % _minute) / _second);
var daysLabel = pluralize(days, 'Дней'); var hoursLabel = pluralize(hours, 'Часов'); var minutesLabel = pluralize(minutes, 'Минут'); var secondsLabel = pluralize(seconds, 'Секунд');
var days = leadingZero(days); var hours = leadingZero(hours); var minutes = leadingZero(minutes); var seconds = leadingZero(seconds);
function pluralize(number, text) { if(number === 1) { return text; } else { return text+'' } }
function leadingZero(number) { if(number < 10) { return '0'+number; } else { return number; } }
$('.chart__bar--days .chart__bar-number').html(days); $('.chart__bar--days').css('height', ((days/360)*100)+'%'); $('.chart__bar--days .chart__bar-label').html(daysLabel);
$('.chart__bar--hours .chart__bar-number').html(hours); $('.chart__bar--hours').css('height', ((hours/24)*100)+'%'); $('.chart__bar--hours .chart__bar-label').html(hoursLabel);
$('.chart__bar--minutes .chart__bar-number').html(minutes); $('.chart__bar--minutes').css('height', ((minutes/60)*100)+'%'); $('.chart__bar--minutes .chart__bar-label').html(minutesLabel);
$('.chart__bar--seconds .chart__bar-number').html(seconds); $('.chart__bar--seconds').css('height', ((seconds/60)*100)+'%'); $('.chart__bar--seconds .chart__bar-label').html(secondsLabel); }
timer = setInterval(showRemaining, 1000);
html:


css:
.widget { position: absolute; top: 0; bottom: 0; left: 0; right: 0; height: 100%; width: 100%; } .widget__foreground { position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; } .widget--countdown__main { position: absolute; width: 50%; left: 50px; top: 120px; font-family: PT Serif Caption,Georgia,Times New Roman,Times,serif; color: #000000; text-shadow: #fff 1px 0px, #fff 1px 1px, #fff 0px 1px, #fff -1px 1px, #fff -1px 0px, #fff -1px -1px, #fff 0px -1px, #fff 1px -1px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px; } .widget--countdown__title { font-size: 36px; line-height: 42px; font-weight: 700; margin: 0 50px 20px 0; } .widget--countdown__supporting-text { margin-bottom: 25px; line-height: 17px; width: 50%; } .widget--countdown__chart { height: 100%; width: 50%; max-width: 470px; position: absolute; right: 50px; } .chart { position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; display: flex; justify-content: space-between; } .chart__bar { width: 23%; position: relative; display: inline-block; align-self: flex-end; -webkit-transition: all linear .2s; -moz-transition: all linear .2s; -ms-transition: all linear .2s; -o-transition: all linear .2s; transition: all linear .2s; } .chart__bar-content { position: absolute; bottom: 20px; width: 100%; text-align: center; } .chart__bar-number { font-size: 36px; font-weight: 300; } .chart__bar-label { text-transform: uppercase; } .chart__bar--days { background-color: rgba(0, 191, 228, .90); box-shadow: 0px 0px 40px -5px rgba(0, 191, 228, .90); } .chart__bar--hours { background-color: rgba(249, 108, 207, .90); box-shadow: 0px 0px 40px -5px rgba(249, 108, 207, .90); } .chart__bar--minutes { background-color: rgba(0, 207, 167, .90); box-shadow: 0px 0px 40px -5px rgba(0, 207, 167, .90); } .chart__bar--seconds { background-color: rgba(238, 138, 56, .90); box-shadow: 0px 0px 40px -5px rgba(238, 138, 56, .90); } .chart__bar--days,.chart__bar--hours,.chart__bar--minutes,.chart__bar--seconds { background-image: url(pattern-timer.png); background-position: bottom right; background-repeat: repeat; }


Ответ

upd.2 обновил ф-ю calendiff, чтобы правильно считала случаи, когда надо «отмотать» назад не один, а два месяца, для компенсации смещения в днях. Напр. чтобы компенсировать -31 день, добавить дни предыдущего одного месяца (30 дней) не достаточно, нужно заехать в пред-предыдущий месяц.
upd. Как выяснилось, надо всё-таки считать точную «календарную» дистанцию между двух дат: и от 28 января до 28 февраля (31 день), и от 28 февраля до 28 марта (28 дней) – в обоих случаях ровно 1 месяц. Поэтому написал функцию для такого «человеческого» подсчёта разницы между двумя датами.
Поскольку разметка повторяется, отличается лишь названием класса, вынес её генерацию в функцию. jQuery элементы достаточно найти один раз, и затем обращаться к сохранённым, вместо того, чтобы при каждом вызове заново искать в DOM-дереве. Добавил функцию для склонения существительных, в зависимости от числа (День, Дня, Дней). В стили добавил цвета для колонок года и месяца.
// допустимые форматы времени: // https://tools.ietf.org/html/rfc2822#page-14 // https://www.w3.org/TR/NOTE-datetime // Напр. 1 декабря 2018, полдень по Москве: var end = new Date('2018-12-01T12:00+03:00') ,offset = (new Date().getTimezoneOffset())*6E4 ,timer ,$el ,key ,html = '' ; // generate markup html = ['years','months','days','hours','minutes','seconds'] .reduce( function(p,c){ return p += makeHtml(c);}, ''); $('.widget--countdown__chart > .chart').html(html); $el = { // найти один раз и запомнить Y: { n: $('.chart__bar--years .chart__bar-number'), l: $('.chart__bar--years .chart__bar-label'), b: $('.chart__bar--years') } ,M: { n: $('.chart__bar--months .chart__bar-number'), l: $('.chart__bar--months .chart__bar-label'), b: $('.chart__bar--months') } ,D: { n: $('.chart__bar--days .chart__bar-number'), l: $('.chart__bar--days .chart__bar-label'), b: $('.chart__bar--days') } ,h: { n: $('.chart__bar--hours .chart__bar-number'), l: $('.chart__bar--hours .chart__bar-label'), b: $('.chart__bar--hours') } ,m: { n: $('.chart__bar--minutes .chart__bar-number'), l: $('.chart__bar--minutes .chart__bar-label'), b: $('.chart__bar--minutes') } ,s: { n: $('.chart__bar--seconds .chart__bar-number'), l: $('.chart__bar--seconds .chart__bar-label'), b: $('.chart__bar--seconds') } } /** * генерирует html одного блока */ function makeHtml(key) { return [ '

', '
', '
', '
', '
', '
', ].join("
").replace(/%KEY%/g, key); } function showRemaining() { var start = new Date(), r; if( end - start < 0) return timeHasCome(); r = calendiff( start, end); render( 'Y', r.years, ['Год','Года','Лет'], 3); // сколько максимум лет может быть? render( 'M', r.months, ['Месяц','Месяца','Месяцев'], 12); render( 'D', r.days, ['День','Дня','Дней'], 31); render( 'h', r.hours, ['Час','Часа','Часов'], 24); render( 'm', r.minutes, ['Минута','Минуты','Минут'], 60); render( 's', r.seconds, ['Секунда','Секунды','Секунд'], 60); } /** * Когда отсчёт закончился, или время уже прошло */ function timeHasCome() { if(timer) { window.clearInterval( timer); timer = undefined; } $('.widget.widget--countdown').html('
Баста, карапузики!
'); } function render( key, n, s, max) { $el[key].n.text( ('0' + n).slice(-2)); $el[key].l.text( getNumEnding(n, s)); $el[key].b.css('height', ((n / max) * 100) + '%'); } /** * Calendar difference between two dates * @param Date object dateIn start date * @param Date object dateOut end date * @return object with int properties * "years", "months", "days", "hours", "minutes" and "seconds" * * https://github.com/sergiks/calendiff.js * * @author Sergei Sokolov */ function calendiff( dateIn, dateOut) { var out = { years : 0 ,months : 0 ,days : 0 ,hours : 0 ,minutes : 0 ,seconds : 0 } ,sign = 1 ,diff = 0 ,proto ,monthsShift ,prop ; // check input proto = Object.prototype.toString.call(dateIn); if( proto !== '[object Date]') { dateIn = new Date( dateIn); if( isNaN( dateIn.getTime())) throw 'Incorrect "In" date format'; } proto = Object.prototype.toString.call(dateOut); if( proto !== '[object Date]') { dateOut = new Date( dateOut); if( isNaN( dateOut.getTime())) throw 'Incorrect "Out" date format'; } // check numeric difference diff = dateOut.getTime() - dateIn.getTime(); if( diff === 0) { return out; } else if( diff < 0) { sign = -1; dateOut = [dateIn, dateIn = dateOut][0]; // swap the dates } // calculate human-readable difference out.seconds += dateOut.getSeconds() - dateIn.getSeconds(); if( out.seconds < 0) { out.seconds += 60; out.minutes--; } out.minutes += dateOut.getMinutes() - dateIn.getMinutes(); if( out.minutes < 0) { out.minutes += 60; out.hours--; } out.hours += dateOut.getHours() - dateIn.getHours(); if( out.hours < 0) { out.hours += 24; out.days--; } // complex part: a month can have various number of days // when entering with a negative number of days, up to -31, // it might take up to two months shift back // should the preceding month only have 28, 29 or 30 days. out.days += dateOut.getDate() - dateIn.getDate(); while( out.days < 0) { monthsShift = 0; out.days += new Date( dateOut.getFullYear(), dateOut.getMonth() - monthsShift, 0).getDate(); monthsShift++; out.months--; } out.months += dateOut.getMonth() - dateIn.getMonth(); if( out.months < 0) { out.months += 12; out.years--; } out.years += dateOut.getFullYear() - dateIn.getFullYear(); // negative difference case if( sign < 0) for( prop in out) if( out[prop]) out[prop] *= -1; // avoid -0 values return out; } /** * Функция возвращает окончание для множественного числа слова на основании числа и массива окончаний * param iNumber Integer Число на основе которого нужно сформировать окончание * param aEndings Array Массив слов или окончаний для чисел (1, 4, 5), * например ['яблоко', 'яблока', 'яблок'] * return String */ function getNumEnding(iNumber, aEndings) { var sEnding, i; iNumber = iNumber % 100; if (iNumber>=11 && iNumber<=19) { sEnding=aEndings[2]; } else { i = iNumber % 10; switch (i) { case (1): sEnding = aEndings[0]; break; case (2): case (3): case (4): sEnding = aEndings[1]; break; default: sEnding = aEndings[2]; } } return sEnding; } timer = setInterval(showRemaining, 200); .widget { position: absolute; top: 0; bottom: 0; left: 0; right: 0; height: 100%; width: 100%; } .widget__foreground { position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; } .widget--countdown__main { position: absolute; width: 50%; left: 50px; top: 120px; font-family: PT Serif Caption,Georgia,Times New Roman,Times,serif; color: #000000; text-shadow: #fff 1px 0px, #fff 1px 1px, #fff 0px 1px, #fff -1px 1px, #fff -1px 0px, #fff -1px -1px, #fff 0px -1px, #fff 1px -1px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px, #fff 0 0 3px; } .widget--countdown__title { font-size: 36px; line-height: 42px; font-weight: 700; margin: 0 50px 20px 0; } .widget--countdown__supporting-text { margin-bottom: 25px; line-height: 17px; width: 50%; } .widget--countdown__chart { height: 100%; width: 50%; max-width: 470px; position: absolute; right: 50px; } .chart { position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; display: flex; justify-content: space-between; } .chart__bar { width: 23%; position: relative; display: inline-block; align-self: flex-end; -webkit-transition: all linear .2s; -moz-transition: all linear .2s; -ms-transition: all linear .2s; -o-transition: all linear .2s; transition: all linear .2s; } .chart__bar-content { position: absolute; bottom: 20px; width: 100%; text-align: center; } .chart__bar-number { font-size: 36px; font-weight: 300; } .chart__bar-label { text-transform: uppercase; } .chart__bar--years { background-color: rgba(228, 191, 0, .90); box-shadow: 0px 0px 40px -5px rgba(228, 191, 0, .90); } .chart__bar--months { background-color: rgba(207, 108, 249, .90); box-shadow: 0px 0px 40px -5px rgba(207, 108, 249, .90); } .chart__bar--days { background-color: rgba(0, 191, 228, .90); box-shadow: 0px 0px 40px -5px rgba(0, 191, 228, .90); } .chart__bar--hours { background-color: rgba(249, 108, 207, .90); box-shadow: 0px 0px 40px -5px rgba(249, 108, 207, .90); } .chart__bar--minutes { background-color: rgba(0, 207, 167, .90); box-shadow: 0px 0px 40px -5px rgba(0, 207, 167, .90); } .chart__bar--seconds { background-color: rgba(238, 138, 56, .90); box-shadow: 0px 0px 40px -5px rgba(238, 138, 56, .90); } .chart__bar--days,.chart__bar--hours,.chart__bar--minutes,.chart__bar--seconds { background-image: url(pattern-timer.png); background-position: bottom right; background-repeat: repeat; }