こんにちは、しましまです。
今回は、HTMLのSVGタグに触れる機会がありましたので、そのことについてとりあげます。

きっかけ
きっかけは個人的なツール作りで、時間管理をするものがほしいと思いました。
ツールでやりたいことの一つとして、「入力した開始時刻と終了時刻を可視化したい」という思いがあり、
それを実現できるライブラリを探しましたが、これといったものが見つかりませんでした。
その解決案として SVG タグで実現しようと考え、その方針にしました。
コード
今回は Vue.js で作成しました。
HTML
<svg width="300" height="300" viewBox="0 0 300 300">
<!-- 時計盤 -->
<circle cx="150" cy="150" r="140" fill="#fff" stroke="#ccc" stroke-width="2" />
<!-- ハイライトエリア -->
<path :d="arcPath" fill="rgba(64, 186, 141, 0.8)" />
<!-- start 針 -->
<line :x2="startX" :y2="startY" x1="150" y1="150" stroke="#0067C0" stroke-width="4" />
<!-- end 針 -->
<line :x2="endX" :y2="endY" x1="150" y1="150" stroke="#d9333f" stroke-width="4" />
<!-- 中心点 -->
<circle cx="150" cy="150" r="5" fill="#000" />
</svg>
Script
const props = defineProps({
start: { type: String, required: true },
end: { type: String, required: true },
})
const parseTime = (timeStr) => {
const [h, m] = timeStr.split(':').map(Number)
return h + m / 60
}
const toCoord = (hours, radius = 100) => {
const angleDeg = (hours % 12) * 30 - 90 // 0時を上に
const angleRad = (angleDeg * Math.PI) / 180
const x = 150 + radius * Math.cos(angleRad)
const y = 150 + radius * Math.sin(angleRad)
return { x, y }
}
const startHours = computed(() => parseTime(props.start))
const endHours = computed(() => parseTime(props.end))
const startCoord = computed(() => toCoord(startHours.value))
const endCoord = computed(() => toCoord(endHours.value))
const startX = computed(() => startCoord.value.x)
const startY = computed(() => startCoord.value.y)
const endX = computed(() => endCoord.value.x)
const endY = computed(() => endCoord.value.y)
// 時計盤上のハイライト扇形(start → end 方向に描く)
const arcPath = computed(() => {
const r = 120
const startAngle = (startHours.value % 12) * 30 - 90
const endAngle = (endHours.value % 12) * 30 - 90
const start = {
x: 150 + r * Math.cos((startAngle * Math.PI) / 180),
y: 150 + r * Math.sin((startAngle * Math.PI) / 180),
}
const end = {
x: 150 + r * Math.cos((endAngle * Math.PI) / 180),
y: 150 + r * Math.sin((endAngle * Math.PI) / 180),
}
const largeArcFlag = (endHours.value - startHours.value + 12) % 12 > 6 ? 1 : 0
return `
M 150 150
L ${start.x} ${start.y}
A ${r} ${r} 0 ${largeArcFlag} 1 ${end.x} ${end.y}
Z
`
})
動作イメージ(9:00 – 14:00)

おわりに
自由度が高く、できることが多い印象を受けました。
まだ慣れていないためか、保守するのは少ししんどそうな気がしています。
困ったときの手段として引き出しにしまっておきます。