Simple Calendar

采用 canvas 绘制一个简单的日历:

See the Pen simple calendar by Wen Yan (@yanwen) on CodePen.

https://static.codepen.io/assets/embed/ei.js


index.pug

#calendar

style.styl

@import url('https://fonts.googleapis.com/css?family=Permanent+Marker');

body
  text-align center
  
  #calendar
    & > canvas
      background url('https://www.hdwallpapers.in/download/polar_bear_child_dream-1366x768.jpg') no-repeat center center
      background-size cover

app.js

/**
 * @author Yan Wen <i@yanwen.email>
 * @license GPLv3
 * @modefiedAt 10/08/2018
 */

;(function (extend) {
  extend.prototype.getFullMonth = function () {
    return this.getMonth() + 1;
  };

  /** Get the start day of the first day in a month */
  extend.prototype.getStartDay = function () {
    // Get total number of days since 1/1/1800
    const startDay1800 = 3;
    const totalNumberOfDays = this.getTotalNumberOfDays();

    // Return the start day
    return (totalNumberOfDays + startDay1800) % 7;
  };
  
  /** Get the total number of days since Jan 1, 1800 */
  extend.prototype.getTotalNumberOfDays = function () {
    let total = 0;
    // Get the total days from 1800 to year - 1
    for (let i = 1800; i < this.getFullYear(); i++)
      total += Date.isLeapYear(i) && 366 || 365;

    // Add days from Jan to the month prior to the calendar month
    for (let i = 1; i < this.getFullMonth(); i++)
      total += this.getNumberOfDaysInMonth(i);

    return total;
  };

  /** Get the number of days in a month */
  extend.prototype.getNumberOfDaysInMonth = function (month) {
    switch (month) {
      case 1: case 3: case 5: case 7: case 8: case 10: case 12:
        return 31;
      case 4: case 6: case 9: case 11:
        return 30;
      case 2:
        return Date.isLeapYear(this.getFullYear()) && 29 || 28;
      default:
        return 0;
    }
  };

  /** Determine if it is a leap year */
  extend.isLeapYear = function (year) {
    return year % 400 === 0 ||
      (year % 4 === 0 && year % 100 !== 0);
  };
}(Date));


(function () {
  'use strict';

  const _Renderer = (function () {

    const self = {
      get ROOT_X () { return 0; },
      get ROOT_Y () { return 0; },
      
      get HEADER_FONT () { return 'arial'; },
      get DATE_FONT () { return 'Permanent Marker'; },
      get LINE_WIDTH () { return 2; },
      get BG_COLOR () { return 'transparent'; },
      get CURRENT_DATE_COLOR () { return 'rgb(58, 173, 222)'; },
      get OTHER_DATE_COLOR () { return 'rga(0, 0, 0)'; },
      
      get MONTH_NAMES () {
        return [
          "January", "February", "March", "April", "May", "June",
          "July", "August", "September", "October", "November", "December"
        ];
      },
      get DAY_NAMES () {
        return [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ];
      },
    };

    const $ = function () {
      const canvas = (() => {
        const canvas = document.createElement('canvas');
        document.querySelector('#calendar').appendChild(canvas);
        return canvas;
      })();
      const ctx = canvas.getContext('2d');
      
      console.log(canvas);

      self.boxSize = -1;
      self.headerFont = ``;
      self.dateFont = ``;

      self.__defineGetter__('canvas', function () {
        return canvas;
      });
      self.__defineGetter__('ctx', function () {
        return ctx;
      });

      self._drawLine = __drawLine.bind(this);
      self._renderCalendar = __renderCalendar.bind(this);
      self._refreshWindowParams = __refreshWindowParams.bind(this);
      
      // Responsive
      window.addEventListener('resize', () => self._refreshWindowParams(), true);
    };

    $.prototype.render = function () {
      self._refreshWindowParams();
      self._renderCalendar();
    };

    function __drawLine (x, y, dx, dy) {
      self.ctx.beginPath();
      self.ctx.moveTo(x, y);
      self.ctx.lineTo(x + dx, y + dy);
      self.ctx.stroke();
    }

    function __refreshWindowParams() {
      const size = Math.min(window.innerWidth, window.innerHeight) * .8;
      self.boxSize = size / 8;

      // 7 x 7 in 7 x 8
      self.canvas.width = (self.boxSize / .618) * 7 + self.ROOT_X;
      self.canvas.height = self.boxSize * 8 + self.ROOT_Y;

      self.headerFont = `normal ${self.boxSize / 2}px/0 ${self.HEADER_FONT}`;
      self.dateFont = `normal ${self.boxSize / 2}px/0 ${self.DATE_FONT}`;
    }

    function __renderCalendar (date = new Date()) {
      /** BG */
      self.ctx.fillStyle = self.BG_COLOR;
      self.ctx.fillRect(self.ROOT_X, self.ROOT_Y, self.boxSize * 8, self.boxSize * 9);

      /** Header */
      self.ctx.font = self.headerFont;
      self.ctx.strokeText(
        `${self.MONTH_NAMES[date.getMonth()]} ${date.getFullYear()}`,
        self.canvas.width / 2.6, self.boxSize);   // 1x boxSize
      self._drawLine(0, self.boxSize * 1.3, self.canvas.width, 0); // 1x boxSize
      
      self.DAY_NAMES.forEach( (DAY_NAME, index) =>
        self.ctx.strokeText(DAY_NAME, self.boxSize * (index + 1) * 1.3, self.boxSize * 2) /* 2x boxSize */
      );
      
      /** Day (++2x boxSize) */
      self.ctx.font = self.dateFont;
      self.ctx.fillStyle = self.CURRENT_DATE_COLOR;

      const startDay = date.getStartDay();
      const numberOfDaysInMonth = date.getNumberOfDaysInMonth(date.getFullMonth());
   
      let strokePos = { x: startDay, y: 3 };
      for (let i = 1; i <= numberOfDaysInMonth; i++) {
        if (date.getDate() === i) {
          // Current date 
          self.ctx.fillText(
            i < 10 && `  ${i}` || ` ${i}`,
            self.boxSize * (strokePos.x + 1) * 1.3,
            self.boxSize * strokePos.y );
        } else {
          self.ctx.strokeText(
            i < 10 && `  ${i}` || ` ${i}`,
            self.boxSize * (strokePos.x + 1) * 1.3,
            self.boxSize * strokePos.y );
        }

        strokePos = { ...strokePos, x: strokePos.x + 1 };
        if ((i + startDay) % 7 == 0)
           strokePos = { ...strokePos, x: 0, y: strokePos.y + 1 };
      }
    }

    return $;
  }());


  return function () {
    const _renderer = new _Renderer();
    console.log({_renderer});

//     const _render = () => {
//       _renderer.render();
//       window.requestAnimationFrame(_render);
//     };
//     window.requestAnimationFrame(_render);

    _renderer.render();
    window.addEventListener('resize', () => {
      _renderer.render();
    });
  };

}())();

作者: YanWen

Web 开发者

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

You are commenting using your WordPress.com account. Log Out /  更改 )

Google photo

You are commenting using your Google account. Log Out /  更改 )

Twitter picture

You are commenting using your Twitter account. Log Out /  更改 )

Facebook photo

You are commenting using your Facebook account. Log Out /  更改 )

Connecting to %s