Skip to content

Instantly share code, notes, and snippets.

@shimizu
Last active March 2, 2018 07:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shimizu/2660d136ba9156de119ac5ccb8130d33 to your computer and use it in GitHub Desktop.
Save shimizu/2660d136ba9156de119ac5ccb8130d33 to your computer and use it in GitHub Desktop.
Responsive Calendar
license: mit
scrolling: yes

点線をドラッグしてチャートのサイズを変更することができます。

<!DOCTYPE html>
<html lang="jp">
<head>
<style>
html, body {
padding: 0px;
width:100%;
height:100%;
}
#chart {
width: 90%;
border: 8px dashed gray;
border-left: none;
border-top:none;
border-bottom:none;
cursor:all-scroll;
}
.calendar {
width: 100%;
}
</style>
<script src="//unpkg.com/babel-standalone@6.26.0/babel.min.js"></script>
<script src="//unpkg.com/babel-standalone@6.26.0/babel.min.js"></script>
<script src="//unpkg.com/d3@4.12.2/build/d3.min.js"></script>
</head>
<body>
<div id="chart"></div>
<script type="text/babel">
const WeeksPerYear = 53;
const parent = d3.select("#chart");
const formatPercent = d3.format(".1%");
render()
function render(){
d3.range(2013, 2018).forEach(function(year){
const calendarWrpper = parent.selectAll(`.calendar.Y${year}`).data([null]);
const wrapper = calendarWrpper.merge(calendarWrpper.enter().append("div").classed(`calendar Y${year}`, true));
renderCalendar(wrapper, year)
})
}
function renderCalendar(wrapper, years){
const w = wrapper.node().clientWidth;
const h = wrapper.style(w / WeeksPerYear * 7);
const marginGuid = w / WeeksPerYear;
const margin = {top:marginGuid*2, left:marginGuid*2, bottom:0, right:marginGuid*2};
const pw = w - (margin.left + margin.right);
const ph = h - (margin.top + margin.bottom);
const cellSize = pw / WeeksPerYear;
const updateSVG = wrapper.selectAll("svg").data([years]);
const svg = updateSVG.merge(updateSVG.enter().append("svg"))
.attr("width", w)
.attr("height", marginGuid*7 + (margin.top + margin.bottom))
const updateCalendar = svg.selectAll("g").data(d => [d])
const enterCalendar = updateCalendar.enter().append("g");
enterCalendar.append("g").classed("cellLayer", true);
enterCalendar.append("g").classed("monthLayer", true);
enterCalendar.append("g").classed("labelLayer", true);
const calendar = updateCalendar.merge(enterCalendar).attr("transform", `translate(${margin.left}, ${margin.top}) `);
const cellLayer = calendar.select(".cellLayer");
const monthLayer = calendar.select(".monthLayer");
const labelLayer = calendar.select(".labelLayer");
const title = renderCalendarTitle(labelLayer, cellSize, margin);
const cell = renderCalendarCell(cellLayer, cellSize);
const manth = renderMonthGuideline(monthLayer, cellSize)
}
function renderMonthGuideline(layer, cellSize){
const pathMonth = function(t0) {
const t1 = new Date(t0.getFullYear(), t0.getMonth() + 1, 0),
d0 = t0.getDay(), w0 = d3.timeWeek.count(d3.timeYear(t0), t0),
d1 = t1.getDay(), w1 = d3.timeWeek.count(d3.timeYear(t1), t1);
return "M" + (w0 + 1) * cellSize + "," + d0 * cellSize
+ "H" + w0 * cellSize + "V" + 7 * cellSize
+ "H" + w1 * cellSize + "V" + (d1 + 1) * cellSize
+ "H" + (w1 + 1) * cellSize + "V" + 0
+ "H" + (w0 + 1) * cellSize + "Z";
}
const updateLine = layer.selectAll("path")
.data(function(d) { return d3.timeMonths(new Date(d, 0, 1), new Date(d + 1, 0, 1)); })
const line = updateLine.merge(updateLine.enter().append("path"))
.attr("fill", "none")
.attr("stroke", "#000")
.attr("d", pathMonth);
return pathMonth;
}
function renderCalendarTitle(layer, cellSize, margin){
const updateYearLabel = layer.selectAll("text").data(function(d){ return [d] })
const yearLabel = updateYearLabel.merge(updateYearLabel.enter().append("text"));
yearLabel.attr("transform", `translate(${-cellSize},${cellSize * 3.5}) rotate(90)`)
.attr("font-size", cellSize)
.attr("font-family", "sans-serif")
.attr("text-anchor", "middle")
.attr("y", "0.5em")
.attr("x", "-0.5em")
.text(function(d) { return d; });
return yearLabel;
}
function renderCalendarCell(layer, cellSize){
const updateCell = layer.selectAll("rect")
.data(function(d) { return d3.timeDays(new Date(d, 0, 1), new Date(d + 1, 0, 1)); })
const cell = updateCell.merge(updateCell.enter().append("rect"))
.attr("width", cellSize)
.attr("height", cellSize)
.attr("fill", "none")
.attr("stroke", "#ccc")
.attr("x", function(d) { return d3.timeWeek.count(d3.timeYear(d), d) * cellSize; })
.attr("y", function(d) { return d.getDay() * cellSize; })
.datum(d3.timeFormat("%Y-%m-%d"));
return cell
}
//divエレメントをドラッグでリサイズできるようにする。
const dispatch = d3.dispatch("resize");
dispatch.on("resize", render);
setResizeControler();
function setResizeControler(){
const drag = d3.drag()
.on("drag", resized)
d3.select("#chart")
.call(drag);
function resized(e){
const s = d3.event.sourceEvent;
const w = (s.pageX < 400) ? 400 : s.pageX;
d3.select(this)
.style("width", `${w}px`)
dispatch.call("resize");
}
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment