点線をドラッグしてチャートのサイズを変更することができます。
Last active
March 2, 2018 07:23
-
-
Save shimizu/2660d136ba9156de119ac5ccb8130d33 to your computer and use it in GitHub Desktop.
Responsive Calendar
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: mit | |
scrolling: yes |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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