D3.jsで時間割を作るテスト
Drag & drop機能を一旦削る
✅やはりzoomがおかしい
04:10:36 直った
次にやること
特定の領域のみ表示されるようzoomしておく
k,x,yの値がわかればいい
code:app.js
(async () => {
const { h, html, render, useState, useMemo, useCallback, useEffect } = htmPreact;
const { select, scaleBand, scaleTime, axisBottom, axisLeft, drag, zoom, schemeCategory10 } = d3;
const XAxis = ({ scale, height, padding }) => {
const chartHeight = height - padding.top - padding.bottom;
useEffect(() => select(g).call(
axisBottom(scale)
.ticks(10)
.tickFormat((d) => d.getHours() === 0 && d.getMinutes() === 0 && d.getSeconds() === 0
// 日付変更線では年月日も表示する
? lightFormat(d, "yyyy-MM-dd HH:mm")
: lightFormat(d, "HH:mm")
)
.tickSize(-chartHeight)
return html<g transform=${translate(0, ${height - padding.bottom})} ref=${setG} />;
};
const Bar = ({ event: { from, to, categoryNo }, scale, height }) =>
html`<rect
x=${scale(from)}
y=${0}
width=${(scale(to) ?? 0) - (scale(from) ?? 0)}
height=${height}
/>`;
const padding = { top: 10, right: 10, bottom: 10, left: 75 };
const Timeline = ({ schedule, width, height }) => {
const timeLineDomain = useMemo(
() => [
startOfDay(min(schedule.map((e) => e.from))),
addDays(startOfDay(max(schedule.map((e) => e.from))), 1),
],
);
const originalScale = useMemo(
() => scaleTime()
.domain(timeLineDomain)
);
const barHeight = height - padding.top - padding.bottom;
const zoomer = useMemo(() => zoom()
.translateExtent(0, 0], [width, 0)
.on("zoom", (e) => {
setScale(() => e.transform.rescaleX(originalScale));
return html`<style>
:host {
position: fixed;
bottom: 0;
left: 0;
border: 1px solid gray;
border-radius: 4px;
}
.tooltip {
position: absolute;
text-align: center;
width: auto;
height: auto;
padding: 5px;
font: 12px;
background: white;
-webkit-box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.8);
-moz-box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.8);
box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.8);
visibility: hidden;
}
.bar:hover {
fill: brown;
}
rect {
mix-blend-mode: multiply;
}
.dragging {
opacity: 0.7;
}
</style>
<svg width=${width} height=${height} ref=${setSVG}>
<${XAxis} scale=${scale} padding=${padding} height=${height} />
${schedule.map((e) => html<${Bar} event=${e} scale=${scale} height=${barHeight} />)}
</svg>`;
};
/** @type {Event[]} */
const schedule = [
{
categoryNo: 0,
from: new Date(2020, 7, 11),
to: new Date(2020, 7, 11, 7, 0, 0),
},
{
categoryNo: 1,
from: new Date(2020, 7, 11, 7, 30, 0),
to: new Date(2020, 7, 11, 8, 30, 0),
},
{
categoryNo: 3,
from: new Date(2020, 7, 11, 8, 30, 0),
to: new Date(2020, 7, 11, 10, 0, 0),
},
{
categoryNo: 7,
from: new Date(2020, 7, 12),
to: new Date(2020, 7, 12, 7, 0, 0),
},
{
categoryNo: 5,
from: new Date(2020, 7, 12, 7, 30, 0),
to: new Date(2020, 7, 12, 8, 30, 0),
},
{
categoryNo: 2,
from: new Date(2020, 7, 13, 8, 30, 0),
to: new Date(2020, 7, 13, 10, 0, 0),
},
];
render(html<${Timeline} schedule=${schedule} width=${900} height=${200} />, document.body);
})().catch((e) => console.error(e));