时钟日历

听说12是一个神奇的数字?

Posted by theVan on April 3, 2021

简介

时钟日历,一个像时钟一样的日历。。。(主要基于 canvas)。

代码

/* Date part start */

function MyDate(year) {
    this.data = [];
    this.year = year;
    this.months = 12;

    this.initData = () => {
        for (let i=0; i< this.months; i++) {
            temp = [];
            temp.push((new Date(this.year, i, 1)).getDay());
            temp.push((new Date(this.year, i + 1 , 0)).getDate());
            this.data.push(temp);
        }
    };
}

/* Date part end */

/* View part start */
 
function Rim(canvas, items, outerRadius, innerRadius) {
    this.rimItemsList = [];
    this.loacteHelperArr = [];  // 定位辅助数组
    this.canvas = canvas;
    this.canvasContext = canvas.getContext('2d');
    this.items = items;
    this.outerRadius = outerRadius;
    this.innerRadius = innerRadius;
    this.initCenterRadian = Math.PI * 3 / 2;
    this.itemRadian = Math.PI * 2 / items;
    this.value = 12;
    this.selectedValue = 12;

    this.init = () => {
        this.createLocateHelper(this.initCenterRadian);
        this.initCanvas();
        // this.test();
        this.createFrame(this.value);
        this.initRimItems();
        this.registerEvent();
        return this;
    };

    // view module

    this.initCanvas = () => {
        this.canvasContext.translate(this.outerRadius, this.outerRadius);
        this.canvasContext.strokeStyle = '#ccc';
        this.canvasContext.fillStyle = 'rgba(0,0,0,.2)';
        this.canvasContext.font = '100px 蔚然雅黑';
        this.canvasContext.textAlign = 'center';
        this.canvasContext.textBaseline = 'top';
        this.canvasContext.save();
    }

    this.test = () => {
        this.canvasContext.beginPath();
        this.canvasContext.moveTo(-this.outerRadius, 0);
        this.canvasContext.lineTo(this.outerRadius, 0);
        this.canvasContext.moveTo(0, -this.outerRadius);
        this.canvasContext.lineTo(0, this.outerRadius);
        this.canvasContext.stroke();
    }

    this.createFrame = (slectedValue = 0) => {
        for (let i = 0, step = this.itemRadian; i < this.items; i++, step += this.itemRadian) {
            this.canvasContext.rotate(step);
            this.canvasContext.beginPath();
            this.canvasContext.moveTo(this.loacteHelperArr[0], this.loacteHelperArr[1]);
            this.canvasContext.arc(0, 0, this.innerRadius, this.loacteHelperArr[2], this.loacteHelperArr[3]);
            this.canvasContext.lineTo(this.loacteHelperArr[4], this.loacteHelperArr[5]);
            this.canvasContext.arc(0, 0, this.outerRadius, this.loacteHelperArr[3], this.loacteHelperArr[2], true);
            this.canvasContext.closePath();
            this.canvasContext.stroke();
            if (i == slectedValue) {
                this.canvasContext.fillText(i + 1, 0, -this.outerRadius + 32);
                this.canvasContext.fill();
            }
            this.canvasContext.restore();
            this.canvasContext.save();
        }
    };

    this.clearFrame = () => {
        this.canvasContext.clearRect(-this.outerRadius, -this.outerRadius, 2 * this.outerRadius, 2 * this.outerRadius);
    };

    this.createLocateHelper = (centerRadian) => {
        // 起始点 x0 y0 坐标
        this.loacteHelperArr.push(Math.cos(centerRadian - this.itemRadian / 2) * this.innerRadius);
        this.loacteHelperArr.push(Math.sin(centerRadian - this.itemRadian / 2) * this.innerRadius);

        // 圆弧起始和结束弧度
        this.loacteHelperArr.push(centerRadian - this.itemRadian / 2);
        this.loacteHelperArr.push(centerRadian + this.itemRadian / 2);

        // 过度点 x1 y1 坐标
        this.loacteHelperArr.push(Math.cos(centerRadian + this.itemRadian / 2) * this.outerRadius);
        this.loacteHelperArr.push(Math.sin(centerRadian + this.itemRadian / 2) * this.outerRadius);
    };

    // event module

    this.initRimItems = () => {
        for (let i = 0; i < this.items; i++) {
            this.rimItemsList.push(new RimItem(i, this.items, this.outerRadius, this.innerRadius));
        }
    };

    this.registerEvent = (callback) => {
        this.canvas.onmousemove = (e) => {
            this.rimItemsList.forEach(item => {
                if (item.isOn(e.offsetX, e.offsetY)) {
                    item.update((value) => {
                        if (item.isChange) {
                            this.clearFrame();
                            this.createFrame(value);
                            this.selectedValue = value;
                            item.isChange = false;
                            callback(value);
                        };
                    });
                }
            });
        };
    }
}

