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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more