БлогNot. "Космические" часы вместо полярных

"Космические" часы вместо полярных

Разных часов на Яваскрипте тут уже много (только последние - раз, два и т.д.), но популярные в сети "полярные часы" всех разновидностей (пример) мне не нравятся своим "исчезающим сектором" при переходе к новой минуте, да ещё тем, что самая быстрая стрелка почему-то всегда двигается по самой большой окружности, что противоречит законам физики :)

Вместо этого мне кажутся приятней вот такие "космические часы" , приведённые ниже в работе и в виде заготовки-исходника (файл .html в кодировке Юникода utf-8 без обрамляющих тегов).

Все размеры относительны и зависят от размеров стороны канвы, возможно, код ещё будет дополняться, поэтому отдельным файлом не прилагаю.

В IE11 и ниже показанный на странице листинг работать не будет из-за аргумента функции по умолчанию вида signed = [], в MS Edge и остальных современных браузерах (2015 год и новее) всё должно работать.

 <div style="margin: 0 auto; text-align: center;">
  <canvas id="spaceCanvas" style="background: #000; font-family: sans-serif;"></canvas>
 </div>
 <script>
  function spaceCanvas(id,size) { //Функция-обёртка для часов
   let canvas = document.getElementById(id);
   let ctx = canvas.getContext('2d');
   canvas.style.width = canvas.style.height = size+'px'; 
   canvas.width = canvas.height = size;
    //или window.innerWidth, window.innerHeight
   let centerX = canvas.width/2;
   let centerY = canvas.height/2;
   let fontSize = Math.max(Math.floor(size/30),8);
   let PI  = Math.PI, cos = Math.cos, sin = Math.sin;

   function getDaysInMonth (month, year) { //arg. Month is 1..12
    return new Date(year, month, 0).getDate(); 
   } 

   function now () {
    let date = new Date();
    let hour=date.getHours();
    let minute=date.getMinutes();
    let sec=date.getSeconds();
    let day=date.getDate();
    let month=date.getMonth(); //0-11
    let year=date.getFullYear();
    let weekday=date.getDay();//0-6, с Вс
    let daysInMonth = getDaysInMonth(month+1,year);
    return [hour, minute, sec, day, month, daysInMonth, weekday];
   }

   function pad2 (i) { return String('0' + i).slice(-2); }

   function drawPlanet(radius, color, dat, all, dat1, all1, signed = []) { //Одна "планета"
    //Контур из точек по количеству позиций "планеты":
    ctx.beginPath();
    ctx.strokeStyle = color;
    for (let i=0; i<all; i++) {
     let theta = (2*PI/all)*i;
     let cx = centerX + radius * cos((PI/2) - theta);
     let cy = centerY - radius * sin((PI/2) - theta);
     ctx.moveTo (cx,cy);
     ctx.arc(cx,cy,1,0,2*PI);
    }
    if (signed.length==0) signed = Array(all).fill(0).map(function(val,i){ return i; });
    ctx.stroke();
    ctx.closePath();
    //Сама "планета":
    let theta = (2*PI/all1)*dat1;
    let cx = centerX + radius * cos((PI/2) - theta);
    let cy = centerY - radius * sin((PI/2) - theta);
    let alignedDat = isNaN(parseInt(signed[dat])) ? signed[dat] : pad2(signed[dat]);
    ctx.font = fontSize +'px monospace';
    let textWid = ctx.measureText(alignedDat);
    let arrowRadius = Math.max(Math.floor(textWid.width*2/3),2);
    ctx.beginPath();
    ctx.moveTo (cx,cy);
    ctx.arc (cx,cy,arrowRadius,0,2*PI);
    ctx.fillStyle = color;
    ctx.fill();
    ctx.fillStyle = '#fff';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(alignedDat, cx, cy);
    ctx.closePath();
   }

   function numberOfDay() { //Номер дня в текущем году
    let now = new Date();
    let start = new Date(now.getFullYear(), 0, 0);
    let diff = now - start;
    let oneDay = 1000 * 60 * 60 * 24;
    let day = Math.floor(diff / oneDay);
    return day;
   }

   function numberOfDays() { //Количество дней в текущем году
    let now = new Date();
    let year = now.getFullYear();
    return (year%4==0) && (year%100!=0) || (year%400==0) ? 366 : 365;
   }

   function draw() { //Отрисовка всего
    let date = now(); //[hour, minute, sec, day, month, daysInMonth, weekday]
    clearScreen();
    let minRadius = Math.max(fontSize*2,24),
        maxRadius = size / 2 - fontSize*2,
        d = Math.floor((maxRadius-minRadius)/5),
        hms = date[0]*60*60+date[1]*60+date[2],
        h24 = 24*60*60;
    drawPlanet(maxRadius, 'green',date[4]+1, 12, (numberOfDay()-1)*h24+hms, numberOfDays()*h24, 
     ['','янв','фев','мар','апр','май','июн','июл','авг','сен','окт','ноя','дек']); //month
    let days = Array(date[5]+1).fill(0).map(function(val,i){ return i; });
    drawPlanet(maxRadius - d, 'red',date[3], date[5], (date[3]-1)*h24+hms, date[5]*h24, 
     days); //day
    drawPlanet(maxRadius - 2*d, 'gray',date[6], 7, date[6]*h24+hms,7*h24, 
     ['Вс','Пн','Вт','Ср','Чт','Пт','Сб']); //weekday
    drawPlanet(minRadius + 2*d, 'blue',date[0], 24, hms, h24); //hours
    drawPlanet(minRadius + d, 'darkcyan',date[1], 60, date[1]*60+date[2], 60*60); //min
    drawPlanet(minRadius, 'magenta',date[2], 60, date[2], 60); //sec
   }

   function clearScreen() { ctx.clearRect(0, 0, canvas.width, canvas.height); }

   draw();
   setInterval (draw, 1000);
  } //spaceCanvas

  window.addEventListener ('load', function (e) {
   spaceCanvas('spaceCanvas',600); //id канвы, размер её стороны
  });  
 </script>
 <noscript>
  <div style="margin: 0 auto; text-align: center;">
   Включите в браузере JavaScript для работы приложения!
  </div>
 </noscript> 

P.S. Ниже прикреплена версия скрипта, работающая в Internet Explorer 11 (исключено определение массивов как [] и методы массивов map и fill). Увидеть, что изменилось в исходнике, можно, открыв исходник прикреплённой страницы.

В самом новом на сегодня "Wordpress" этот код отлично добавился как виджет типа "HTML-код".

 spaceClock_IE11.html, открыть в текущей вкладке (5 Кб)

Версия, которая также совместима с IE11 + дополнительно должна адаптироваться к изменениям размера окна браузера (особенно удобно нажать в браузере клавишу F11 для полноэкранного отображения):

 spaceClock_fullScreen.html, открыть в текущей вкладке (5 Кб)


теги: javascript графика время

12.03.2020, 17:18; рейтинг: 80