Я сохранил только визуальный аспект в CSS (с некоторыми изменениями) и полностью переписал весь код в «чистый» Javascript (без jQuery) и сохранил библиотеку GSAP / TweenMax.
Вы можете поставить какмного countDown, как вы хотите, заполнив массив с указанием заголовков и продолжительности и т. д.
Существует только один setInterval для обработки другого countDown, и он остановится с последним активным countDown. это решение, потому что использование нескольких setInterval в то же время показалось мне непропорциональным для этого и излишне перегружало ОС.
Для большей точности все countDowns основаны на системных часах (а не на циклах вызоваsetInterval, потому что они «естественным образом» смещены и поэтому несовместимы для любого использования измерения времени).
Этот скрипт позволяет выполнять 2 типа countDown:
- в течение фиксированной продолжительности (например,6 минут для яиц)
- либо в конце даты (или времени) (например: день рождения, околоintment ...)
Другой интерес этого скрипта заключается в том, что он генерирует по запросу набор элементов html, полезный для отображения countDown на странице, и позволяет выбирать отображение с номером желаемогоед.
const myCountDowns= [ { title: 'timer <strong>24h</strong>'
, type : 'Hours'
, timer: '24h'
}
, { title: 'Tea cup <strong>2\' 45"</strong>'
, type : 'Minutes'
, timer: '2m 45s'
}
, { title: 'until the new year <strong>2020</strong>'
, type : 'Days'
, date : '01 01 2020' // local Month Day Year
}
]
CountDown.BuildsAndRun( myCountDowns )
// ->type : 'Days' or 'Hours' or 'Minutes' or 'seconds'
// set "timer" for time duration otherwise set a "date" value
// timer string format is _number_UNIT where UNIT = 'd','h','m','s' for Days, Hours, Minutes, Seconds
// ex : '3d 25m 6s' = 3 days 0 hours 25 minutes, 6 seconds (days = 0, 0 is defauls for all units)
// ex : '6s 3d 25m' = the same, there is no order
// date format is JS Date format see new Date( _STRING_ )
ПОЛНЫЙ КОД (на фрагменте ниже)
(function( CountDown )
{
// Private vars
const domEndBody = document.body.querySelector('script') || document.body // for countDowns insert place
, eWrapp = document.createElement('div')
, eTitle = document.createElement('h1')
, eCountDown = document.createElement('div')
, eTimeBlock = document.createElement('div')
, eCountTitle = document.createElement('span')
, eFigure = document.createElement('div')
, counters = [] // list of CountDowns
, one_Sec = 1000
, one_Min = one_Sec * 60
, one_Hour = one_Min * 60
, one_Day = one_Hour * 24
, padZ =(val,sz) => ('0'.repeat(sz)+val.toString(10)).slice(-sz) // return string with leading zeros
, Interface = [ { xL:8, Title:'Days', Fig:3 } // titles & counts of figures
, { xL:5, Title:'Hours', Fig:2 }
, { xL:3, Title:'Minutes', Fig:2 }
, { xL:0, Title:'Seconds', Fig:2 }
]
, animOpt = { rotationX: 0, transformPerspective: 300, ease: Quart.easeOut, clearProps: 'all' }
var activeCounters = 0
// finalize countDown elements
eWrapp.className = 'wrap'
eTitle.innerHTML = 'F<strong>D</strong>' // 'Draft <strong>Countdown</strong>'
eCountDown.className = 'countdown'
eTimeBlock.className = 'bloc-time'
eCountTitle.className = 'count-title'
eFigure.className = 'figure'
eFigure.innerHTML = '<span class="top" > </span>'
+ ' <span class="top-back" > <span> </span> </span>'
+ ' <span class="bottom" > </span>'
+ ' <span class="bottom-back"> <span> </span> </span>'
//Public Method ........................................................................
CountDown.BuildsAndRun = function( TimerArray )
{
for (let TimerParms of TimerArray)
{ CountDown_Build ( TimerParms ) }
setTimeout(() => { CountDown_Run() }, 300); // the Timeout is just for start spectacle
}
// Private Methods......................................................................
CountDown_Build = function( parms )
{
let len = parms.type==='Hours'?6:parms.type==='Minutes'?4:parms.type==='seconds'?2:9
, ctD = { lg : len // countDown number of figure (digits)
, face : ' '.repeat(len) // actuel face of countDown
, fig : [] // array of firures (DOM elements)
, ref : counters.length // ID of this countDown
, time : null // time to add to compute taget time for CountDown
, target : null // target Timie value
, activ : true // to check if count down is activ or not ( finished )
}
// generate all Figures of CountDown
for(let i=len;i--;) { // from len to 0 (just my fav ninja)
ctD.fig.push( eFigure.cloneNode(true) )
}
// CountDown DOM making
let xWrapp = eWrapp.cloneNode(true)
, xTitle = eTitle.cloneNode(true)
, xCountDown = eCountDown.cloneNode(true)
, noFig = 0 // ref on the first ctD.fig list (of figures)
xTitle.innerHTML = parms.title
xWrapp.style.width = len===9?'1105px':len===6?'740px':len===4?'485px':'230px'
//xCountDown.style.width = len===9?'1090px':len===6?'730px':len===4?'470px':'230px'
xWrapp.appendChild(xTitle)
xWrapp.appendChild(xCountDown)
// making of bloc-time elements
for(eBlk of Interface)
{
if(len>eBlk.xL)
{
let xTimeBlock = eTimeBlock.cloneNode(true)
, xCountTitle = eCountTitle.cloneNode(true)
xCountTitle.textContent = eBlk.Title
xTimeBlock.appendChild(xCountTitle)
for(let f=eBlk.Fig;f--;) // (fav ninja again)
{ xTimeBlock.appendChild(ctD.fig[noFig++]) } // move figures inside
xCountDown.appendChild(xTimeBlock)
}
}
document.body.insertBefore(xWrapp, domEndBody) // insert CountDowm on page
// set count down initial values
if (parms.timer) // in case of timer...
{
let TimeInfos = get_DHMS(parms.timer, len )
ctD.time = TimeInfos.add
counters.push( ctD )
activeCounters++
updateInterface( ctD.ref, TimeInfos.dis ) // show first figure faces
}
else if (parms.date) // in case of CountDown until date
{
ctD.target = new Date(parms.date);
counters.push( ctD )
if (showFaceOnNow( ctD.ref ))
{ activeCounters++ }
}
}
CountDown_Run = function()
{
for (let elm of counters)
{
if (elm.time)
{ elm.target = new Date().getTime() + elm.time }
}
let timerInterval = setInterval(_=>
{
counters.forEach((elm,ref)=>
{
if ( elm.activ )
{
if (!showFaceOnNow( ref ))
{ activeCounters-- }
}
if ( activeCounters<=0 )
{ clearInterval(timerInterval) }
})
}
, one_Sec )
}
showFaceOnNow = function(ref)
{
let now = new Date().getTime()
, tim = counters[ref].target - now
, face = '0'.repeat( counters[ref].lg )
if (tim >= 0)
{
face = padZ(Math.floor(tim / one_Day), 3)
face += padZ((Math.floor((tim % one_Day ) / one_Hour)), 2)
face += padZ((Math.floor((tim % one_Hour) / one_Min )), 2)
face += padZ((Math.floor((tim % one_Min ) / one_Sec )), 2)
face = padZ( face, counters[ref].lg )
}
else
{
counters[ref].activ = false
}
updateInterface ( ref, face)
return counters[ref].activ
}
updateInterface = function(ref, newVal)
{
for(let p = counters[ref].lg ; p--;) // update faces figures backward
{
if (counters[ref].face.charAt(p) !== newVal.charAt(p))
{
animateFigure( counters[ref].fig[p], newVal.charAt(p) )
}
}
counters[ref].face = newVal
}
get_DHMS = function (timer_val, lg)
{
let vDelay = { d:0, h:0, m:0, s:0 }
, vNum = '0'
, ret = { add: 0, dis: ''}
for (const c of timer_val)
{
if (/[0-9]/.test(c) ) vNum += c
if (/[dhms]/.test(c) )
{
vDelay[c] = parseInt(vNum)
vNum = '0'
}
}
ret.add = (vDelay.d*one_Day)+(vDelay.h*one_Hour)+(vDelay.m*one_Min)+(vDelay.s*one_Sec)
ret.dis = (padZ(vDelay.d,3)+padZ(vDelay.h,2)+padZ(vDelay.m,2)+padZ(vDelay.s,2)).slice(-lg)
return ret
}
animateFigure = function (domElm, newChar)
{
let eTop = domElm.querySelector('.top')
, eBottom = domElm.querySelector('.bottom')
, eBack_top = domElm.querySelector('.top-back')
// Before we begin, change the back value and the back bottom value
eBack_top.querySelector('span').textContent = newChar
domElm.querySelector('.bottom-back span').textContent = newChar
TweenMax.to(eTop, 0.8, // Then animate
{ rotationX : '-180deg'
, transformPerspective: 300
, ease : Quart.easeOut
, onComplete : function()
{
eTop.textContent = newChar
eBottom.textContent = newChar
TweenMax.set(eTop, { rotationX: 0 })
}
})
TweenMax.to(eBack_top, 0.8, animOpt)
}
}( window.CountDown = window.CountDown || {}));
/********************************************************************************************/
const myCountDowns= [ { title: 'timer <strong>24h</strong>'
, type : 'Hours'
, timer: '24h'
}
, { title: 'Tea cup <strong>2\' 45"</strong>'
, type : 'Minutes'
, timer: '2m 45s'
}
, { title: 'until the new year <strong>2020</strong>'
, type : 'Days'
, date : '01 01 2020' // local Month Day Year
}
]
CountDown.BuildsAndRun( myCountDowns )
// ->type : 'Days' or 'Hours' or 'Minutes' or 'seconds'
// set "timer" for time duration otherwise set a "date" value
// timer string format is _number_UNIT where UNIT = 'd','h','m','s' for Days, Hours, Minutes, Seconds
// ex : '3d 25m 6s' = 3 days 0 hours 25 minutes, 6 seconds (days = 0, 0 is defauls for all units)
// ex : '6s 3d 25m' = the same, there is no order
// date format is JS Date format see new Date( _STRING_ )
body {
background-color: #f2f1ed;
margin: 0;
}
.wrap {
margin: 2em auto;
height: 270px;
width: 1500px; /* be re-calculate on JS */
border-radius: 1em;
padding: 10px 5px 0 5px;
box-shadow: 0px 0px 1px 1px rgba(170, 170, 170, 0.64);
}
a {
text-decoration: none;
color: #1a1a1a;
}
h1 {
margin-bottom: 30px;
text-align: center;
font: 300 2.25em "Lato";
text-transform: uppercase;
}
h1 strong {
font-weight: 400;
color: #ea4c4c;
}
h2 {
margin-bottom: 80px;
text-align: center;
font: 300 0.7em "Lato";
text-transform: uppercase;
}
h2 strong {
font-weight: 400;
}
.countdown {
/* width: 100%; or be re-calculate on JS */
margin: 0 auto;
padding: 0 10px 10px 10px;
}
.countdown .bloc-time {
float: left;
margin-right: 45px;
text-align: center;
}
.countdown .bloc-time:last-child {
margin-right: 0;
}
.countdown .count-title {
display: block;
margin-bottom: 15px;
font: normal 0.94em "Lato";
color: #1a1a1a;
text-transform: uppercase;
}
.countdown .figure {
position: relative;
float: left;
height: 110px;
width: 100px;
margin-right: 10px;
background-color: #fff;
border-radius: 8px;
-moz-box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.2), inset 2px 4px 0 0 rgba(255, 255, 255, 0.08);
-webkit-box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.2), inset 2px 4px 0 0 rgba(255, 255, 255, 0.08);
box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.2), inset 2px 4px 0 0 rgba(255, 255, 255, 0.08);
}
.countdown .figure:last-child {
margin-right: 0;
}
.countdown .figure > span {
position: absolute;
left: 0;
right: 0;
margin: auto;
font: normal 5.94em/107px "Lato";
font-weight: 700;
color: #de4848;
}
.countdown .figure .top:after, .countdown .figure .bottom-back:after {
content: "";
position: absolute;
z-index: -1;
left: 0;
bottom: 0;
width: 100%;
height: 100%;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.countdown .figure .top {
z-index: 3;
background-color: #f7f7f7;
transform-origin: 50% 100%;
-webkit-transform-origin: 50% 100%;
-moz-border-radius-topleft: 10px;
-webkit-border-top-left-radius: 10px;
border-top-left-radius: 10px;
-moz-border-radius-topright: 10px;
-webkit-border-top-right-radius: 10px;
border-top-right-radius: 10px;
-moz-transform: perspective(200px);
-ms-transform: perspective(200px);
-webkit-transform: perspective(200px);
transform: perspective(200px);
}
.countdown .figure .bottom {
z-index: 1;
}
.countdown .figure .bottom:before {
content: "";
position: absolute;
display: block;
top: 0;
left: 0;
width: 100%;
height: 50%;
background-color: rgba(0, 0, 0, 0.02);
}
.countdown .figure .bottom-back {
z-index: 2;
top: 0;
height: 50%;
overflow: hidden;
background-color: #f7f7f7;
-moz-border-radius-topleft: 10px;
-webkit-border-top-left-radius: 10px;
border-top-left-radius: 10px;
-moz-border-radius-topright: 10px;
-webkit-border-top-right-radius: 10px;
border-top-right-radius: 10px;
}
.countdown .figure .bottom-back span {
position: absolute;
top: 0;
left: 0;
right: 0;
margin: auto;
}
.countdown .figure .top, .countdown .figure .top-back {
height: 50%;
overflow: hidden;
-moz-backface-visibility: hidden;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
.countdown .figure .top-back {
z-index: 4;
bottom: 0;
background-color: #fff;
-webkit-transform-origin: 50% 0;
transform-origin: 50% 0;
-moz-transform: perspective(200px) rotateX(180deg);
-ms-transform: perspective(200px) rotateX(180deg);
-webkit-transform: perspective(200px) rotateX(180deg);
transform: perspective(200px) rotateX(180deg);
-moz-border-radius-bottomleft: 10px;
-webkit-border-bottom-left-radius: 10px;
border-bottom-left-radius: 10px;
-moz-border-radius-bottomright: 10px;
-webkit-border-bottom-right-radius: 10px;
border-bottom-right-radius: 10px;
}
.countdown .figure .top-back span {
position: absolute;
top: -100%;
left: 0;
right: 0;
margin: auto;
}
<link href='https://fonts.googleapis.com/css?family=Lato:300,400,700' rel='stylesheet' type='text/css'>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenMax.min.js"></script>
<!-- no more HTML code -->