scrapbox-position
指定した座標にあるscrpabox editor上の諸々の情報を取得する函数群
内容物
getBorder({x,y})
指定した座標に一番近い文字境界を取得する
cursorや選択範囲の端など、ほぼ文字境界に位置する座標に対して用いる
引数
x: ページの可視領域の左上を原点とした座標系でのx成分
y: ページの可視領域の左上を原点とした座標系でのy成分
返り値
code:ts
type Return = {
left?: CharObject; //境界の左側の文字
right?: CharObject; //境界の右側の文字
line: LineObject; // 文字がいる行
} |{
line: LineObject;
message: 'No char found';
} | {
message: 'No line found.' | 'No border matched.';
};
行頭や行末など、片側の文字がないときはundefinedが入る
境界が見つからなかったときはすべてundefinedになる
getLine({y})
指定した座標上にある行のDOMを取得する
引数
y: ページの可視領域の左上を原点とした座標系でのy成分
返り値
LineObject | undefined
行がなかったらundefinedになる
dependencies
code:script.js
import {char as c} from '/api/code/programming-notes/scrapbox-char-accessor/script.js';
import {line as l} from '/api/code/programming-notes/scrapbox-line-accessor/script.js';
const scrapboxDOM = {
lines: document.getElementsByClassName('lines')?.0, };
code:script.js
export function getBorder({x, y}) {
const line = getLine({y});
if (!line) return {message: 'No line found.'};
const chars = [...line.DOM.querySelectorAll('spanclass^="c-"')]; if (chars.length === 0) return {line, message: 'No char found'};
// 文字境界のリストを作る
const borders = [...chars.map((char, i, charList) => {
return {left: i === 0 ? undefined : charListi - 1, right: char}; }), {left: chars.pop(), right: undefined}];
//console.log({borders});
境界の判定方法
https://gyazo.com/0af8f64b7e28b7a51423280b22c5df2a
それぞれの文字の中間を判定領域の端とする
行頭や行末など、どちらかの文字がない場合は、一方の文字のwidthの値を用いる
code:script.js
const matchedBorders = borders
.flatMap(({left: lChar, right: rChar}) => {
const lRect = lChar?.getBoundingClientRect?.() ?? rChar.getBoundingClientRect();
const rRect = rChar?.getBoundingClientRect?.() ?? lChar.getBoundingClientRect();
const left = (lChar ? lRect.left : lRect.left - lRect.width) + lRect.width / 2;
const right = (rChar ? rRect.left : rRect.right) + rRect.width / 2;
//console.log({left, right});
return [];
});
if (matchedBorders.length === 0) return {line, message: 'No border matched.'};
if (matchedBorders.length === 1) return {line, ...matchedBorders0}; もし複数の文字が見つかったら、yから絞り込みをかける
18:33:52 絞り込みに失敗しているようだtakker.icon
yの値が境界ギリギリだったからみたい
https://kakeru.app/f783878a7a06325356f6a919e5882e50 https://i.kakeru.app/f783878a7a06325356f6a919e5882e50.svg
code:script.js
const lineRect = line.DOM.getBoundingClientRect();
const border = matchedBorders
.map(({left, right}, i) => {
const lTop = left?.DOM?.getBoundingClientRect?.()?.top ?? lineRect.top;
const rTop = right?.DOM?.getBoundingClientRect?.()?.top ?? lineRect.top;
const top = i === 0 ? lineRect.top : Math.max(lTop, rTop);
return {left, right, top};
})
.find(({top}, i, list) => top <= y && y < (listi + 1?.top ?? lineRect.bottom)); if (border) return {line, left: border.left, right: border.right};
return {line, message: 'No border matched.'};
}
code:script.js
export function getLine({y}) {
const rect = line.getBoundingClientRect();
return rect.top <= y && y < rect.bottom;
}));
}