function RimItem(number, items, outerRadius, innerRadius) {
    this.number = number;
    this.items = items;
    this.outterRadius = outerRadius;
    this.innerRadius = innerRadius;
    this.radian = Math.PI * 2 / items;
    this.centerRadian = number * (Math.PI * 2 / items);
    this.ischange = true;     // 记录事件触发前 rimItem 的触发状态
    this.isSelect = false;
    this.center = outerRadius;
    this.value = (number + (this.items / 4 - 1)) % this.items;

    this.isOn = (offsetX, offsetY) => {
        if (Math.abs(this.centerRadian - this.getRadian(offsetX, offsetY)) < this.radian / 2) {
            this.isSelect = true;
            return true;
        }
        this.isSelect = false;
        this.isChange = true;
        return false;
    };

    // 计算触发事件坐标的角弧度
    this.getRadian = function (offsetX, offsetY) {
        let tranX = offsetX - this.center;
        let tranY = offsetY - this.center;
        let radian = Math.atan(tranY / tranX);
        // 按照象限进行坐标弧度转换
        if (tranX <= 0 && tranY >= 0) {
            radian = Math.PI + radian;
        } else if (tranX <= 0 && tranY <= 0) {
            radian = Math.PI + radian;
        } else if (tranX > 0 && tranY <= 0) {
            radian = Math.PI * 2 + radian;
        }
        if (radian > Math.PI * 2 - (Math.PI / this.items)) {
            radian = Math.atan(tranY / tranX);
        }
        return radian;
    };

    this.update = function (callback) {
        callback(this.value);
    };

}

/* View part end */

/* Main part start */

const OUTER_RADIUS = 450; // px
const INNER_RADIUS = 300; // px
const ITEMS = 12;

let canvas = document.getElementById('calender_rim');
let table = document.getElementById('calender_table');

let date = new Date();
let myDate = new MyDate(date.getFullYear());
let rim = new Rim(canvas, ITEMS, OUTER_RADIUS, INNER_RADIUS);
let curMonth = date.getMonth();
let curDate = date.getDate();
let curYear = date.getFullYear();

init(curMonth);

function init(month) {
    console.log(curMonth);
    rim.value = month;
    rim.init();
    myDate.initData(month);
    renderMonth(month);
    rim.registerEvent((value)=> {
        table.innerHTML='';
        renderMonth(value);
    });
};

function renderMonth(month) {
    let monthData = getMonthData(month);
    let innerHTML = '<caption><h1>'+curYear+'</h1></caption><tr>';
    monthData.forEach((v,i) => {
        if (curMonth == month && curDate == v) {
            innerHTML += '<td style="background-color: rgba(0,0,0,.2)">' + v +'</td>';
        } else {
            innerHTML += '<td>' + v + '</td>';
        }
        if ((i+1) % 7 == 0) {
            innerHTML += '</tr><tr>';
        }
    });
    innerHTML +='<tr>';
    table.innerHTML=innerHTML;
};

function getMonthData(month) {
    let monthData = [];
    for (let i = 0; i < myDate.data[month][0]; i++) {
        monthData.push('&nbsp;');
    }
    for (let i = 0; i < myDate.data[month][1]; i++) {
        monthData.push(i + 1);
    }
    return monthData;
};

console.log(getMonthData(7));

/* Main part end */