upd.2 Updated FY calendiffin order to properly consider cases where " retrieve " is not one, but two months, to compensate for shifts in the days. In order to compensate for the 31 days, adding the days of the previous one month (30 days) is not sufficient, it is necessary to come in the previous month.upd. It appears that the precise “calendar” distance between two dates, 28 January and 28 February (31 days days) and 28 February (28 days) should be considered, in both cases 1 month sharp. That's why. https://github.com/sergiks/calendiff.js/blob/master/calendiff.js?ts=2 for such a “human” account of the difference between the two dates.Since the marking is repeated, it's only a class name, it's a function. jQuery elements are sufficient to be found once, and then to be treated safely, instead of looking again at DOM trees at each challenge. Added the function https://habrahabr.ru/post/105428/ depending on the number (Day, Day, Day). The style added colors to the columns of the year and the month.// допустимые форматы времени:
// 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 [
'<div class="chart__bar chart__bar--%KEY%">',
' <div class="chart__bar-content"> ',
' <div class="chart__bar-number"></div> ',
' <div class="chart__bar-label"></div> ',
' </div> ',
'</div> ',
].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('<div class="done">Баста, карапузики!</div>');
}
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 <hello@sergeisokolov.com>
*/
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;
}<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="widget widget--countdown">
<div class="widget__foreground">
<div class="widget--countdown__chart">
<div class="chart">
</div>
</div>
</div>
</div>