HeatMapCalendar 라는 이름은 그냥 내 마음대로 붙여봤음...




2012 년 예제






1년치 달력을 가로축을 월(month), 세로축을 주(week)로 배열하여 한 눈에 볼 수 있게 해주는 달력.

각 날짜마다 데이터가 있고, 해당 데이터를 색값으로 계산하여 여러 형태로 보여줄 수 있음.

원래라면 좀 더 예쁘게 나왔어야 하는건데,

예를 들면 여름은 빨갛고 겨울은 파랗다거나...-_-;

 

참조: http://mbostock.github.com/d3/ex/calendar.html

 

참조링크의 원본은 그림을 그리는 스타일이나, 간단하게 테이블로 가능할 것 같아서 만들어 봤음.

핵심은 아래와 같음.

  1. 1년치 날짜정보를 배열로 가져와 pivot 처리.
  2. 날짜정보를 인덱스로 가지는 배열과 맵핑하여 색값 계산.

 

색값은 참조링크처럼 말 그대로 HeatMap 처럼 나타낼 수도 있고, 데이터의 성격에 따라서 여러가지 응용이 가능할 것으로 보여짐.







<!DOCTYPE HTML>

<html>
<head>
<style>
.tbl_cal { border: 0px; border-collapse: collapse; border-spacing: 0px; }
.tbl_cal td { width: 10px; height: 10px; border: 1px solid #CDCDCD; }
.tbl_cal .no_date { border: 0px; }
.tbl_cal .sunday { border-top: 2px solid black; }
.tbl_cal .saturday { border-bottom: 2px solid black; }
.tbl_cal .first_day_of_month { border-top: 2px solid black; }
.tbl_cal .first_week_of_month { border-left: 2px solid black; }
.tbl_cal .last_week { border-right: 2px solid black; }
.tbl_cal .last_day_of_year { border-bottom: 2px solid black; }
</style>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<script>
$(document).ready(function(){
    // 년도 지정
    var year = new Date().getFullYear();
    // 시작일, 종료일
    var iStart = new Date(year, 0, 1, 0, 0, 0).getTime();
    var iEnd   = new Date(year, 11, 31, 0, 0, 0).getTime();
    // 날짜 데이터를 저장할 배열
    var aDate = [[]];
    // 시작 빈칸 채우기
    var iStartPos = new Date(year,0,1,0,0,0).getDay();
    for (var i=0; i<iStartPos; i++) {
        aDate[0][i] = null;
    }
    // 날짜 데이터 채우기 (+임시 데이터 만들기)
    var aTmpData = {};
    i = iStart;
    var iWeek = 0;
    var j = iStartPos;
    while (i <= iEnd) {
        var iPos = (new Date(i)).getDay();
        if (!aDate[iWeek]) aDate.push([]);
        var oDate = getDate(i);
        aDate[iWeek][iPos] = {
            date: oDate
        };
        // 임시 데이터 만들기
        aTmpData[oDate.year+oDate.month+oDate.day] = Math.floor(Math.random() * 255) + 1;
        i += 86400000;
        ++j;
        if (j > 6) {
            j = 0;
            ++iWeek;
        }
    }
    // 마지막 빈칸 채우기
    iEndPos = new Date(i).getDay();
    if (iEndPos > 0) {
        for (i=iEndPos; i<7; i++) {
            aDate[iWeek][i] = null;
        }
    }
    // pivot
    var aDatePivot = [[], [], [], [], [], [], []];
    for (i=0; i<aDate.length; i++) {
        for (j=0; j<aDate[i].length; j++) {
            aDatePivot[j].push(aDate[i][j]);   
        }
    }   
    // 테이블 만들기
    var sMonth = '';
    var iTotalWeeks = aDatePivot[0].length;
    var sCalendarTable = '';
    sCalendarTable += '<table id="tbl_cal" class="tbl_cal">';
    for (i=0; i<aDatePivot.length; i++) {
        sCalendarTable += '<tr>';
        for (j=0; j<iTotalWeeks; j++) {
            var aClass = [];
            var bgColor = '';
            var sId = '';
            if (aDatePivot[i][j] == null) aClass.push('no_date');
            else {
                var d = aDatePivot[i][j].date;
                sId = ' id="tbl_cal_'+d.year+d.month+d.day+'"';
                // 테이블 상단,하단
                if (i == 0) aClass.push('sunday');
                else if (i == 6) aClass.push('saturday');
                // 월의 첫날, 마지막 날
                if (d.day == '01') {
                    aClass.push('first_day_of_month');
                }
                // 월간 분리
                if (d.month != sMonth) {
                    sMonth = d.month;
                    aClass.push('first_week_of_month');
                }
                // 테이블 마지막
                if (j == iTotalWeeks-1) {
                    aClass.push('last_week');
                    if (d.month==12 && d.day==31 && i<6) aClass.push('last_day_of_year');
                }
                else if (j == iTotalWeeks-2) {
                    if (aDatePivot[i][j+1] == null) aClass.push('last_week');
                }
                // 색상 지정 (with 임시데이터)
                var RGB = '';
                RGB = (255 - aTmpData[d.year+d.month+d.day]).toString(16);
                if (RGB.length < 2) RGB = '0'+RGB;
                RGB = '#FF'+RGB+RGB;               
                bgColor = ' style="background-color:'+RGB+';"';
            }
            sCalendarTable += '<td'+sId+' class="'+aClass.join(' ')+'"'+bgColor+'></td>';
        }
        sCalendarTable += '</tr>';
    }
    sCalendarTable += '</table>';
    var sInfoLayer = '<div id="info" style="display:none;"></div>';
     
    // 붙이기 + 이벤트 걸기
    $('body').append(sCalendarTable+sInfoLayer)
        .find('[id="tbl_cal"] td')
        .mouseover(function(){
            if (!$(this).attr('id')) return;
            $('#info').show();
        })
        .mousemove(function(e){
            if (!$(this).attr('id')) return;
            var d = $(this).attr('id').replace(/tbl_cal_/, '');
            var info = d+'<br />'+aTmpData[d];
            $('#info').html(info);
            console.log();
            $('#info')
                .css('border', '1px solid black').css('background-color', 'yellow')
                .css('left', e.pageX - (parseInt($('#info').css('width'))/2))
                .css('top', e.pageY - (parseInt($('#info').css('height'))) - 5)
                .css('z-index', 999).css('position', 'absolute').css('display', '');
        })
        .mouseout(function(){
            if (!$(this).attr('id')) return;
            $('#info').hide();
        });
 
    // 타임스탬프를 받아서 Ymd 값 반환
    function getDate(timestamp)
    {
        var zeroPad = function(digit, len){
            var i = digit.toString().length;
            while (i < len) {
                digit = '0'+digit;
                ++i;
            }
            return digit;
        };
        var aResult = {
            'year'null,
            'month': null,
            'day':   null
        };
        var d = new Date(timestamp);
        aResult.year  = d.getFullYear()+'';
        aResult.month = zeroPad(d.getMonth()+1, 2)+'';
        aResult.day   = zeroPad(d.getDate(), 2)+'';
        return aResult;
    }
});
</script>
</head>
 
<body>
</body>
</html>


Posted by bloodguy
,