这几天闲来无聊,就自己做了一个Js版本的农历日历,由于Js的日期是从1970年1月1日开始计时的以偏移量来计算周期的,所以日历就暂时只支持1970年以后的日期。另外查询了一下农历日期的计算方法,发现没有数学模型和公式可循,只能以古历的不规则数组进行提取换算,所以就保存了1970到2050范围内的转换参数,日历支持的上限就就到2050年为止。
效果图如下:
源码如下:
var monthName_ch = new Array('一月','二月','三月','四月','五月','六月','七月','八月','九月','十月','十一月','十二月');
var day_of_month = new Array(31,28,31,30,31,30,31,31,30,31,30,31);
var lunarDay = new Array('初一','初二','初三','初四','初五','初六','初七','初八','初九','初十','十一','十二','十三','十四','十五','十六','十七','十八','十九',
'二十','二十一','二十二','二十三','二十四','二十五','二十六','二十七','二十八','二十九','三十');
var festival = {'1-1':'元旦','2-14':'情人节','3-5':'雷锋纪念日','3-8':'妇女节','3-15':'消费者日','4-1':'愚人节','4-5':'清明节','5-1':'劳动节','6-1':'儿童节','8-1':'建军节','9-10':'教师节','10-1':'国庆节','12-25':'圣诞节'};
var lFestival = {'1-1':'春节','1-15':'元宵节','5-5':'端午节','7-7':'七夕','8-15':'中秋节','9-9':'重阳节','12-8':'腊八','12-30':'除夕'}
var currentYear;
var currentMonth;
var currentDayIndex;
function getEl(element) {
if (arguments.length > 1) {
for (var i = 0, elements = [], length = arguments.length; i < length; i++)
elements.push($(arguments[i]));
return elements;
}
element = document.getElementById(element);
return element;
}
function checkLeap(y){
if(y%400==0)
return true;
else if(y%4==0&&y%100!=0)
return true;
return false;
}
/**
* @param year
* @return how many days in param year
**/
function lYearDays(y) {
var i, sum=348;
for(i=0x8000;i>0x8;i>>=1)
sum+=(lunarInfo[y-1900]&i)?1:0;
return (sum+leapDays(y));
}
/**
* @param year
* @return number of leap month days of param year
**/
function leapDays(y) {
if(leapMonth(y))
return ((lunarInfo[y-1900]&0x10000)?30:29);
else return 0;
}
/**
* @param year
* @return leap month of param year, return 0 if none
**/
function leapMonth(y) {
return (lunarInfo[y-1900]&0xf);
}
/**
* @param month
* @return number of days in param year/month
**/
function monthDays(y,m) {
return ((lunarInfo[y-1900]&(0x10000>>m))?30:29 );
}
/**
* constructor of Lunar object
* @param current date
* @return Lunar with lYear/lMonth/lDay
**/
function Lunar(objDate) {
var i, leap=0, temp=0;
var baseDate = new Date(1900,0,31);
var offset = (objDate - baseDate)/86400000;
this.nMonth = objDate.getMonth()+1;
this.nDay = objDate.getDate();
for(i=1900; i<2050 && offset>0; i++) {
temp = lYearDays(i);
offset -= temp;
}
if(offset<0) {
offset += temp;
i--;
}
this.year = i;
leap = leapMonth(i); //which month leap
this.isLeap = false;
for(i=1; i<13 && offset>0; i++) {
//if leap month
if(leap>0 && i==(leap+1) && this.isLeap==false){
--i;
this.isLeap = true;
temp = leapDays(this.year);
}else
temp = monthDays(this.year, i);
//cancel leap
if(this.isLeap==true && i==(leap+1))
this.isLeap = false;
offset -= temp;
}//end of for
if(offset==0 && leap>0 && i==leap+1)
if(this.isLeap)
this.isLeap = false;
else{
this.isLeap = true;
--i;
}
if(offset<0){
offset += temp;
--i;
}
this.month = i;
this.day = offset + 1;
}
/*******************
* GUI js core
*******************/
function initCal(){
initStyle();
var t = new Date();
currentYear = t.getYear();
currentMonth = t.getMonth();
currentDayIndex = 'cal_tbody_'+(t.getDate()+t.getDay()-1);
showCal();
}
function showCal(){
var tDate = new Date ();
if(currentYear ==tDate.getYear()&¤tMonth==tDate.getMonth())
getEl(currentDayIndex).style.color='orange';
else
getEl(currentDayIndex).style.color='white';
var today = new Date(currentYear,currentMonth,1);
var month = today.getMonth();
var year = today.getYear();
var week = today.getDay();
var day = today.getDate();
var idIndex = week+1;
var dayLength = day_of_month[month];
var i;
if(month==1&&checkLeap(year))
dayLength++;
for(i=1;i<idIndex;i++)
getEl('cal_tbody_'+i).innerText = '';
for(var i=0;i<dayLength;i++){
getEl('cal_tbody_'+idIndex).innerText = i+1;
idIndex++;
}
for(i=idIndex;i<43;i++)
getEl('cal_tbody_'+i).innerText = '';
getEl('cal_title').innerText = monthName_ch[month]+' '+currentYear+'年';
}
function initStyle(){
getEl('cal_pre').onmouseover = function(){getEl('cal_pre').src='images/cal_pre_over.png'};
getEl('cal_pre').onmouseout = function(){getEl('cal_pre').src='images/cal_pre.png'};
getEl('cal_next').onmouseover = function(){getEl('cal_next').src='images/cal_next_over.png'};
getEl('cal_next').onmouseout = function(){getEl('cal_next').src='images/cal_next.png'};
getEl('cal_ok_btn').onmouseover = function(){getEl('cal_ok_btn').style.color='blue'};
getEl('cal_ok_btn').onmouseout = function(){getEl('cal_ok_btn').style.color='rgb(75,75,75)'};
getEl('cal_reset_btn').onmouseover = function(){getEl('cal_reset_btn').style.color='blue'};
getEl('cal_reset_btn').onmouseout = function(){getEl('cal_reset_btn').style.color='rgb(75,75,75)'};
getEl('cal_exit_btn').onmouseover = function(){getEl('cal_exit_btn').style.color='blue'};
getEl('cal_exit_btn').onmouseout = function(){getEl('cal_exit_btn').style.color='rgb(75,75,75)'};
var tds = getEl('cal_tbody').getElementsByTagName('td');
var tdsId;
for(var i=0;i<tds.length;i++){
tds[i].onmouseover = tdStyleMV;
tds[i].onmouseout = tdStyleMO;
}
}
function tdStyleMV(){
var obj = window.event.srcElement;
obj.style.background='url("images/cal_unit_over.png")';
var day = obj.innerText;
if(day.length==0){
obj.title='';
return;
}
var lDate = new Lunar(new Date(currentYear,currentMonth,day));
obj.title= formatLunar(lDate);
}
function formatLunar(obj){
if(festival[obj.nMonth+'-'+obj.nDay])
var fest = festival[obj.nMonth+'-'+obj.nDay];
if(lFestival[obj.month+'-'+obj.day])
var lFest = lFestival[obj.month+'-'+obj.day];
var str = '';
if(obj.isLeap)
str = '农历润'+monthName_ch[obj.month-1]+lunarDay[obj.day-1]+' ';
else
str = '农历'+monthName_ch[obj.month-1]+lunarDay[obj.day-1]+' ';
if(fest)
str = str+ fest;
if(lFest&&!obj.isLeap)
str = str+ ' '+lFest;
return str;
}
function tdStyleMO(){
var obj = window.event.srcElement;
obj.style.background='url("images/cal_unit.png")';
}
function toggleMonth(mark){
if(mark==0){
currentMonth--;
if(currentMonth==-1){
if(currentYear==1970){
alert("该日历不支持1970年之前的日期");
currentMonth++;
return;
}
currentYear--;
currentMonth=12;
}
}else{
currentMonth++;
if(currentMonth==12){
if(currentYear==2050){
alert("该日历不支持2050年之后的日期");
currentMonth++;
return;
}
currentYear++;
currentMonth = 0;
}
}
showCal();
}
function resetCal(){
getEl('cal_year').value='';
getEl('cal_month').value='';
var d = new Date();
var t = new Date();
currentYear = t.getYear();
currentMonth = t.getMonth();
showCal(currentYear,currentMonth);
}
function queryCal(){
var year = getEl('cal_year').value;
var month = getEl('cal_month').value;
if(year.length==0)
alert("输入年份非空");
if(isNaN(year))
alert("年份输入格式错误!");
else if(isNaN(month))
alert("月份输入格式错误!");
else if(year<1970||year>2050)
alert("输入年份超出界限");
else if(month.length==0){
currentYear = year;
currentMonth = 1;
showCal(currentYear,currentMonth);
}else if(month>12||month<1)
alert("输入月份超出界限");
else{
currentYear = year;
currentMonth = month-1;
showCal(currentYear,currentMonth);
}
}
具体的图片资源和源码文件我已经上传到我的资源了,有兴趣的朋友可以去下载来看看,反复调试过Css的效果之后,发现在火狐浏览器和IE下Div边距的2px问题很令人恼火,而且Dom事件模型也很大区别,所以在火狐里就无法使用了,希望以后有空可以再做一个更通用一点的。