ドキュメント / Web Components
タイマーを埋め込む
プレーヤーとエディターは標準の Web Components です。フレームワークなしで、どのページにもプレーヤーを埋め込めます。
- ビルド不要
- データを送信しない
- CSS 変数でテーマ設定
タイマーを再生する
完全な HTML の例をそのままページにコピーしてください。配信しているブラウザ用モジュールは依存関係を含んでいるので、パッケージのインストールやビルドツールは不要です。sequence プロパティを設定し、ブラウザが音を出せるよう、スタートは訪問者に押してもらいます。
プレーヤーの下部には、既定で「Powered by EigenTime Timer」という小さなリンクが表示され、新しいタブでこのサイトを開きます。ただのリンクなので、クリックされるまで何も読み込みません。no-attribution 属性を付けると非表示になります。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex">
<title>My timer</title>
<style>
body { margin: 0 auto; padding: 1rem; max-width: 48rem; font: 16px system-ui, sans-serif; }
sequence-timer-player, sequence-timer-builder { display: block; margin: 1rem 0; }
</style>
</head>
<body>
<h1>My timer</h1>
<sequence-timer-player locale="en-US"></sequence-timer-player>
<p data-status role="status"></p>
<script type="module">
import 'https://timer.eigentime.org/embed/v1/player.js';
const sequence = {
schemaVersion: 1,
id: 'my-timer',
metadata: { name: { 'en-US': 'My timer' } },
children: [
{ type: 'phase', id: 'prepare', role: 'prepare', durationMs: 5000 },
{
type: 'group', id: 'round', repeat: 4,
children: [
{
type: 'phase', id: 'work', role: 'work', durationMs: 30000,
cues: { start: 'beep', countdownSeconds: 3 },
},
{ type: 'phase', id: 'rest', role: 'rest', durationMs: 10000 },
],
},
],
};
const player = document.querySelector('sequence-timer-player');
player.sequence = sequence;
player.addEventListener('complete', () => {
document.querySelector('[data-status]').textContent = 'Sequence complete.';
});
// Exit stops the run and leaves fullscreen; the page decides what follows,
// such as closing a dialog. Pages with nothing to do can hide ::part(exit).
player.addEventListener('exit', () => {
document.querySelector('[data-status]').textContent = 'Stopped. What Exit does next is up to your page.';
});
// Playback starts only when the visitor presses Start.
</script>
</body>
</html>エディターとプレーヤー
エディターとプレーヤーは別々に読み込まれます。エディターの start イベントは検証済みのシーケンスを渡し、例ではそれを同じプレーヤーコンポーネントに渡しています。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex">
<title>My timer</title>
<style>
body { margin: 0 auto; padding: 1rem; max-width: 48rem; font: 16px system-ui, sans-serif; }
sequence-timer-player, sequence-timer-builder { display: block; margin: 1rem 0; }
</style>
</head>
<body>
<h1>My timer</h1>
<sequence-timer-builder locale="en-US"></sequence-timer-builder>
<sequence-timer-player locale="en-US"></sequence-timer-player>
<p data-status role="status"></p>
<script type="module">
import 'https://timer.eigentime.org/embed/v1/player.js';
import 'https://timer.eigentime.org/embed/v1/builder.js';
const sequence = {
schemaVersion: 1,
id: 'my-timer',
metadata: { name: { 'en-US': 'My timer' } },
children: [
{ type: 'phase', id: 'prepare', role: 'prepare', durationMs: 5000 },
{
type: 'group', id: 'round', repeat: 4,
children: [
{
type: 'phase', id: 'work', role: 'work', durationMs: 30000,
cues: { start: 'beep', countdownSeconds: 3 },
},
{ type: 'phase', id: 'rest', role: 'rest', durationMs: 10000 },
],
},
],
};
const player = document.querySelector('sequence-timer-player');
player.sequence = sequence;
player.addEventListener('complete', () => {
document.querySelector('[data-status]').textContent = 'Sequence complete.';
});
// Exit stops the run and leaves fullscreen; the page decides what follows,
// such as closing a dialog. Pages with nothing to do can hide ::part(exit).
player.addEventListener('exit', () => {
document.querySelector('[data-status]').textContent = 'Stopped. What Exit does next is up to your page.';
});
const builder = document.querySelector('sequence-timer-builder');
builder.sequence = sequence;
builder.addEventListener('start', async (event) => {
player.sequence = event.detail.sequence;
await player.updateComplete;
player.start();
player.scrollIntoView({ block: 'start' });
});
// Playback starts only when the visitor presses Start.
</script>
</body>
</html>イベント
statechange、phasechange、cue、complete の各イベントを受け取って、独自の UI や分析に使えます。コンポーネント自身はどこにもデータを送りません。
「終了」ボタンは計時を止めて全画面を解除し、exit イベントを送ります。その後どうするか(ダイアログを閉じる、エディターに戻るなど)はページ側で決めます。終了の必要がないページでは ::part(exit) { display: none; } で隠せます。
const player = document.querySelector('sequence-timer-player');
player.addEventListener('statechange', (event) => {
// 'idle' | 'running' | 'paused' | 'completed'
console.log(event.detail.state);
});
player.addEventListener('phasechange', (event) => {
const { step } = event.detail;
console.log(step.phase.role, step.durationMs);
});
player.addEventListener('cue', (event) => {
console.log(event.detail.sound, event.detail.secondsRemaining);
});
player.addEventListener('complete', () => {
console.log('done');
});
player.addEventListener('exit', () => {
// Stopped and out of fullscreen: close your dialog, go back, …
});テーマ設定
色とサイズは CSS カスタムプロパティ(--sequence-primary、--sequence-work など)として公開されているので、Shadow DOM に触れずにテーマを変えられます。ボタンの文字を読みやすく保つため、--sequence-primary と --sequence-on-primary は一緒に設定してください。
sequence-timer-player {
color-scheme: dark; /* light, dark, or inherit the page's */
--sequence-primary: #60a5fa;
--sequence-on-primary: #0b1220;
--sequence-work: #60a5fa;
--sequence-rest: #2dd4bf;
}サイズとレイアウト
プレーヤーは自身の幅に合わせてレイアウトします。要素に幅を与えれば、リングと文字もそれに合わせて大きさが変わります。幅 800px 以上ではリングを左、表示とボタンを右に置きます。layout="stacked" は常に縦に積み、layout="split" はボタンが収まる 576px 以上で左右に並べます。
--sequence-dial-size はページ上のリングの最大サイズ(既定 440px)で、clamp() を使えばビューポートの高さに合わせられます。全画面ではリングは常に画面に収まります。
さらに細かく調整するには、::part() で dial、phase、time、note、stats、next、controls、toolbar、exit、attribution の各パーツにスタイルを当てます。no-controls を付けるとボタンがすべて隠れ、ページ側で再生を操作できます。
<sequence-timer-player locale="ja-JP"></sequence-timer-player>
<style>
sequence-timer-player {
max-width: 720px;
}
</style>配信とセルフホスト
ブラウザ用モジュールは /embed/v1/ の下にあり、別オリジンからの import を許可しています。player.js だけを import すればエディターは読み込まれません。これらの URL を直接使うのがいちばん簡単です。v1 のファイルはその場で更新され、変更は後方互換のものに限られます。npm パッケージはまだありません。
自分でコピーをホストするには、下のコマンドでモジュールをサイトの 1 つのフォルダーにダウンロードします。player.js、エディターを使うなら builder.js、両者が共有するモジュール(名前にバージョンのハッシュが付きます)、サードパーティのライセンスです。そのうえで、例の import をコピー先(たとえば /sequence-timer/player.js)に向けます。
コピーは自動では更新されません。更新するときは、1 つのファイルだけを置き換えず、フォルダー全体をダウンロードし直してください。ファイルはハッシュ付きの名前で互いを import しているため、別のリリースのファイルを混ぜると読み込めません。
# Download into sequence-timer/, then put the whole folder on your site
mkdir sequence-timer && cd sequence-timer
base=https://timer.eigentime.org/embed/v1
curl -fO "$base/player.js" -fO "$base/builder.js" -fO "$base/THIRD-PARTY-LICENSES.txt"
# The shared module's name changes with each release; read it from the imports
for file in $(grep -ho '"\./[^"]*\.js"' player.js builder.js | tr -d '"' | sort -u); do
curl -fO "$base/${file#./}"
done埋め込んだコンポーネントは、オフライン用のワーカーを登録せず、サイトの設定を読まず、シーケンスを自動保存せず、分析データも送りません。保存と共有はホスト側が管理します。インストールとオフライン対応はオンライン版のツールだけの機能です。