Public
Edited
Dec 18, 2022
1 fork
2 stars
Insert cell
Insert cell
clock = {
// Parameters
const width = 400;
const radius = width / 2;
const height = width;
const [cx, cy] = [width / 2, height / 2];

// Clock settings
const minTickLength = 20;
const hourTickLength = 40;
const hourTickWidth = 10;

const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height]);

const center = svg.append("g").attr("transform", `translate(${cx}, ${cy})`);

const sixty = d3.range(0, 60);
const twelve = d3.range(0, 12);

const scaleSixty = d3
.scaleLinear()
.domain([0, 60])
.range([0, 2 * Math.PI]);

const scaleTwelve = d3
.scaleLinear()
.domain([0, 12])
.range([0, 2 * Math.PI]);

// Dials

const minuteTicks = center
.selectAll("line.minute-ticks")
.data(sixty)
.join("line")
.attr("class", "minute-ticks")
.attr("x1", radius)
.attr("x2", radius - 12)
.attr("stroke", "black")
.attr("stroke-width", 4)
.attr("transform", (m) => `rotate(${min2deg(m)})`);

const hourTicks = center
.selectAll("line.marker-hours")
.data(twelve)
.join("line")
.attr("class", "marker-hours")
.attr("x2", hourTickLength)
.attr("stroke", "black")
.attr("stroke-width", 10)
.attr(
"transform",
(h) => `rotate(${hour2deg(h)})translate(${radius - hourTickLength}, 0)`
);

// Handles
const handles = center.append("g");

const handleHours = center.append("g");
handleHours
.append("line")
.attr("x1", -40)
.attr("x2", radius - 60)
.attr("stroke", "black")
.attr("stroke-width", 18);

// Minutes Handle
const handleMinutes = center.append("g");
handleMinutes
.append("line")
.attr("x1", -40)
.attr("x2", radius - 10)
.attr("stroke", "black")
.attr("stroke-width", 16);

// Seconds Handle
const secondHandleColor = "red";
const handleSeconds = center.append("g");
handleSeconds.append("circle").attr("r", 8).attr("fill", secondHandleColor);
handleSeconds
.append("line")
.attr("x1", -60)
.attr("x2", 140)
.attr("stroke-width", 6)
.attr("stroke", secondHandleColor);
handleSeconds
.append("circle")
.attr("cx", 140)
.attr("r", 14)
.attr("fill", secondHandleColor);

function updateClock() {
const { hours, minutes, secondsDecimal } = getDateParts(new Date());

handleHours
.data([hours + minutes / 60])
.attr("transform", (h) => `rotate(${hour2deg(h)})`);

handleMinutes
.data([minutes])
.attr("transform", (m) => `rotate(${min2deg(m)})`);

handleSeconds
.data([secondsDecimal])
.attr("transform", (s) => `rotate(${sec2deg(s)})`);
}

const intervalId = setInterval(() => {
updateClock();
}, 1000 / 10);

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
SECONDS_CYCLE = 58.5
Insert cell
sec2deg = (sec) =>
sec > SECONDS_CYCLE ? -90 : (360 / SECONDS_CYCLE) * sec - 90
Insert cell
min2deg = (min) => (360 / 60) * min - 90
Insert cell
hour2deg = (hour) => (360 / 12) * hour - 90
Insert cell
rad2deg = (rad) => (180 / Math.PI) * rad
Insert cell
function getDateParts(date) {
return {
hours: date.getHours(),
minutes: date.getMinutes(),
seconds: date.getSeconds(),
secondsDecimal: date.getSeconds() + date.getMilliseconds() / 1000
};
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more