はてなブログで ResizeObserver と SVG のオーバレイを組み合わせる
はてなブログの Markdown モードでは、HTML を直接書けることが知られているが、記事の中に SVG を直接マークアップしたり <script>
タグを書けることはあまり知られていない。
しかし、これらを巧く使うと、たとえば関連する単語間を線で結ぶといったギミックに対応できる。
I
don't
trust
any
candidate
.To
begin
with,
they
always
make
promises
they
don't
keep
.
さらに、ブラウザの幅が変わって改行位置が変わっても、線の端点の座標が再計算される。
ソース
<div id="myContainer" style="display: flex; flex-direction: column; position: relative; gap: 1rem;"> <div style="display: flex; flex-direction: row; flex-wrap: wrap;"> <div style="flex: 0 0 auto;">I </div> <div style="flex: 0 0 auto;">don't </div> <div style="flex: 0 0 auto;">trust </div> <div style="flex: 0 0 auto;">any </div> <div style="flex: 0 0 auto; display: flex; z-index: 1"> <div style="flex: 0 0 auto" id="candidate">candidate</div>. </div> </div> <div style="display: flex; flex-direction: row; flex-wrap: wrap;"> <div style="flex: 0 0 auto;">To </div> <div style="flex: 0 0 auto;">begin </div> <div style="flex: 0 0 auto;">with, </div> <div style="flex: 0 0 auto; display: flex; z-index: 1"> <div style="flex: 0 0 auto;" id="they1">they</div> </div> <div style="flex: 0 0 auto;">always </div> <div style="flex: 0 0 auto;">make </div> <div style="flex: 0 0 auto;" id="promises1">promises </div> <div style="flex: 0 0 auto; display: flex; z-index: 1"> <div style="flex: 0 0 auto;" id="they2">they</div> </div> <div style="flex: 0 0 auto;">don't </div> <div style="flex: 0 0 auto;">keep </div> <div style="flex: 0 0 auto; border: 1px solid #cccccc;" id="promises2"></div> <div style="flex: 0 0 auto;">.</div> </div> <div id="overlay" style="position: absolute; left:0; right:0; top:0; bottom:0;"> <svg id="svg" x=0 y=0> <line id="line1" stroke="#cccc00" stroke-width="2" /> <line id="line2" stroke="#cccc00" stroke-width="2" /> </svg> </div> </div> <script> const resizeObserver = new ResizeObserver(entries => { const promises1 = document.getElementById('promises1'); const promises2 = document.getElementById('promises2'); promises2.style.width = `${promises1.clientWidth}px`; const overlay = document.getElementById('overlay'); const svg = document.getElementById('svg'); svg.setAttribute('width', `${overlay.clientWidth}`); svg.setAttribute('height', `${overlay.clientHeight}`); const candidate = document.getElementById('candidate'); const x1 = candidate.offsetLeft + candidate.clientLeft + candidate.clientWidth * 0.5; const y1 = candidate.offsetTop + candidate.clientTop + candidate.clientHeight * 0.5; candidate.style.backgroundColor = 'white'; candidate.style.border = '1px solid #cccc00'; const they1 = document.getElementById('they1'); const x2 = they1.offsetLeft + they1.clientLeft + they1.clientWidth * 0.5; const y2 = they1.offsetTop + they1.clientTop + they1.clientHeight * 0.5; const they2 = document.getElementById('they2'); const x3 = they2.offsetLeft + they2.clientLeft + they2.clientWidth * 0.5; const y3 = they2.offsetTop + they2.clientTop + they2.clientHeight * 0.5; they1.style.backgroundColor = 'white'; they1.style.border = '1px solid #cccc00'; they2.style.backgroundColor = 'white'; they2.style.border = '1px solid #cccc00'; const line1 = document.getElementById('line1'); line1.setAttribute('x1', x1); line1.setAttribute('y1', y1); line1.setAttribute('x2', x2); line1.setAttribute('y2', y2); const line2 = document.getElementById('line2'); line2.setAttribute('x1', x1); line2.setAttribute('y1', y1); line2.setAttribute('x2', x3); line2.setAttribute('y2', y3); }); resizeObserver.observe(document.getElementById('myContainer')); </script>
この文字列に対して、(^\s+)|(\n)
を ''
に置換して、余分なインデントや改行を消し、Markdown モード or はてなブログモードの編集エリアにペーストする。