HTML5 API

Posted コメントするカテゴリー: javascript

HTML5 API

HTML5のAPIについて、いくつか試してみます。
HTML5APIは非常に多く用意されているので、簡単にいくつかピックアップしてみます。

注意が必要な点としては、使用するAPIがブラウザの種類やブラウザのバージョンによって
対応/非対応があるという点です。
また、バージョンによっても対応状況が異なります。

APIの使用を検討する際に十分検討して実装する必要があります。

以下、いくつかのAPIで簡単なサンプルを試してみます。

Geolocation API

(許可済の場合)ユーザの現在の緯度経度を取得します。

以下は「navigator.geolocation.getCurrentPosition」の例です。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>

<div>lat:<span id='lat'></span></div>
<div>lon:<span id='lon'></span></div>

<script>
navigator.geolocation.getCurrentPosition(
	(position) => {
		// 現在地が取得できた場合の処理
		let lat = document.querySelector("#lat");
		let lon = document.querySelector("#lon");
		
		lat.innerHTML = position.coords.latitude;
		lon.innerHTML = position.coords.longitude;
	},
	(error) => {
		// エラーが発生した場合の処理(オプション)
		console.error(error.message);
	}
);
</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

クロスドメインメッセージ

異なるドメインで開かれているウィンドウ(ブラウザ)に対し、メッセージを送るAPIです。

送信元では、postMessage()メソッドを使います。
postMessage()メソッドは、1つめの引数にメッセージ、2つめの引数に送信先ウィンドウの出身を表す文字列を指定し、

送信先(受信側)では、windowオブジェクトでmessageイベントが発生します。
messageイベントでは、以下のプロパティを持ちます。

data
送信元から送られたメッセージのコピー

source
送信元のwindowオブジェクト

origin
送信元の出身(URL形式)を表した文字列

送信先(受信側)の処理では、処理の先頭でoriginプロパティを確認すると安全です。
これは異なる出身からのメッセージは処理せず、送信元の出身が明確な場合に処理をするという考えです。

以下、簡単なサンプルを用意しました。

これはサンプルでは親ウィンドウ、子ウィンドウで書いていますが、これは異なるドメインのiframeを表示する場合でも同じです。

クロスドメインメッセージ (親ウィンドウから子ウィンドウへメッセージを渡す例)

送信側(親ウィンドウ)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>

<div id="bottom">
    <input type="button" id="openwindow"  value="ウィンドウオープン">
    <input type="button" id="postmessage" value="子ウィンドウへメッセージの送信">
</div>

<script>
// ウィンドウの定義
let popup_window;

// ウィンドウオープンボタン
let openwindow_dom = document.querySelector("#openwindow");
openwindow_dom.addEventListener('click', (e) => {
    // 新規ウィンドウを開く
    popup_window = window.open(
        "https://propanlab.net/demo/blogsample/js/195/test2_1.html",
        "test",
        "width=600, height=500"
    );
}, false);

// 子ウィンドウへのメッセージの送信
let postmessage_dom = document.querySelector("#postmessage");
postmessage_dom.addEventListener('click', (e) => {
    // 開いたウィンドウに対し、postMessage()メソッドを使い、メッセージを送る
    popup_window.postMessage(
        "サンプル文字列です。postMessageのテスト",
        "https://propanlab.net/"
    );
}, false);

</script>
</body>
</html>

受信側(子ウィンドウ)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>

メッセージ受信側画面

<script>
addEventListener("message", (event) => {
    // 送信元のorigin情報をチェックし、想定と異なる場合は以下の処理を止める
    if (event.origin != "https://propansystem.net") {
        console.log("origin err !");
        return;
    }
    
    // 以下、なんらかの処理

    // 例:受信したevent.dataをポップアップ表示する
    alert("受信したデータ:" + event.data);

}, false);
</script>
</body>
</html>

サーバ上のHTMLはこちら(test2.html)

画面にアクセスし、一旦「ウィンドウオープン」ボタンを押下して子ウィンドウを開き、
その後に「子ウィンドウへメッセージの送信」ボタンを押下すると、
子ウィンドウに対してpostMessage()メソッドを使ってデータが送信されていることが確認できます。

クロスドメインメッセージ (子ウィンドウから親ウィンドウへメッセージを渡す例)

受信側(親ウィンドウ)
便宜上、一旦親ウィンドウから子ウィンドウを開き、その後に子ウィンドウから親ウィンドウへメッセージを送るサンプルになっています。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>

<div id="bottom">
    <input type="button" id="openwindow"  value="ウィンドウオープン">
</div>

<script>
// ウィンドウの定義
let popup_window;

// ウィンドウオープンボタン
let openwindow_dom = document.querySelector("#openwindow");
openwindow_dom.addEventListener('click', (e) => {

    // 新規ウィンドウを開く
    popup_window = window.open(
    	"https://propanlab.net/demo/blogsample/js/195/test3_1.html",
    	"test",
    	"width=600, height=500"
    );
}, false);

// (子ウィンドウからの)メッセージの受信
window.addEventListener("message", (event) => {

    // 送信元のorigin情報をチェックし、想定と異なる場合は以下の処理を止める
    if (event.origin != "https://propanlab.net") {
        console.log("origin err !");
        return;
    }
    
    // 以下、なんらかの処理

    // 例:受信したevent.dataをポップアップ表示する
    alert("受信したデータ:" + event.data);

}, false);

</script>
</body>
</html>

送信側(子ウィンドウ)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>

メッセージ受信側画面

<div id="bottom">
    <input type="button" id="postmessage" value="親ウィンドウへのメッセージの送信">
</div>

<script>
// 親ウィドウへのメッセージの送信
let postmessage_dom = document.querySelector("#postmessage");
postmessage_dom.addEventListener('click', (e) => {
    // 親ウィンドウに対し、postMessage()メソッドを使い、メッセージを送る
    window.opener.postMessage(
        "親ウィンドウへのテスト文字列送信",
        "https://propansystem.net/"
    );
}, false);
</script>
</body>
</html>

サーバ上のHTMLはこちら(test3.html)

画面にアクセスし、子ウィンドウを開きます。
その後、子ウィンドウにある「親ウィンドウへメッセージの送信」ボタンを押下すると、
親ウィンドウ側でメッセージを受信しポップアップ表示がされます。

上記の2つの例で、別ドメインにまたがるポップアップ(またはiframe)ウィンドウに対し、データの送受信ができることがわかります。

Web Worker(ウェブワーカー)

メイン処理とは別にバックグラウンドで別な処理を実行するAPIです。
重い計算処理や非同期タスクをメインスレッドとは別に実行する際に使います。

Web Workerは処理を別スレッドにする際、メイン処理とサブ処理で
それぞれjavascriptのファイルを分けて書くと処理の見通しが良くなります。
(便宜上Worker側で処理することをサブ処理と同じ意味で書いています)

またその他の留意点として以下があります。
・javascriptで長時間の処理が発生する場合、メインの処理が止まるリスクが無くなる
・(サブ処理から)windowオブジェクトやDocumentオブジェクトにアクセスすることはできない
・postMessage()メソッドを使いサブ処理へ値を渡すことができる

Web Worker(ウェブワーカー) 具体例

ここからはWeb Workerの具体例をあげます。

サブ処理側で処理を開始したい場合、Workerオブジェクトをnewしてインスタンスを生成して使用します。

// Web Workerを作成
const worker = new Worker('外部javascriptファイル');

Workerを作成したら、Wokerに対してデータを送信できます。

worker.postMessage('なんらかのデータ');

Worker側で重い処理(時間のかかる処理)を開始し、処理が完了したタイミングでmessageを受け取ります。

// Web Workerからのメッセージを受け取る
worker.onmessage = function (event) {
    
    // 重い処理が終わった後のなんらかの処理
    
};

Web Worker(ウェブワーカー) 具体例 (画面サンプル)

メイン処理

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>

メイン処理

<script>

alert("メイン処理:開始");

// Web Workerを作成
const worker = new Worker('test4_sub.js');

// Web Workerからのメッセージを受け取る
worker.onmessage = function (event) {
	// Workerから受け取った結果を表示
    alert("Web Worker側のサブ処理終了 : 計算結果:" + event.data);
};

// Web Workerにデータを送信
// 1から10000000000までの全合計値を計算
worker.postMessage(10000000000);

alert("メイン処理:終了 (サブ処理はバックグラウンドで実行中)");

</script>
</body>
</html>

サブ処理

self.onmessage = function (event) {

    console.log("sub start");

    const num = event.data; // メインスレッドから受け取ったデータ
    let sum = 0;

    // 重い処理(1~numまでの合計を計算)
    for (let i = 1; i <= num; i++) {
        sum += i;
    }

    console.log("sub end");

    // 計算結果をメインスレッドに送信
    self.postMessage(sum);
};

サーバ上のHTMLはこちら(test4_main.html)

画面にアクセスすると、まず「メイン処理開始」のダイアログが表示されます。
画面にしたがってOKボタンを押下すると、サブ処理がWorkerで実行されます。
その後、サブ処理は時間がかかる処理を進めていますが、メイン処理側ではサブ処理の状態とは無関係に処理が完了します。
サブ処理が(長時間経過したあと)完了するとメイン処理側にメッセージを渡し、メイン処理側でダイアログを表示して終了します。

Blob

BlobはBinary Large Objectの略で、バイナリーデータを扱う際のAPIです。
以下の特徴があります。
・Blobはバイナリーデータを扱います
・小きいサイズのデータでも扱えます
・Blobに対しては、バイト単位で大きさを判定、MIMEタイプを調べる、小さいBlobに分割すること、が可能です
・APIは非同期で処理されます
・webブラウザに保存できます(メモリ、ディスク使用)
・ローカルファイルシステム、ローカルデータベース、他のウィンドウ、ほかのWokerからBlobを読み書き可能です。

・structored cloneアルゴリズムのサポート
・メッセージイベントを通じて取得
・BlobBuiderオブジェクトで作成可能

Blob取得後の処理例は
・postMessage()メソッドで他のウィンドウやWokerに送信可能
・BlobをAJAXを用いてサーバにアップロード可能
・createObjectURL()メソッドで特種なblobを取得可能

以下、簡単なサンプルをあげます。

Blob – Blobを生成するサンプル

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>

<script>
let blob = new Blob(["サンプルテキスト"], { type: "text/plain" });

// 開発者ツールで確認
console.log("blob -> " + blob);

for (let key in blob) {
    console.log(key + " : " + blob[key]);
}
</script>
</body>
</html>

サーバ上のHTMLはこちら(test5.html)

画面にアクセスし、開発者ツールでconsole.logの値を確認すると

blob -> [object Blob]

という出力がされます。

Blobというオブジェクトが生成されていることが確認できます。

また、そのオブジェクトの内容を以下の方法で調べてみると、

for (let key in blob) {
    console.log(key + " : " + blob[key]);
}

次のような出力結果になります。

size : 24
type : text/plain
arrayBuffer : function arrayBuffer() { [native code] }
slice : function slice() { [native code] }
stream : function stream() { [native code] }
text : function text() { [native code] }

Blob – URLを生成するサンプル

次にURLを生成するサンプルです。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>

<script>

let blob = new Blob(["サンプルテキスト"], { type: "text/plain" });

let blobUrl = URL.createObjectURL(blob);

// 開発者ツールで確認
console.log("blobUrl -> " + blobUrl);

</script>
</body>
</html>

サーバ上のHTMLはこちら(test5_1.html)

画面にアクセスし、開発者ツールのconsole.loを確認すると以下の出力が確認できます。

blobUrl -> blob:https://propansystem.net/2a498274-bd25-47ef-b798-bdc6aa0c4d0d

URLに「blob:」を含む点が注意です。

Blob – 生成したURLをもとに、blogをダウンロードするサンプル

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>

<script>

let blob = new Blob(["サンプルテキスト"], { type: "text/plain" });

let blobUrl = URL.createObjectURL(blob);

let bloblink = document.createElement("a");

bloblink.href = blobUrl;

bloblink.download = "sample.txt";

bloblink.click();

</script>
</body>
</html>

サーバ上のHTMLはこちら(test5_2.html)

画面にアクセスすると、sample.txtだダウンロードされます。
txtファイルをダウンロードし、内容を確認すると「サンプルテキスト」の文字列が出力されていることが確認できます。

FilesystemAPI

FilesystemAPIはブラウザからローカルのファイルにアクセスすることができるAPIです。
ただし、全てのブラウザ、全てバージョンで動作するわけではないので注意が必要です。
以下のサンプルはchromeブラウザで動作するものです。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>

<div>
	<button id="openFile">ファイルを開く</button>
	<button id="saveFile">ファイルを保存</button>
</div>

<textarea id="textArea" cols="30" rows="10" placeholder="ファイル内容"></textarea>

<script>

const openFileButton = document.getElementById("openFile");
const saveFileButton = document.getElementById("saveFile");
const textArea = document.getElementById("textArea");

let fileHandle;

// ファイルを開く
openFileButton.addEventListener("click", async () => {
    try {
        // ユーザーにファイル選択ダイアログを表示
        [fileHandle] = await window.showOpenFilePicker({
            types: [
                {
                    description: "テキストファイル",
                    accept: { "text/plain": [".txt"] },
                },
            ],
        });

        // ファイルを読み取る
        const file = await fileHandle.getFile();
        const contents = await file.text();
        textArea.value = contents;
    } catch (error) {
        console.error("ファイルを開けませんでした:", error);
    }
});

// ファイルを保存する
saveFileButton.addEventListener("click", async () => {
    try {
        if (!fileHandle) {
            // 新しいファイルの保存
            fileHandle = await window.showSaveFilePicker({
                suggestedName: "newFile.txt",
                types: [
                    {
                        description: "テキストファイル",
                        accept: { "text/plain": [".txt"] },
                    },
                ],
            });
        }

        // ファイルに書き込む
        const writable = await fileHandle.createWritable();
        await writable.write(textArea.value);
        await writable.close();
        alert("ファイルを保存しました!");
    } catch (error) {
        console.error("ファイルを保存できませんでした:", error);
    }
});

</script>
</body>
</html>

サーバ上のHTMLはこちら(test6.html)

画面にアクセスし、「ファイル内容」に欄に任意の文字列を入力後、「ファイルを保存」ボタンを押下すると
ローカル環境への保存確認ダイアログが表示されます。
保存時に任意のファイル名として保存すると、ローカル環境に任意のファイル名として保存されます。
また、ファイルの内容はブラウザで記載した内容になっていることが確認できます。

「ファイルを開く」ボタンを押下すると、ローカル環境のファイルを開く操作をし、
ファイル内容を下のテキストエリアに展開します。
さらに展開したテキストエリアに対して内容を編集して「ファイルを保存」ボタンを押下して変更した内容を保存することができます。

Indexed Database API

Indexed Database APIはブラウザからデータベースを使用するAPIです。
以下の特徴があります。

ブラウザ内データベース:
クライアント側で構造化データを保存できるAPIです。

NoSQL型:
キーと値のペアでデータを保存し、テーブルではなくオブジェクトストアを使用します。

非同期処理:
操作は非同期的に行われ、PromiseやEventで結果を管理します。

キーの柔軟性:
主キーやインデックスを設定し、効率的なデータ検索が可能です。

永続性:
ブラウザが閉じられてもデータが保持されます。
容量が大きい: localStorage(約5MB)よりも多くのデータを保存できます(数百MB~GB)。

また、メリットは以下になります。

大容量データの保存:
ローカルストレージよりもはるかに多くのデータを保存できます。

高速なデータアクセス:
主キーやインデックスを活用することで効率的にデータを検索できます。

構造化データのサポート:
JSONのような複雑なデータ構造をそのまま保存可能です。

非同期処理対応:
メインスレッドをブロックせず、アプリの応答性を維持できます。
オフラインサポート: サーバーに依存せずローカルにデータを保存できるため、
オフラインアプリの実装に適しています。

デメリットは以下になります。

APIの複雑さ:
他のストレージAPI(例: localStorage)と比べて、初期設定や操作が複雑です。

ブラウザ依存性:
一部の古いブラウザや軽量ブラウザでは対応していない場合があります。

容量制限:
ブラウザごとに保存可能なデータ容量が異なります。

デバッグの難しさ:
開発ツールでのデバッグがやや煩雑で、テストがしにくいことがあります。

非同期処理の学習コスト:
イベント駆動のモデルに慣れていないと、扱いが難しい場合があります。

以下、簡単なサンプルを用意しました。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>

<div>
    <button id="addData"   >データを追加</button>
    <button id="readData"  >データを取得</button>
    <button id="deleteData">データを削除</button>
</div>
<div id="output"></div>

<script>
// IndexedDBの設定
let dbName    = "TestDB";
let storeName = "Users";
let db;

// データベースを開く
let openRequest = indexedDB.open(dbName, 1);

// データベース初期化
openRequest.onupgradeneeded = (event) => {
    db = event.target.result;

    // オブジェクトストアが存在しなければ作成
    if (!db.objectStoreNames.contains(storeName)) {
        db.createObjectStore(storeName, { keyPath: "id" });
    }
};

// データベース接続成功
openRequest.onsuccess = (event) => {
    db = event.target.result;
    console.log("データベース接続成功");
};

// エラー処理
openRequest.onerror = (event) => {
    console.error("データベース接続エラー:", event.target.error);
};

// データを追加
document.getElementById("addData").addEventListener("click", () => {
    let transaction = db.transaction(storeName, "readwrite");
    let store       = transaction.objectStore(storeName);
    let userData = { id: 1, name: "テスト名前001", age: 50 };
    let request = store.add(userData);

    request.onsuccess = () => {
        console.log("データ追加成功:", userData);
        document.getElementById("output").textContent = "データを追加しました。";
    };

    request.onerror = (event) => {
        console.error("データ追加エラー:", event.target.error);
    };
});

// データを取得
document.getElementById("readData").addEventListener("click", () => {
    let transaction = db.transaction(storeName, "readonly");
    let store       = transaction.objectStore(storeName);

    let request = store.get(1); // idが1のデータを取得

    request.onsuccess = () => {
        console.log("データ取得成功:", request.result);
        document.getElementById("output").textContent =
            "取得したデータ: " + JSON.stringify(request.result);
    };

    request.onerror = (event) => {
        console.error("データ取得エラー:", event.target.error);
    };
});

// データを削除
document.getElementById("deleteData").addEventListener("click", () => {
    let transaction = db.transaction(storeName, "readwrite");
    let store       = transaction.objectStore(storeName);

    let request = store.delete(1); // idが1のデータを削除

    request.onsuccess = () => {
        console.log("データ削除成功");
        document.getElementById("output").textContent = "データを削除しました。";
    };

    request.onerror = (event) => {
        console.error("データ削除エラー:", event.target.error);
    };
});

</script>
</body>
</html>

サーバ上のHTMLはこちら(test7.html)

画面にアクセスし「データ追加」ボタンを押下すると、予め決めておいたテストデータがIndexed Databaseに登録されます。
また、同じボタンを押下しても、1レコードだけになります。(IDが同じ為)

次に「データを取得」ボタンを押下すると、DBに登録した値を取得し画面に出力します。
同様に「データを削除」ボタンを押下すると、登録した値が削除されます。

canvas – 色

Posted コメントするカテゴリー: javascript

canvas – 色

図形の色について試します。

パスの内側を塗りつぶす

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    // canvasのDOMを取得
    const canvas = document.getElementById('canvas');

    // getContextメソッドでコンテキストを取得
    // ここでは2次元の平面的な図形を扱うので、引数に「2d」を指定
    const ctx = canvas.getContext('2d');

    // コンテキストのstyleを定義
    // パスの開始
    ctx.beginPath();

    // 長方形
    ctx.moveTo(10, 10);
    ctx.rect(30, 30, 100, 60);

    // 色を指定する
    ctx.fillStyle   = "#339999";

    // パスを色と線を描画する
    ctx.fill();
</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

画面にアクセスすると長方形のパスにそって内側に色が描画されます。

パスを塗りつぶす

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    // canvasのDOMを取得
    const canvas = document.getElementById('canvas');

    // getContextメソッドでコンテキストを取得
    // ここでは2次元の平面的な図形を扱うので、引数に「2d」を指定
    const ctx = canvas.getContext('2d');

    // コンテキストのstyleを定義
    // パスの開始
    ctx.beginPath();

    // 長方形
    ctx.moveTo(10, 10);
    ctx.rect(30, 30, 100, 60);

    // 色を指定する
    ctx.strokeStyle = "#000000";

    // パスを色と線を描画する
    ctx.stroke();
</script>
</body>
</html>

サーバ上のHTMLはこちら(test2.html)

画面にアクセスすると長方形のパスに色が描画されます。

パスとパスの内側を塗りつぶす

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    // canvasのDOMを取得
    const canvas = document.getElementById('canvas');

    // getContextメソッドでコンテキストを取得
    // ここでは2次元の平面的な図形を扱うので、引数に「2d」を指定
    const ctx = canvas.getContext('2d');

    // コンテキストのstyleを定義
    // パスの開始
    ctx.beginPath();

    // 長方形
    ctx.moveTo(10, 10);
    ctx.rect(30, 30, 100, 60);

    // 色を指定する
    ctx.fillStyle   = "#339999";
    ctx.strokeStyle = "#000000";

    // パスを色と線を描画する
    ctx.fill();
    ctx.stroke();
</script>
</body>
</html>

サーバ上のHTMLはこちら(test3.html)

画面にアクセスすると長方形のパスの内側とパスそのものに色が描画されます。

パスとパスの内側を塗りつぶす(透明度の設定)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    // canvasのDOMを取得
    const canvas = document.getElementById('canvas');

    // getContextメソッドでコンテキストを取得
    // ここでは2次元の平面的な図形を扱うので、引数に「2d」を指定
    const ctx = canvas.getContext('2d');

    // コンテキストのstyleを定義
    // パスの開始
    ctx.beginPath();

    // 長方形
    ctx.moveTo(10, 10);
    ctx.rect(30, 30, 100, 60);

    // 色を指定する
    ctx.fillStyle   = 'rgba(255, 0,   0, 0.3)';
    ctx.strokeStyle = 'rgba(0  , 0, 255, 0.3)';

    // パスを色と線を描画する
    ctx.fill();
    ctx.stroke();
</script>
</body>
</html>

サーバ上のHTMLはこちら(test4.html)

画面にアクセスすると長方形のパスの内側とパスそのものに色が描画されます。
透明度の指定は、RGBと透明度の組み合わせで指定しています。

rgba(R, G, B, A)

サンプルの場合は、パスもパスの内側も0.2の透明度なので、描画された図形は薄く見えます。

canvas – 2つの図形を重ねた際の描画について

Posted コメントするカテゴリー: javascript

canvas – 2つの図形を重ねた際の描画について

2つの図形を重ねた場合の描画を試します。
図形を重ねた場合にcanvasで「ノンゼロワインディング規則」というルールが適用されます。

例えば図形1と図形2がパスで設定される時、図形1は時計回りに描画
図形2は反時計周りに描画をすると、内側の領域は塗りつぶされない仕様になります。

以下に簡単なサンプルを用意しました。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    // canvasのDOMを取得
    const canvas = document.getElementById('canvas');
    
    // getContextメソッドでコンテキストを取得
    // ここでは2次元の平面的な図形を扱うので、引数に「2d」を指定
    const ctx = canvas.getContext('2d');
    
    // コンテキストのstyleを定義
    // パスの開始
    ctx.beginPath();

    // 図形1を時計周りで指定
    ctx.moveTo(10, 10);
    ctx.lineTo(90, 10);
    ctx.lineTo(90, 50);
    ctx.lineTo(10, 50);

    // 図形2を反時計回で指定
    ctx.moveTo(15, 15);
    ctx.lineTo(15, 45);
    ctx.lineTo(85, 45);
    ctx.lineTo(85, 15);

    // パスを塗りつぶす
    ctx.fill();
    
</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

画面にアクセスすると長方形の線状の図形が描画されます。
これは図形1が外側の少し大きい長方形を描画し、図形2が少し小さい内側の長方形を描画する際、
内側の長方形の描画を反時計周りの順でパスを設定することにより、外側の長方形の塗りつぶしの領域が空白になった為です。

では、実験的に内側の図形2を時計回りに描画するとどうなるか試してみます。
以下に簡単なサンプルを用意しました。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    // canvasのDOMを取得
    const canvas = document.getElementById('canvas');
    
    // getContextメソッドでコンテキストを取得
    // ここでは2次元の平面的な図形を扱うので、引数に「2d」を指定
    const ctx = canvas.getContext('2d');
    
    // コンテキストのstyleを定義
    // パスの開始
    ctx.beginPath();

    // 図形1を時計周りで指定
    ctx.moveTo(10, 10);
    ctx.lineTo(90, 10);
    ctx.lineTo(90, 50);
    ctx.lineTo(10, 50);

    // 図形2を反時計回で指定
    ctx.moveTo(15, 15);
    ctx.lineTo(85, 15);
    ctx.lineTo(85, 45);
    ctx.lineTo(15, 45);

    // パスを塗りつぶす
    ctx.fill();
    
</script>
</body>
</html>

サーバ上のHTMLはこちら(test2.html)

画面にアクセスすると、今度は内側の長方形が時計回りの順でパスが設定されているため、
外側の長方形の塗りつぶしが、空白にならないことがわかります。

canvas – 3次ベジェ曲線

Posted コメントするカテゴリー: javascript

canvas – 3次ベジェ曲線

3次ベジェ曲線を試します。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    // canvasのDOMを取得
    const canvas = document.getElementById('canvas');
    
    // getContextメソッドでコンテキストを取得
    // ここでは2次元の平面的な図形を扱うので、引数に「2d」を指定
    const ctx = canvas.getContext('2d');
    
    // コンテキストのstyleを定義
    // パスの開始
    ctx.beginPath();

    // 3次曲線の例
    ctx.moveTo(10, 10);
    ctx.bezierCurveTo(30, 30, 30, 30, 50, 50);
    ctx.bezierCurveTo(70, 70, 50, 45, 20, 60);

    // パスを線で描画する
    ctx.stroke();
</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

画面にアクセスすると3次ベジェ曲線が描画されます。

canvas – 長方形

Posted コメントするカテゴリー: javascript

canvas – 長方形

長方形を試します。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    // canvasのDOMを取得
    const canvas = document.getElementById('canvas');
    
    // getContextメソッドでコンテキストを取得
    // ここでは2次元の平面的な図形を扱うので、引数に「2d」を指定
    const ctx = canvas.getContext('2d');
    
    // コンテキストのstyleを定義
    // パスの開始
    ctx.beginPath();

    // 長方形
    ctx.moveTo(10, 10);
    ctx.rect(30, 30, 100, 60);

    // パスを線で描画する
    ctx.stroke();
</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

画面にアクセスすると長方形が描画されます。

canvas – 円弧

Posted コメントするカテゴリー: javascript

canvas – 円弧

円弧を試します。

コンテキストに対してのメソッドはarcを使います。

arc(x, y, radius, startAngle, endAngle, counterclockwise)

以下、簡単なサンプルです。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    // canvasのDOMを取得
    const canvas = document.getElementById('canvas');
    
    // getContextメソッドでコンテキストを取得
    // ここでは2次元の平面的な図形を扱うので、引数に「2d」を指定
    const ctx = canvas.getContext('2d');
    
    // コンテキストのstyleを定義
    // パスの開始
    ctx.beginPath();

    // 図形の描画
    ctx.arc(50, 50, 40, 0, -1, true);

    // パスを線で描画する
    ctx.stroke();
</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

画面にアクセスすると、1ラジアンの円弧の線が描画されます。

この時、arcメソッドの引数に注目すると、

arc(x, y, radius, startAngle, endAngle, counterclockwise)

各引数の定義は以下の意味になります。

x : 円の中心点x座標
y : 円の中心点y座標
radius : 半径
startAngle : 開始角度(ラジアン)
endAngle : 終了角度(ラジアン)
counterclockwise : 描画する方向(時計周り:false か 反時計周り:true)

サンプルでは、

ctx.arc(50, 50, 40, 0, -1, true);

という記述なので、半径40の円弧を画面上の(50,50)の位置から、
描画する線は、0ラジアンから開始し、-1ラジアンまで、反時計回りで描画する。
という動きになります。

ラジアンについて

ラジアンは数学の分野で頻繁に使われます。
日常では度数(0~360)の数値のほうが馴染みがあります。
ラジアンの定義は、円の半径の長さを孤としたときの、円の中心角度が1ラジアン。とされています。
度数でいうと、1ラジアンは、57.29578度に相当します。

canvas – 直線

Posted コメントするカテゴリー: javascript

canvas – 直線

直線を試します。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    // canvasのDOMを取得
    const canvas = document.getElementById('canvas');
    
    // getContextメソッドでコンテキストを取得
    // ここでは2次元の平面的な図形を扱うので、引数に「2d」を指定
    const ctx = canvas.getContext('2d');
    
    // コンテキストのstyleを定義
    // パスの開始
    ctx.beginPath();

    // 図形1の描画
    ctx.moveTo(10, 10);
    ctx.lineTo(60, 60);

    // パスを線で描画する
    ctx.stroke();
</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

画面にアクセスすると、座標(10, 10)から(60, 60)へ直線が描画されます。

canvas – ペン(moveTo())の移動

Posted コメントするカテゴリー: javascript

canvas – ペン(moveTo())の移動

前回の投稿では、canvasで線を描画する際にパスをbeginPath()で開始し、
moveTo(100, 100)した後にlineTo(10, 10)とlineTo(20, 80)を行いました。

今回はパスを描画した後、moveTo(100, 100)を利用して、別な位置から新たなパスを描画します。
以下サンプルを用意しました。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    // canvasのDOMを取得
    const canvas = document.getElementById('canvas');
    
    // getContextメソッドでコンテキストを取得
    // ここでは2次元の平面的な図形を扱うので、引数に「2d」を指定
    const ctx = canvas.getContext('2d');
    
    // コンテキストのstyleを定義
    // パスの開始
    ctx.beginPath();

    // 図形1の描画
    ctx.moveTo(100, 100);
    ctx.lineTo(10 , 10 );
    ctx.lineTo(20 , 80 );

    // 図形2の描画
    ctx.moveTo(150, 150);
    ctx.lineTo(30 , 120);
    ctx.lineTo(50 , 80 );

    // パスを線で描画する
    ctx.stroke();
</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

画面にアクセスすると、図形1と図形2がそれぞれ表示され、別な箇所からパスが定義されて線が描画されています。

canvas – 線の描画

Posted コメントするカテゴリー: javascript

canvas – 線の描画

canvasで線を描画します。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    // canvasのDOMを取得
    const canvas = document.getElementById('canvas');
    
    // getContextメソッドでコンテキストを取得
    // ここでは2次元の平面的な図形を扱うので、引数に「2d」を指定
    const ctx = canvas.getContext('2d');
    
    // コンテキストのstyleを定義(背景色: グレー、大きさ: 150の正方形)
    ctx.beginPath();
    ctx.moveTo(100, 100);
    ctx.lineTo(10, 10);
    ctx.lineTo(20, 80);
    ctx.fill();
</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

canvas

Posted コメントするカテゴリー: javascript

canvas

canvasについて調べます。

canvasの特徴

・html5で標準化されている。
・特定のブラウザを除き広くサポートされている。
・メソッドを呼び出して画面(図形)を描画します。
・canvasタグ内で描画APIを呼び出して画面を描画します。

SVGとの違い

svgはxml構造のツリー構造で画面を構築します。
canvasはメソッドを呼び出すことで図形を描画します。
その為の描画APIも多数あります。

描画メソッドはcanvasタグでは定義されておらず、描画コンテキストに定義されています。
描画コンテキストはcanvasタグ内で実行するjavascriptでgetContext()メソッドにより呼び出すことができます。

単純なサンプル

以下に簡単なcanvasのサンプルを用意しました。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
    // canvasのDOMを取得
    const canvas = document.getElementById('canvas');
    
    // getContextメソッドでコンテキストを取得
    // ここでは2次元の平面的な図形を扱うので、引数に「2d」を指定
    const ctx = canvas.getContext('2d');
    
    // コンテキストのstyleを定義(背景色: グレー、大きさ: 150の正方形)
    ctx.fillStyle = '#cccccc';
    ctx.fillRect(0, 0, 150, 150);
</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

画面アクセスすると、150pxの正方形が表示されます。
以降、canvasでどんなことができるのか、試していきます。

SVGアニメーション – Web Animations API を使う – モーフィングアニメーションについて

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – モーフィングアニメーションについて

SVGを使用したモーフィングアニメーションを試します。
以下サンプルです。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
</head>
<body>
<svg width="300" height="250">
    <path 
        d            = "M 10,10 90,150 120,20 Z"
        stroke       = "#eeeeee"
        stroke-width = "1"
        fill         = "#dddddd"
    >
        <animate
            attributeName = "d"
            repeatCount   = "indefinite"
            dur           = "3s"
            to            = "M 130,40 150,180 160,40 Z"
        >
    </path>
</svg>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

画面にアクセスすると、SVG画像がモーフィングします。

考え方としては、

<path 
    d            = "M 10,10 90,150 120,20 Z"
    stroke       = "#eeeeee"
    stroke-width = "1"
    fill         = "#dddddd"
>

で指定したSVG画像に対し、

<animate
    attributeName = "d"
    repeatCount   = "indefinite"
    dur           = "3s"
    to            = "M 130,40 150,180 160,40 Z"
>

上記で指定した「to」のパス情報に3秒かけて、パスの変化をしている。
ということになります。

モーフィングする際のパス情報は、開始時と終了時で同じ数のパスにする必要があります。

attributeNameのパラメータにはサンプルではパスを示す「d」を指定していますが、
「d」以外の値(例えば色の塗りつぶし情報等)でも指定可能です。

SVGアニメーション – Web Animations API を使う – CSSアニメーション再生時のgetAnimations()の取得について

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – CSSアニメーション再生時のgetAnimations()の取得について

前回の投稿(https://propansystem.net/blog/10005)のサンプルをもとに、
CSSアニメーション再生時のgetAnimations()の取得について試してみます。

SVGでのアニメーション再生中のオブジェクトの情報をgetAnimations()で
取得できることがわかりました。

同様にcssアニメーション中のdivタグの情報をgetAnimations()で取得してみます。

cssのアニメーションは指定オブジェクトを左から右へ500px移動するシンプルなものにしました。

@keyframes cssAnimation {
    0% {
        transform: translateX(0px);
    }
    100% {
        transform: translateX(500px);
    }
}

このcssアニメーション中にgetAnimations()を実行し、取得した結果を画面に出力するサンプルを用意しました。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
<style>
.bottom {
    margin-bottom    : 10px;
}
#test1_dom {
    color            : #5f5d5d;
    background-color : #eeeeee;
    font-size        : 1.1rem;
    width            : 150px;
    animation        : cssAnimation 10s ease-out 0s forwards;
}
@keyframes cssAnimation {
    0% {
        transform        : translateX(0px);
    }
    100% {
        transform        : translateX(500px);
    }
}
</style>
</head>
<body>
<!-- ボタン -->
<div class="bottom">
    <input type="button" id="getAnimations" value="getAnimations情報の取得と出力">
</div>
<!-- アニメーション用DIV -->
<div id="test1_dom">TEST1</div>
<!-- getAnimationsの取得結果 -->
<div class="params" id="getAnimationsVal"></div>

<script>
// DOM要素取得
let test1_dom            = document.querySelector("#test1_dom"       );
let getAnimations_dom    = document.querySelector("#getAnimations"   );
let getAnimationsVal_dom = document.querySelector("#getAnimationsVal");

// getAnimationsの実行と画面出力
getAnimations_dom.addEventListener('click', (e) => {
    let animations = test1_dom.getAnimations();
    let animations_display_value = '';
    // 各アニメーションの詳細を表示
    animations.forEach((animation, index) => {
        animations_display_value += 'Animation    :' + index                                 + '<br />';
        animations_display_value += 'Play State   :' + animation.playState                   + '<br />';
        animations_display_value += 'Start Time   :' + animation.startTime                   + '<br />';
        animations_display_value += 'Current Time :' + animation.currentTime                 + '<br />';
        animations_display_value += 'Duration     :' + animation.effect.getTiming().duration + '<br />';
        animations_display_value += 'Animation ID :' + animation.id                          + '<br />';
    });
    // 画面内のDOMへ出力する
    getAnimationsVal_dom.innerHTML = animations_display_value;
}, false);
</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

画面にアクセスするとcssアニメーションが再生されます。
再生中に「getAnimations情報の取得と出力」ボタンを押下すると、
SVG再生中に取得表示した時と同様に、cssアニメーション再生中の情報も取得できることがわかります。

SVGアニメーション – Web Animations API を使う – アニメーション再生時のgetAnimations()について

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – アニメーション再生時のgetAnimations()について

アニメーション再生時に、再生しているdiv要素のメソッドとして「getAnimations()」が使えます。
このメソッドは再生中のアニメーションのAnimationのオブジェクト(の配列)として取得することができます。
また、アニメーションを停止してidleになっている要素では取得できません。
css定義されたアニメーションも取得できます。

以下に簡単なサンプルを用意しました。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
<style>
.bottom {
    margin-bottom    : 10px;
}
#test1_dom {
    color            : #5f5d5d;
    background-color : #eeeeee;
    font-size        : 1.1rem;
    width            : 150px;
}
</style>
</head>
<body>
<!-- ボタン -->
<div class="bottom">
    <input type="button" id="start_animate"   value="アニメーション開始"    >
    <input type="button" id="pause_animate"   value="アニメーション一時停止">
    <input type="button" id="reverse_animate" value="アニメーション逆再生"  >
    <input type="button" id="finish_animate"  value="アニメーション終了"    >
    <input type="button" id="cancel_animate"  value="アニメーション停止"    >
</div>
<!-- ボタン -->
<div class="bottom">
    <input type="button" id="getAnimations"   value="getAnimationsの実行"   >
</div>
<!-- アニメーション用DIV -->
<div id="test1_dom">TEST1 (<span id="display"></span>)</div>
<!-- 再生秒数 -->
<div class="params" id="currentTime_value"></div>
<!-- getAnimationsの取得結果 -->
<div class="params" id="getAnimationsVal"></div>
<script>
// DOM要素取得
let start_animate_dom    = document.querySelector("#start_animate"   );
let pause_animate_dom    = document.querySelector("#pause_animate"   );
let reverse_animate_dom  = document.querySelector("#reverse_animate" );
let finish_animate_dom   = document.querySelector("#finish_animate"  );
let cancel_animate_dom   = document.querySelector("#cancel_animate"  );
let display_dom          = document.querySelector("#display"         );
let test1_dom            = document.querySelector("#test1_dom"       );
let getAnimations_dom    = document.querySelector("#getAnimations"   );
let getAnimationsVal_dom = document.querySelector("#getAnimationsVal");

// アニメーション用オブジェクト
let animation1;
// keyframes定義
const keyframes1 = [
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(200px)'},
];
// option定義
const options1 = {
    duration: 5000
};
animation1 = test1_dom.animate(keyframes1, options1);

animation1.cancel(); // 初期値は停止

// アニメーション開始
start_animate_dom.addEventListener('click', (e) => {
    animation1.play();
    display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション一時停止
pause_animate_dom.addEventListener('click', (e) => {
    animation1.pause();
    display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション逆再生
reverse_animate_dom.addEventListener('click', (e) => {
    animation1.reverse();
    display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション終了
finish_animate_dom.addEventListener('click', (e) => {
    animation1.finish();
    display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション停止
cancel_animate_dom.addEventListener('click', (e) => {
    animation1.cancel();
    display_dom.innerHTML = animation1.playState;
}, false);

// getAnimationsの実行と画面出力
getAnimations_dom.addEventListener('click', (e) => {
	let animations = test1_dom.getAnimations();
	let animations_display_value = '';
    // 各アニメーションの詳細を表示
    animations.forEach((animation, index) => {
        animations_display_value += 'Animation    :' + index                                 + '<br />';
        animations_display_value += 'Play State   :' + animation.playState                   + '<br />';
        animations_display_value += 'Start Time   :' + animation.startTime                   + '<br />';
        animations_display_value += 'Current Time :' + animation.currentTime                 + '<br />';
        animations_display_value += 'Duration     :' + animation.effect.getTiming().duration + '<br />';
        animations_display_value += 'Animation ID :' + animation.id                          + '<br />';
    });
    // 画面内のDOMへ出力する
    getAnimationsVal_dom.innerHTML = animations_display_value;
}, false);

// 再生秒数を定期的に更新する
setInterval(() => {
    // アニメーションが再生中の場合に秒数を更新
    if (animation1.playState === 'running') {
        // currentTimeはミリ秒で返されるので、秒に変換して表示
        let disp_sec = (animation1.currentTime / 1000).toFixed(2) + '秒';
        document.getElementById('currentTime_value').innerHTML = disp_sec;
    }
}, 10); // 0.01秒ごとにチェック
</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

画面アクセスして「アニメーション開始」ボタンを押下し、
「getAnimations情報の取得と出力」ボタンを押下すると、アニメーション再生中は1つの(再生中の)オブジェクトの情報が取得できます。

ボタン押下時のイベントには、getAnimations情報の詳細を取得し、画面内のDOMに出力する処理を書いています。

// getAnimationsの実行と画面出力
getAnimations_dom.addEventListener('click', (e) => {
	let animations = test1_dom.getAnimations();
	let animations_display_value = '';
    // 各アニメーションの詳細を表示
    animations.forEach((animation, index) => {
        animations_display_value += 'Animation    :' + index                                 + '<br />';
        animations_display_value += 'Play State   :' + animation.playState                   + '<br />';
        animations_display_value += 'Start Time   :' + animation.startTime                   + '<br />';
        animations_display_value += 'Current Time :' + animation.currentTime                 + '<br />';
        animations_display_value += 'Duration     :' + animation.effect.getTiming().duration + '<br />';
        animations_display_value += 'Animation ID :' + animation.id                          + '<br />';
    });
    // 画面内のDOMへ出力する
    getAnimationsVal_dom.innerHTML = animations_display_value;
}, false);

アニメーションの再生中に「getAnimations情報」を何度か押下すると、
再生中の都度の情報が取得できていることがわかります。

また、アニメーションの再生が終わると、getAnimationsの情報は取得できなくなり、
画面出力も空白になります。

SVGアニメーション – Web Animations API を使う – アニメーションのPromiseについて

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – アニメーションのPromiseについて

Animationオブジェクトには以下のプロパティが定義されています。
①Animation.ready
②Animation.finished

これはPromiseの仕組みで動作します。
PromiseはECMAScript 2015から導入された概念で、javascriptで利用できますが、言語として組み込まれたのは他の言語です。

Promiseは非同期処理を抽象化したオブジェクトと、オブジェクトを操作する仕組みです。
ここではPromiseの詳解はしないですが、このPromiseの概念はjavascriptを書き進めるにあたり避けて通れない概念です。

SVGアニメーションでPromiseを利用する例として、上記の「ready」と「finished」を軸に進めます。

Promiseのおもな動き

Promiseには非同期処理をおもに取り扱います。
(N件の)非同期処理の結果が解決(resolve)された際、コールバック関数をthenメソッドを使って設定することができます。
また、解決ではなく非同期処理が失敗(reject)された際にも処理(コールバック関数)が呼ばれます。

ちなみに、N件の処理が1番早く「成功or失敗」が決着した結果を採用し、処理を続行するには Promise.race() と書きます。

Animation.readyの動き

Animation.readyはアニメーションの再生準備が完了した際にthenのコールバック関数を実行します。
以下、簡単なサンプルです。
再生準備が完了したかどうかをconsole.logに出力しています。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
<style>
#bottom {
    margin-bottom    : 10px;
}
#test1_dom {
    color            : #5f5d5d;
    background-color : #eeeeee;
    font-size        : 1.1rem;
    width            : 150px;
}
</style>
</head>
<body>
<!-- ボタン -->
<div id="bottom">
    <input type="button" id="start_animate"   value="アニメーション開始"    >
    <input type="button" id="pause_animate"   value="アニメーション一時停止">
    <input type="button" id="reverse_animate" value="アニメーション逆再生"  >
    <input type="button" id="finish_animate"  value="アニメーション終了"    >
    <input type="button" id="cancel_animate"  value="アニメーション停止"    >
</div>
<!-- アニメーション用DIV -->
<div id="test1_dom">TEST1 (<span id="display"></span>)</div>
<!-- 再生秒数 -->
<div class="params" id="currentTime_value"></div>
<script>
// DOM要素取得
let start_animate_dom   = document.querySelector("#start_animate"  );
let pause_animate_dom   = document.querySelector("#pause_animate"  );
let reverse_animate_dom = document.querySelector("#reverse_animate");
let finish_animate_dom  = document.querySelector("#finish_animate" );
let cancel_animate_dom  = document.querySelector("#cancel_animate" );
let display_dom         = document.querySelector("#display"        );
let test1_dom           = document.querySelector("#test1_dom"      );
// アニメーション用オブジェクト
let animation1;
// keyframes定義
const keyframes1 = [
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(200px)'},
];
// option定義
const options1 = {
    duration: 5000
};
animation1 = test1_dom.animate(keyframes1, options1);

animation1.cancel(); // 初期値は停止

// Promiseの確認
// 再生準備ができているかを確認する
animation1.ready.then(function() {
    console.log("再生準備完了");
});

// アニメーション開始
start_animate_dom.addEventListener('click', (e) => {
    animation1.play();
    display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション一時停止
pause_animate_dom.addEventListener('click', (e) => {
    animation1.pause();
    display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション逆再生
reverse_animate_dom.addEventListener('click', (e) => {
    animation1.reverse();
    display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション終了
finish_animate_dom.addEventListener('click', (e) => {
    animation1.finish();
    display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション停止
cancel_animate_dom.addEventListener('click', (e) => {
    animation1.cancel();
    display_dom.innerHTML = animation1.playState;
}, false);


// 再生秒数を定期的に更新する
setInterval(() => {
    // アニメーションが再生中の場合に秒数を更新
    if (animation1.playState === 'running') {
        // currentTimeはミリ秒で返されるので、秒に変換して表示
        let disp_sec = (animation1.currentTime / 1000).toFixed(2) + '秒';
        document.getElementById('currentTime_value').innerHTML = disp_sec;
    }
}, 10); // 0.01秒ごとにチェック
</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

Animation.finishedの動き

上記と同様に「再生完了しているか」を確認するサンプルです。
再生完了をPromiseを使って「animation1.finished.then」という形で確認しています。
非同期的に動作するので、再生秒数にかか関わらず動作します。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
<style>
#bottom {
    margin-bottom    : 10px;
}
#test1_dom {
    color            : #5f5d5d;
    background-color : #eeeeee;
    font-size        : 1.1rem;
    width            : 150px;
}
</style>
</head>
<body>
<!-- ボタン -->
<div id="bottom">
    <input type="button" id="start_animate"   value="アニメーション開始"    >
    <input type="button" id="pause_animate"   value="アニメーション一時停止">
    <input type="button" id="reverse_animate" value="アニメーション逆再生"  >
    <input type="button" id="finish_animate"  value="アニメーション終了"    >
    <input type="button" id="cancel_animate"  value="アニメーション停止"    >
</div>
<!-- アニメーション用DIV -->
<div id="test1_dom">TEST1 (<span id="display"></span>)</div>
<!-- 再生秒数 -->
<div class="params" id="currentTime_value"></div>
<script>
// DOM要素取得
let start_animate_dom   = document.querySelector("#start_animate"  );
let pause_animate_dom   = document.querySelector("#pause_animate"  );
let reverse_animate_dom = document.querySelector("#reverse_animate");
let finish_animate_dom  = document.querySelector("#finish_animate" );
let cancel_animate_dom  = document.querySelector("#cancel_animate" );
let display_dom         = document.querySelector("#display"        );
let test1_dom           = document.querySelector("#test1_dom"      );
// アニメーション用オブジェクト
let animation1;
// keyframes定義
const keyframes1 = [
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(200px)'},
];
// option定義
const options1 = {
    duration: 5000
};
animation1 = test1_dom.animate(keyframes1, options1);

animation1.cancel(); // 初期値は停止

// Promiseの確認
// 再生準備ができているかを確認する
animation1.ready.then(function() {
    console.log("再生準備完了");
});

// 再生完了しているかを確認する
animation1.finished.then(function() {
    console.log("再生完了");
});

// アニメーション開始
start_animate_dom.addEventListener('click', (e) => {
    animation1.play();
    display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション一時停止
pause_animate_dom.addEventListener('click', (e) => {
    animation1.pause();
    display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション逆再生
reverse_animate_dom.addEventListener('click', (e) => {
    animation1.reverse();
    display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション終了
finish_animate_dom.addEventListener('click', (e) => {
    animation1.finish();
    display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション停止
cancel_animate_dom.addEventListener('click', (e) => {
    animation1.cancel();
    display_dom.innerHTML = animation1.playState;
}, false);


// 再生秒数を定期的に更新する
setInterval(() => {
    // アニメーションが再生中の場合に秒数を更新
    if (animation1.playState === 'running') {
        // currentTimeはミリ秒で返されるので、秒に変換して表示
        let disp_sec = (animation1.currentTime / 1000).toFixed(2) + '秒';
        document.getElementById('currentTime_value').innerHTML = disp_sec;
    }
}, 10); // 0.01秒ごとにチェック
</script>
</body>
</html>

サーバ上のHTMLはこちら(test2.html)

2つのAnimationオブジェクトに対し、Animation.finishedを参照してPromise.allで終了タイミングを取得する

アニメーション1とアニメーション2を用意し、1と2をそれぞれ再生して「どちらも終了したタイミング」を
Promise.allを用いて取得(確認)します。

便宜上、このサンプルの場合のアニメーション1とアニメーション2は途中で異常終了しなものとしています。

Promise.allを使う場合は、引数の中にアニメーション1とアニメーション2のfinishedを格納したPromiseを使用しています。

アニメーション1の「anifini1」を取得

// 再生完了しているかを確認する
let anifini1 = animation1.finished.then(function() {
    console.log("アニーメション1 再生完了");
});

アニメーション2の「anifini2」を取得

// 再生完了しているかを確認する
let anifini2 = animation2.finished.then(function() {
    console.log("アニーメション2 再生完了");
});

取得した「anifini1」と「anifini2」をPromise.allの引数として使用し、
アニメーション1とアニメーション2の両方が完了したタイミングで、アラート(とconsoleログ)を出力

// Promise allの確認
Promise.all([anifini1, anifini2]).then(() => {
	console.log('Promise.all then');
	alert('Promise.all then');
});

サンプルの全体は以下のように書きました。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
<style>
#bottom {
    margin-bottom    : 10px;
}
#test1_dom, #test2_dom {
    color            : #5f5d5d;
    background-color : #eeeeee;
    font-size        : 1.1rem;
    width            : 150px;
}
</style>
</head>
<body>
<!-- ボタン -->
<div id="bottom">
    <input type="button" id="start_animate"   value="アニメーション開始"    >
    <input type="button" id="pause_animate"   value="アニメーション一時停止">
    <input type="button" id="reverse_animate" value="アニメーション逆再生"  >
    <input type="button" id="finish_animate"  value="アニメーション終了"    >
    <input type="button" id="cancel_animate"  value="アニメーション停止"    >
</div>
<!-- アニメーション用DIV -->
<div id="test1_dom">TEST1</div>
<div id="test2_dom">TEST2</div>
<!-- 再生秒数 -->
<div class="params" id="currentTime_value1"></div>
<div class="params" id="currentTime_value2"></div>
<script>
//-----------------------------
// DOM要素取得
let start_animate_dom   = document.querySelector("#start_animate"  );
let pause_animate_dom   = document.querySelector("#pause_animate"  );
let reverse_animate_dom = document.querySelector("#reverse_animate");
let finish_animate_dom  = document.querySelector("#finish_animate" );
let cancel_animate_dom  = document.querySelector("#cancel_animate" );
let test1_dom           = document.querySelector("#test1_dom"      );
let test2_dom           = document.querySelector("#test2_dom"      );

//-----------------------------
// アニメーション1用オブジェクト
let animation1;
// keyframes定義
const keyframes1 = [
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(200px)'},
];
// option定義
const options1 = {
    duration: 5000
};
animation1 = test1_dom.animate(keyframes1, options1);

//-----------------------------
// アニメーション2用オブジェクト
//-----------------------------
let animation2;
// keyframes定義
const keyframes2 = [
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(200px)'},
];
// option定義
const options2 = {
    duration: 10000
};
animation2 = test2_dom.animate(keyframes2, options2);

//-----------------------------
animation1.cancel(); // 初期値は停止
animation2.cancel(); // 初期値は停止

//-----------------------------
// Promiseの確認
//-----------------------------
// 再生準備ができているかを確認する
animation1.ready.then(function() {
    console.log("アニーメション1 再生準備完了");
});

// 再生完了しているかを確認する
let anifini1 = animation1.finished.then(function() {
    console.log("アニーメション1 再生完了");
});

//-----------------------------
// Promiseの確認
//-----------------------------
// 再生準備ができているかを確認する
animation2.ready.then(function() {
    console.log("アニーメション2 再生準備完了");
});

// 再生完了しているかを確認する
let anifini2 = animation2.finished.then(function() {
    console.log("アニーメション2 再生完了");
});

// Promise allの確認
Promise.all([anifini1, anifini2]).then(() => {
	console.log('Promise.all then');
	alert('Promise.all then');
});

//-----------------------------
// アニメーション操作
//-----------------------------
start_animate_dom.addEventListener('click', (e) => {
    animation1.play();
    animation2.play();
}, false);
// アニメーション一時停止
pause_animate_dom.addEventListener('click', (e) => {
    animation1.pause();
    animation2.pause();
    display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション逆再生
reverse_animate_dom.addEventListener('click', (e) => {
    animation1.reverse();
    animation2.reverse();
    display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション終了
finish_animate_dom.addEventListener('click', (e) => {
    animation1.finish();
    animation2.finish();
    display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション停止
cancel_animate_dom.addEventListener('click', (e) => {
    animation1.cancel();
    animation2.cancel();
    display_dom.innerHTML = animation1.playState;
}, false);


// 再生秒数を定期的に更新する
setInterval(() => {
    // アニメーション1が再生中の場合に秒数を更新
    if (animation1.playState === 'running') {
        // currentTimeはミリ秒で返されるので、秒に変換して表示
        let disp_sec = (animation1.currentTime / 1000).toFixed(2) + '秒';
        document.getElementById('currentTime_value1').innerHTML = disp_sec;
    }

    // アニメーション2が再生中の場合に秒数を更新
    if (animation2.playState === 'running') {
        // currentTimeはミリ秒で返されるので、秒に変換して表示
        let disp_sec2 = (animation2.currentTime / 1000).toFixed(2) + '秒';
        document.getElementById('currentTime_value2').innerHTML = disp_sec2;
    }
}, 10); // 0.01秒ごとにチェック
</script>
</body>
</html>

サーバ上のHTMLはこちら(test3.html)

画面アクセスして「アニメーション開始」ボタンを押下すると、
アニメーション1とアニメーション2がそれぞれ実行されます。

アニメーション1とアニメーション2は再生秒数が異なるので、
アニメーションが終了するタイミングは別々になります。

どちらのアニメーションも終了したタイミングでPromise.allが動き、アラート表示されることが確認できます。

SVGアニメーション – Web Animations API を使う – アニメーションのfinish、cancel、removeイベントについて

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – アニメーションのfinish、cancel、removeイベントについて

前回までの投稿でanimationのplayStateプロパティでアニメーション中の状態(idle、running、paused、finished、pending)を取得することができました。

// アニメーションが再生中の場合に秒数を更新
if (animation1.playState === 'running') {

ここではアニメーションが終了、停止、削除された時のイベントについて、試してみます。

finish()イベント

アニメーションが終了した時に呼ばれるイベント

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
<style>
#bottom {
    margin-bottom    : 10px;
}
#test1_dom {
    color            : #5f5d5d;
    background-color : #cccccc;
    font-size        : 1.1rem;
    width            : 150px;
}
</style>
</head>
<body>
<!-- ボタン -->
<div id="bottom">
    <input type="button" id="start_animate"  value="アニメーション開始">
</div>
<!-- アニメーション用DIV -->
<div id="test1_dom">TEST1 (<span id="display"></span>)</div>
<!-- 再生秒数 -->
<div class="params" id="currentTime_value"></div>
<script>
// DOM要素取得
let start_animate_dom  = document.querySelector("#start_animate" );
let cancel_animate_dom = document.querySelector("#cancel_animate");
let display_dom        = document.querySelector("#display"       );
let test1_dom          = document.querySelector("#test1_dom"     );

// アニメーション用定義
let animation1;
// keyframes定義
const keyframes1 = [
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(500px)'},
];
// option定義
const options1 = {
    duration   : 3000
};
animation1 = test1_dom.animate(keyframes1, options1);
animation1.cancel(); // 初期値は停止

// アニメーション開始
start_animate_dom.addEventListener('click', (e) => {
	// 開始ボタン押下でスライダ移動した挙動を同じ動きをする
	animation1.play();
	display_dom.innerHTML = animation1.playState;
}, false);

// アニメーション終了時のイベント(注:domではなくanimateのインスタンスに対してのイベント付与)
animation1.addEventListener('finish', (e) => {

	// finish時にアラート
	alert("animation finish !");

	display_dom.innerHTML = animation1.playState;
}, false);

// 再生秒数を定期的に更新する
setInterval(() => {
    // アニメーションが再生中の場合に秒数を更新
    if (animation1.playState === 'running') {
        // currentTimeはミリ秒で返されるので、秒に変換して表示
        let disp_sec = (animation1.currentTime / 1000).toFixed(2) + '秒';
        document.getElementById('currentTime_value').innerHTML = disp_sec;
    }
}, 10); // 0.01秒ごとにチェック

</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

サンプル画面のアニメーション開始を実行し、アニメーションが終わるとアラートが表示されます。
その際「animation1.addEventListener(‘finish’, (e) => {」で記述したfinishのイベントリスナが呼ばれることがわかります。

cancel()イベント

アニメーションがidel状態に入った時に呼ばれるイベント

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
<style>
#bottom {
    margin-bottom    : 10px;
}
#test1_dom {
    color            : #5f5d5d;
    background-color : #eeeeee;
    font-size        : 1.1rem;
    width            : 150px;
}
</style>
</head>
<body>
<!-- ボタン -->
<div id="bottom">
    <input type="button" id="start_animate"   value="アニメーション開始"    >
    <input type="button" id="pause_animate"   value="アニメーション一時停止">
    <input type="button" id="reverse_animate" value="アニメーション逆再生"  >
    <input type="button" id="finish_animate"  value="アニメーション終了"    >
    <input type="button" id="cancel_animate"  value="アニメーション停止"    >
</div>
<!-- アニメーション用DIV -->
<div id="test1_dom">TEST1 (<span id="display"></span>)</div>
<!-- 再生秒数 -->
<div class="params" id="currentTime_value"></div>
<script>
// DOM要素取得
let start_animate_dom   = document.querySelector("#start_animate"  );
let pause_animate_dom   = document.querySelector("#pause_animate"  );
let reverse_animate_dom = document.querySelector("#reverse_animate");
let finish_animate_dom  = document.querySelector("#finish_animate" );
let cancel_animate_dom  = document.querySelector("#cancel_animate" );
let display_dom         = document.querySelector("#display"        );
let test1_dom           = document.querySelector("#test1_dom"      );
// アニメーション用オブジェクト
let animation1;
// keyframes定義
const keyframes1 = [
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(200px)'},
];
// option定義
const options1 = {
    duration   : 3000
};
animation1 = test1_dom.animate(keyframes1, options1);
animation1.cancel(); // 初期値は停止

// アニメーションキャンセル時のイベント(注:domではなくanimateのインスタンスに対してのイベント付与)
animation1.addEventListener('cancel', (e) => {
	// cancel時にアラート
	alert("animation cancel !");
	display_dom.innerHTML = animation1.playState;
}, false);

// アニメーション開始
start_animate_dom.addEventListener('click', (e) => {
	animation1.play();
	display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション一時停止
pause_animate_dom.addEventListener('click', (e) => {
	animation1.pause();
	display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション逆再生
reverse_animate_dom.addEventListener('click', (e) => {
	animation1.reverse();
	display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション終了
finish_animate_dom.addEventListener('click', (e) => {
	animation1.finish();
	display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション停止
cancel_animate_dom.addEventListener('click', (e) => {
	animation1.cancel();
	display_dom.innerHTML = animation1.playState;
}, false);

// 再生秒数を定期的に更新する
setInterval(() => {
    // アニメーションが再生中の場合に秒数を更新
    if (animation1.playState === 'running') {
        // currentTimeはミリ秒で返されるので、秒に変換して表示
        let disp_sec = (animation1.currentTime / 1000).toFixed(2) + '秒';
        document.getElementById('currentTime_value').innerHTML = disp_sec;
    }
}, 10); // 0.01秒ごとにチェック
</script>
</body>
</html>

サーバ上のHTMLはこちら(test2.html)

画面にアクセスし「アニメーション終了」ボタンを押下したとき、
「animation1.addEventListener(‘cancel’, (e) => {」のイベントリスナが呼ばれていることがわかります。

remove()イベント

アニメーションが削除された時に呼ばれるイベント

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
<style>
#bottom {
    margin-bottom    : 10px;
}
#test1_dom {
    color            : #5f5d5d;
    background-color : #eeeeee;
    font-size        : 1.1rem;
    width            : 150px;
}
</style>
</head>
<body>
<!-- ボタン -->
<div id="bottom">
    <input type="button" id="start_animate"   value="アニメーション開始"    >
    <input type="button" id="pause_animate"   value="アニメーション一時停止">
    <input type="button" id="reverse_animate" value="アニメーション逆再生"  >
    <input type="button" id="finish_animate"  value="アニメーション終了"    >
    <input type="button" id="cancel_animate"  value="アニメーション停止"    >
    <input type="button" id="remove_animate"  value="手動削除"              >
</div>
<!-- アニメーション用DIV -->
<div id="test1_dom">TEST1 (<span id="display"></span>)</div>
<!-- 再生秒数 -->
<div class="params" id="currentTime_value"></div>
<script>
// DOM要素取得
let start_animate_dom   = document.querySelector("#start_animate"  );
let pause_animate_dom   = document.querySelector("#pause_animate"  );
let reverse_animate_dom = document.querySelector("#reverse_animate");
let finish_animate_dom  = document.querySelector("#finish_animate" );
let cancel_animate_dom  = document.querySelector("#cancel_animate" );
let remove_animate_dom  = document.querySelector("#remove_animate" );
let display_dom         = document.querySelector("#display"        );
let test1_dom           = document.querySelector("#test1_dom"      );
// アニメーション用オブジェクト
let animation1;
// keyframes定義
const keyframes1 = [
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(200px)'},
];
// option定義
const options1 = {
    duration   : 3000
};
animation1 = test1_dom.animate(keyframes1, options1);
animation1.cancel(); // 初期値は停止

// アニメーションキャンセル時のイベント(注:domではなくanimateのインスタンスに対してのイベント付与)
animation1.addEventListener('remove', (e) => {
	// remove時にアラート
	alert("animation remove !");
	display_dom.innerHTML = animation1.playState;
}, false);

// アニメーション開始
start_animate_dom.addEventListener('click', (e) => {
	animation1.play();
	display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション一時停止
pause_animate_dom.addEventListener('click', (e) => {
	animation1.pause();
	display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション逆再生
reverse_animate_dom.addEventListener('click', (e) => {
	animation1.reverse();
	display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション終了
finish_animate_dom.addEventListener('click', (e) => {
	animation1.finish();
	display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション停止
cancel_animate_dom.addEventListener('click', (e) => {
	animation1.cancel();
	display_dom.innerHTML = animation1.playState;
}, false);

// 手動削除
remove_animate_dom.addEventListener('click', (e) => {
	// removeイベントを発火する
    const removeEvent = new Event('remove');
    animation1.dispatchEvent(removeEvent); // 手動でremoveイベントを発生させる

	// 要素を削除する
	test1_dom.remove();

	display_dom.innerHTML = animation1.playState;
}, false);

// 再生秒数を定期的に更新する
setInterval(() => {
    // アニメーションが再生中の場合に秒数を更新
    if (animation1.playState === 'running') {
        // currentTimeはミリ秒で返されるので、秒に変換して表示
        let disp_sec = (animation1.currentTime / 1000).toFixed(2) + '秒';
        document.getElementById('currentTime_value').innerHTML = disp_sec;
    }
}, 10); // 0.01秒ごとにチェック
</script>
</body>
</html>

サーバ上のHTMLはこちら(test3.html)

画面にアクセスして「手動削除」ボタンを押下すると、
「remove_animate_dom.addEventListener(‘click’, (e) => {」のイベントリスナ内で、
animation1のオブジェクトに対してremoveイベントを発火させています。

// removeイベントを発火する
const removeEvent = new Event('remove');
animation1.dispatchEvent(removeEvent); // 手動でremoveイベントを発生させる

なぜこの方法をとるかというと、animation1のオブジェクトを直接

animation1.remove()

という形で削除メソッドを実行しても「animation1オブジェクトはremoveメソッドを持たない」為にエラーになります。

なので、new Eventでremoveイベントを定義し、それをdispatchEventすることによって
animation1オブジェクトを削除しています。

削除時の挙動としては「animation1.addEventListener(‘remove’, (e) => {」で定義したremoveのイベントハンドラが実行されアラートが表示されることがわかります。
その後、明示的にDOM要素を削除していますが、

// 要素を削除する
test1_dom.remove();

この削除命令がなくてもanimation1のremoveイベントハンドラは実行される点に注意が必要です。

SVGアニメーション – Web Animations API を使う – アニメーション実行時のstartTimeプロパティについて

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – アニメーション実行時のstartTimeプロパティについて

startTimeプロパティはアニメーションする要素の再生開始タイミングを決定します。
また、サンプルでも動作させていますが、startTimeプロパティの値を決定した後、
アニメーションが再生されるまでcurrentTime(現在の再生位置)がマイナスからカウントダウンされるように表示されます。
例えば再生開始(startTime)が5秒後の場合は、現在の再生位置(currentTime)は-5秒から0に減っていきます。

再生開始タイミングがどのように影響するかを確認できる簡単なサンプルを用意しました。
スライダ位置を調整すると自動的にN秒(スライダ値)後にアニメーションが開始されます。

画面内にはN秒後にアニメーション再生される秒数表示と、現在の再生位置(N秒)がわかる秒数表示をしています。
秒数表示は0.01秒ごと画面描画しています。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
<style>
#bottom {
    margin-bottom    : 10px;
}
#test1_dom {
    color            : #5f5d5d;
    background-color : #cccccc;
    font-size        : 1.1rem;
    width            : 150px;
}
.range {
    width: 100%; /* スライダーの幅を画面最大に */
}
</style>
</head>
<body>
<!-- 開始ボタン -->
<div id="bottom">
    <input type="button" id="start_animate"  value="アニメーション開始">
    <input type="button" id="cancel_animate" value="アニメーション停止">
</div>
<!-- スライダ -->
<div class="params_box">
	<span class="param_title">再生開始(startTime)</span>
	<div><input type="range" class="range" id="range1" value="0" max="1000"></div>
	<span class="params" id="range1_value"></span>
</div>
<!-- アニメーション用DIV -->
<div id="test1_dom">TEST1 (<span id="display"></span>)</div>
<!-- 再生秒数 -->
<div class="params" id="currentTime_value"></div>
<script>
// DOM要素取得
let start_animate_dom  = document.querySelector("#start_animate" );
let cancel_animate_dom = document.querySelector("#cancel_animate");
let display_dom        = document.querySelector("#display"       );
let test1_dom          = document.querySelector("#test1_dom"     );
let range1_dom         = document.querySelector("#range1"        );

// アニメーション用オブジェクト
let animation1;
// keyframes定義
const keyframes1 = [
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(500px)'},
];
// option定義
const options1 = {
    duration   : 10000 // 100秒で一周
};
animation1 = test1_dom.animate(keyframes1, options1);
animation1.cancel(); // 初期値は停止

// アニメーション開始
start_animate_dom.addEventListener('click', (e) => {
	// 開始ボタン押下でスライダ移動した挙動を同じ動きをする
	change_range_slider();
}, false);

// アニメーション停止
cancel_animate_dom.addEventListener('click', (e) => {
	animation1.cancel();
	display_dom.innerHTML = animation1.playState;
}, false);

// スライダ値を取得しイベントを付与する
range1_dom.addEventListener('input', change_range_slider);
function change_range_slider()
{
	// 設定値を画面出力
	document.getElementById('range1_value').innerHTML = (range1_dom.value / 10) + '秒後に再生する';

	// 一旦アニメーションを停止する
	animation1.cancel();

	// アニメーションの再生位置を変更する
	animation1.startTime = animation1.timeline.currentTime + (range1_dom.value * 100);

	// divタグの状態を出力する
	display_dom.innerHTML = animation1.playState;
}

// 再生秒数を定期的に更新する
setInterval(() => {
    // アニメーションが再生中の場合に秒数を更新
    if (animation1.playState === 'running') {
        // currentTimeはミリ秒で返されるので、秒に変換して表示
        let disp_sec = (animation1.currentTime / 1000).toFixed(2) + '秒';
        document.getElementById('currentTime_value').innerHTML = disp_sec;
    }
}, 10); // 0.01秒ごとにチェック

</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

画面にアクセスしてスライダを操作すると、N秒後にアニメーションが開始されます。
スライダを変更すると都度、N秒後にアニメーションが開始されることと、現在位置としてcurrentTime秒が変化することがわかります。

SVGアニメーション – Web Animations API を使う – アニメーション実行中の再生位置について

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – アニメーション実行中の再生位置について

前回の投稿(https://propansystem.net/blog/9976)で、再生時のステータス値を確認するサンプルを用意しました。

ここでは、再生時の再生位置(秒)を取得するサンプルを書いて試してみます。

再生位置はcurrentTimeプロパティの値を取得することで得られます。
また、下記サンプルでは0.01秒ごとに取得した値を画面描画しています。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
<style>
#bottomStart {
    margin-bottom    : 10px;
}
#test1_dom {
    color            : #5f5d5d;
    background-color : #cccccc;
    font-size        : 1.1rem;
    width            : 150px;
}
.range {
    width: 100%; /* スライダーの幅を画面最大に */
}
</style>
</head>
<body>
<!-- 開始ボタン -->
<div id="bottomStart">
    <input type="button" id="start_animate"  value="アニメーション開始">
    <input type="button" id="cancel_animate" value="アニメーション停止">
</div>
<!-- 再生秒数の表示 -->
再生秒数:<span class="params" id="currentTime_value"></span>
<!-- アニメーション用DIV -->
<div id="test1_dom">TEST1 (<span id="display"></span>)</div>

<script>
// DOM要素取得
let start_animate_dom  = document.querySelector("#start_animate" );
let cancel_animate_dom = document.querySelector("#cancel_animate");
let display_dom        = document.querySelector("#display"       );
let test1_dom          = document.querySelector("#test1_dom"     );
let range1_dom         = document.querySelector("#range1"        );

// アニメーション用オブジェクト
let animation1;
// keyframes定義
const keyframes1 = [
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(500px)'},
];
// option定義
const options1 = {
    duration   : 10000, // 100秒で一周
    iterations : Infinity
};
animation1 = test1_dom.animate(keyframes1, options1);
animation1.cancel(); // 初期値は停止

// アニメーション開始
start_animate_dom.addEventListener('click', (e) => {
	animation1.play();
	display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション停止
cancel_animate_dom.addEventListener('click', (e) => {
	animation1.cancel();
	display_dom.innerHTML = animation1.playState;
}, false);

// 再生秒数を定期的に更新する
setInterval(() => {
    // アニメーションが再生中の場合に秒数を更新
    if (animation1.playState === 'running') {
        // currentTimeはミリ秒で返されるので、秒に変換して表示
        let disp_sec = (animation1.currentTime / 1000).toFixed(2) + '秒';
        document.getElementById('currentTime_value').innerHTML = disp_sec;
    }
}, 10); // 0.01秒ごとにチェック

</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

画面にアクセスして「アニメーション開始」ボタンを押下します。

ボタンのすぐ下に「再生秒数」がリアルタイムに表示され、値が取得できていることが確認できます。

SVGアニメーション – Web Animations API を使う – アニメーション実行中の再生倍率について

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – アニメーション実行中の再生倍率について

前回の投稿(https://propansystem.net/blog/9978)で、逆再生を行う方法としてplaybackRateプロパティを使用する方法をとりあげました。

ここでは、playbackRateについて再生倍率を制御する目的で試してみます。

簡単なサンプルを書いて試してみます。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
<style>
#bottomStart {
    margin-bottom    : 10px;
}
#test1_dom {
    color            : #5f5d5d;
    background-color : #cccccc;
    font-size        : 1.1rem;
    width            : 150px;
}
.range {
    width: 100%; /* スライダーの幅を画面最大に */
}
</style>
</head>
<body>
<!-- 開始ボタン -->
<div id="bottomStart">
    <input type="button" id="start_animate"  value="アニメーション開始">
    <input type="button" id="cancel_animate" value="アニメーション停止">
</div>
<!-- スライダ -->
<div class="params_box">
	<span class="param_title">再生倍率調整</span>
	<div><input type="range" class="range" id="range1" value="0" max="1000"></div>
	<span class="params" id="range1_value"></span>
</div>
<!-- アニメーション用DIV -->
<div id="test1_dom">TEST1 (<span id="display"></span>)</div>

<script>
// DOM要素取得
let start_animate_dom  = document.querySelector("#start_animate" );
let cancel_animate_dom = document.querySelector("#cancel_animate");
let display_dom        = document.querySelector("#display"       );
let test1_dom          = document.querySelector("#test1_dom"     );
let range1_dom         = document.querySelector("#range1"        );

// アニメーション用オブジェクト
let animation1;
// keyframes定義
const keyframes1 = [
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(500px)'},
];
// option定義
const options1 = {
    duration   : 100000, // 100秒で一周
    iterations : Infinity
};
animation1 = test1_dom.animate(keyframes1, options1);
animation1.cancel(); // 初期値は停止

// アニメーション開始
start_animate_dom.addEventListener('click', (e) => {
	animation1.play();
	display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション停止
cancel_animate_dom.addEventListener('click', (e) => {
	animation1.cancel();
	display_dom.innerHTML = animation1.playState;
}, false);

// スライダの要素を取得し、再生速度調整用のイベントを付与する
range1_dom.addEventListener('input', change_range_slider);
function change_range_slider()
{
	// パターン内のDOM情報を取得する
	let set_param_value = range1_dom.value;

	// アニメーションの再生速度(playbackRate)を変更する
	animation1.playbackRate = set_param_value;

	// 設定値を画面出力
	document.getElementById('range1_value').innerHTML = range1_dom.value + '倍';
}

</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

画面にアクセスして「アニメーション開始」ボタンを押下したあと、「再生倍率調整」のスライダを動かすと再生時の倍率を1~1000で変更することができます。

アニメーションは「DIVタグをx軸法j広告に100秒かけて500px移動する」というシンプルなもので、optionsで「iterations : Infinity」を指定して無限に繰り返すようになっています。

アニメーション開始ボタンを押下して、スライダを動かすと再生倍率が変わりDIVタグの移動スピードが変化することがわかります。

これはあくまでも再生倍率なので、divタグの移動スピードを早めている訳ではないことに注意が必要です。

SVGアニメーション – Web Animations API を使う – アニメーション実行中のreverse()メソッドと、iterationsプロパティのInfinityについて

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – アニメーション実行中のreverse()メソッドと、iterationsプロパティのInfinityについて

前回の投稿(https://propansystem.net/blog/9976)で、option定義でiterations属性の値を「Infinity」にしたサンプルを試します。

前回のサンプルのOptions定義「iterations」プロパティに「Infinity」の値を追加し、
アニメーション開始後にreverse()メソッドを有効にする逆再生ボタンを押下するとjavascriptエラーになります。

Uncaught InvalidStateError: Failed to execute 'reverse' on
 'Animation': Cannot play reversed Animation with infinite target effect end.
 at HTMLInputElement.<anonymous> 

「Infinity」プロパティを有効にすると無限再生することになる為、終了状態を持たない為エラーになります。

SVGアニメーション – Web Animations API を使う – アニメーション実行中のreverseメソッドとplaybackRateプロパティについて

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – アニメーション実行中のreverseメソッドとplaybackRateプロパティについて

前回投稿したサンプルをもとに、アニメーション実行中のreverseの動きを詳しくみてみます。

まず、通常のアニメーション再生ではplay()メソッドを使い、「アニメーション開始→アニメーション終了」まで順再生されます。

次に下記のように逆再生(reverse()メソッド)を使った場合は、アニメーションが逆再生されます。

// アニメーション逆再生
reverse_animate_dom.addEventListener('click', (e) => {
	//animation1 = test1_dom.animate(keyframes1, options1);
	animation1.reverse();
	display_dom.innerHTML = animation1.playState;
}, false);

この時、逆再生中にもう一度「アニメーション逆再生」ボタンを押下すると順再生に戻ります。
これは「逆再生の逆は順再生」という意味の挙動になります。

別な逆再生の例として「Animation.playbackRate」をあげます。
本来の指定の仕方では逆再生用のプロパティではなく「再生速度の倍率」を表します。
playbackRateの値を「2」にすると、再生倍率は2倍になり、
「3」にすると再生倍率は3倍になります。

また負の数では逆方向への再生になり、「-2」だと逆再生方向に2倍の速度でアニメーションします。
「0」の場合にはアニメーションは停止します。

注意点としては、この「playbackRate」の値が「負の数値」を使った場合は、reverse()メソッドにかかわらず逆再生される点です。

SVGアニメーション – Web Animations API を使う – アニメーション実行中のplayStateの値について

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – アニメーション実行中のplayStateの値について

アニメーション実行中にanimationしているオブジェクトのplayStateプロパティを参照することで、実行状態が見えます。
playStateは以下のものがあります。

開始
一時停止
逆再生
終了
停止

実際にサンプルを用意して確かめてみると、逆再生の時の値は「running」と表示され、通常の開始と同じ値になります。(ただしDIVのアニメーションの動きは逆です)

以下に簡単なサンプルを用意しました。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
<style>
#bottomStart {
    margin-bottom    : 10px;
}
#test1_dom {
    color            : #5f5d5d;
    background-color : #eeeeee;
    font-size        : 1.1rem;
    width            : 150px;
}
</style>
</head>
<body>
<!-- 開始ボタン -->
<div id="bottomStart">
    <input type="button" id="start_animate"   value="アニメーション開始"    >
    <input type="button" id="pause_animate"   value="アニメーション一時停止">
    <input type="button" id="reverse_animate" value="アニメーション逆再生"  >
    <input type="button" id="finish_animate"  value="アニメーション終了"    >
    <input type="button" id="cancel_animate"  value="アニメーション停止"    >
</div>
<!-- アニメーション用DIV -->
<div id="test1_dom">TEST1 (<span id="display"></span>)</div>
<!-- 状態表示 -->

<script>
// DOM要素取得
let start_animate_dom   = document.querySelector("#start_animate"  );
let pause_animate_dom   = document.querySelector("#pause_animate"  );
let reverse_animate_dom = document.querySelector("#reverse_animate");
let finish_animate_dom  = document.querySelector("#finish_animate" );
let cancel_animate_dom  = document.querySelector("#cancel_animate" );
let display_dom         = document.querySelector("#display"        );
let test1_dom           = document.querySelector("#test1_dom"      );
// アニメーション用オブジェクト
let animation1;
// keyframes定義
const keyframes1 = [
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(200px)'},
];
// option定義
const options1 = {
    duration   : 10000
};
animation1 = test1_dom.animate(keyframes1, options1);
animation1.cancel(); // 初期値は停止

// アニメーション開始
start_animate_dom.addEventListener('click', (e) => {
	//animation1 = test1_dom.animate(keyframes1, options1);
	animation1.play();
	display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション一時停止
pause_animate_dom.addEventListener('click', (e) => {
	//animation1 = test1_dom.animate(keyframes1, options1);
	animation1.pause();
	display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション逆再生
reverse_animate_dom.addEventListener('click', (e) => {
	//animation1 = test1_dom.animate(keyframes1, options1);
	animation1.reverse();
	display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション終了
finish_animate_dom.addEventListener('click', (e) => {
	//animation1 = test1_dom.animate(keyframes1, options1);
	animation1.finish();
	display_dom.innerHTML = animation1.playState;
}, false);
// アニメーション停止
cancel_animate_dom.addEventListener('click', (e) => {
	//animation1 = test1_dom.animate(keyframes1, options1);
	animation1.cancel();
	display_dom.innerHTML = animation1.playState;
}, false);

</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

SVGアニメーション – Web Animations API を使う – アニメーション実行中にKeyFrameEffectのcompositeプロパティを変更した場合の挙動について

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – アニメーション実行中にKeyFrameEffectのcompositeプロパティを変更した場合の挙動について

アニメーション実行中にKeyFrameEffectのcompositeプロパティをスクリプトから変更することができます。
compositeプロパティはアニメーションのkeyframeの複数の項目がある場合、
その複数の項目の値をどのように設定するかを制御します。

例えばdivが画面上に2つある場合、一つをDivAとし、もう一つをDivBとする場合、
DivAに設定されたkeyframeの項目とDivBに設定されたkeyframeの項目の値をどのようにするかを制御します。

ここでは「DivA」と「DivB」をもとに掘り下げてみます。

ボタンは「add」「accumulate」「replace」3種類あり、それぞれ以下の挙動になります。

①add

DivAとDivBのkeyframeのプロパティを加算(合算)します。
例えばDivAに、以下のkeyframeが設定されており、

[
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(150px)'},
];

DivBに、以下のkeyframeが設定されている場合、

[
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(200px)'},
];

addした結果は以下のようになります。

[
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(150px)'},
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(200px)'},
];

②accumulate

DivAとDivBのkeyframeのプロパティを累積します。
例えばDivAに、以下のkeyframeが設定されており、

[
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(150px)'},
];

DivBに、以下のkeyframeが設定されている場合、

[
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(200px)'},
];

addした結果は以下のようになります。

[
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(350px)'},
];

③replace

DivAとDivBのkeyframeのプロパティを上書き(置き換え)します。
例えばDivAに、以下のkeyframeが設定されており、

[
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(150px)'},
];

DivBに、以下のkeyframeが設定されている場合、

[
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(200px)'},
];

replaceした結果は以下のようになります。

[
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(200px)'},
];

(注意…説明では②accumulateと同じ値になりますが、プロパティがtranslateXのみで、2項目しかないので見かけ上は同様になります)

上記にまとめた挙動を確認する為
画面上のボタン押下によってアニメーション再生中にcompositeプロパティを動的に変更した時の動きを確認できるサンプルを用意しました。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>
<style>
#bottomStart {
    margin-bottom    : 10px;
}
#pseudoElementTest1 {
    color            : #5f5d5d;
    background-color : #ffdfdf;
    font-size        : 1.1rem;
    width            : 100px;
}
#pseudoElementTest2 {
    color            : #5f5d5d;
    background-color : #f4f4f4;
    background-color : #dfe7ff;
    font-size        : 1.1rem;
    width            : 100px;
}
</style>
</head>
<body>
<!-- 開始ボタン -->
<div id="bottomStart">
    <input type="button" id="animate_timline_test" value="アニメーション開始">
</div>

<!-- 変更ボタン -->
<input type="button" id="set_composite_add"        value="addに変更">
<input type="button" id="set_composite_accumulate" value="accumulateに変更">
<input type="button" id="set_composite_replace"    value="replaceに変更">

<!-- 状態 -->
<div>現在の状態:<span id="pseudoElementDisp"></span></div>

<!-- アニメーション用DIV -->
<div id="pseudoElementTest1">TEST1</div>
<div id="pseudoElementTest2">TEST2</div>
<script>
//--------------------------------
// DOM要素取得
//--------------------------------
let animateTimlineTestDom = document.querySelector("#animate_timline_test");
let pseudoElementTest1Dom = document.querySelector("#pseudoElementTest1");
let pseudoElementTest2Dom = document.querySelector("#pseudoElementTest2");
let pseudoElementDispDom  = document.querySelector("#pseudoElementDisp");

// プロパティ変更ボタン
let dom_set_composite_add        = document.getElementById('set_composite_add');
let dom_set_composite_accumulate = document.getElementById('set_composite_accumulate');
let dom_set_composite_replace    = document.getElementById('set_composite_replace');

//--------------------------------
// アニメーション用定義
//--------------------------------
// アニメーション用オブジェクト
let animation1;
let animation2;
// keyframesの定義
const keyframes1 = [
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(150px)'},
];
const keyframes2 = [
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(200px)'},
];

// アニメーション開始ボタン
animateTimlineTestDom.addEventListener('click', (e) => {
    // デフォルトはaddにする
    updateanimation('add');
}, false);

// 「addに変更」
dom_set_composite_add.addEventListener('click', () => {
    updateanimation('add');
}, false);

// 「accumulateに変更」
dom_set_composite_accumulate.addEventListener('click', () => {
    updateanimation('accumulate');
}, false);

// 「replaceに変更」
dom_set_composite_replace.addEventListener('click', () => {
    updateanimation('replace');
}, false);

// 引数に基づいたアニメーションの開始
function updateanimation(upd_composite)
{
    // 画面表示
    pseudoElementDispDom.innerHTML = upd_composite;

    // optionの定義
    const currentOptions1 = {
        duration   : 15000,
        iterations : Infinity
    };
    const currentOptions2 = {
        duration   : 15000,
        iterations : Infinity,
        composite  : upd_composite // このタイミングでcompositeの値を更新
    };

    // オプションを設定する
    animation1 = pseudoElementTest1Dom.animate(keyframes1, currentOptions1);
    // オプションを設定する
    animation2 = pseudoElementTest2Dom.animate(keyframes2, currentOptions2);

    // アニメーション開始
    animation1.play();
    animation2.play();
}

</script>
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

画面にアクセスして「アニメーション開始」を押下します。
すると、画面上の2つのdivタグがアニメーションします。
アニメーション中に「addに変更」「accumulateに変更」「replaceに変更」のいずれかのボタンを押下すると、
2つめのDiv「TEST2」のcompositeプロパティの値が動的に変化します。

「アニメーション開始」ボタン押下後、addに変更ボタンを押下するとDivタグ「pseudoElementTest2」のcurrentOptions2プロパティが変わり、

const keyframes2 = [
    {transform: 'translateX(0px)'  },
    {transform: 'translateX(200px)'},
];

のプロパティの値が動的に変わる(この場合は項目の加算)ことがわかります。

また、「accumulateに変更」ボタン、「replaceに変更」ボタンを押下した時も同様に変化することがわかります。

さらに、アニメーション中に「accumulateに変更」ボタンをN回押下するとプロパティが更新された後、
また「accumulateに変更」ボタンを押下すると値が累積してアニメーションすることがわかります。
この場合はtranslateXの値がN回押下分累積した値になることがわかります。

SVGアニメーション – Web Animations API を使う – KeyFrameEffectのタイミングプロパティを変更する

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – KeyFrameEffectのタイミングプロパティを変更する

KeyFrameEffectのタイミングプロパティをスクリプトから変更することができます。

画面上のボタン押下によってアニメーション再生中にタイミングプロパティを変更するサンプルを用意しました。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>

<style>
.animate_timeline_dom {
    background-color: #cccccc;
    width: 150px;
}
</style>

</head>
<body>

<input type="button" id="animate_timline_test" value="animate start">
<div>
<input type="button" id="set_timing_1" value="タイミングプロパティ1に更新">
<input type="button" id="set_timing_2" value="タイミングプロパティ2に更新">
</div>

<div class="KeyframeEffectDom">
    <div>タイミングプロパティ -> <span id="timing"></span></div>
</div>

<div class="animate_timeline_dom">TestDom</div>

<script>

let keyf = [
    {transform: "translateX(0px)"  },
    {transform: "translateX(400px)"},
];

// タイミングプロパティ1
let time1 = {
    direction : 'normal',
    duration  : 5000
};

// タイミングプロパティ2
let time2 = {
    direction : 'reverse',
    duration  : 10000
};

let animate_timeline_dom = document.querySelector(".animate_timeline_dom");

let keyframes_test = new KeyframeEffect(
    animate_timeline_dom,
    keyf,
    time1
);

let anim_test = new Animation(
    keyframes_test,
    document.timeline,
);

let timingdom = document.querySelector("#timing");
timingdom.innerHTML = 'タイミングプロパティ 1';

animate_timline_test.addEventListener('click', () => {
    anim_test.play();
}, false);

set_timing_1.addEventListener('click', () => {
	timingdom.innerHTML = 'タイミングプロパティ 1';
	anim_test.effect.updateTiming(time1); // タイミングプロパティ1 のセット
}, false);

set_timing_2.addEventListener('click', () => {
	timingdom.innerHTML = 'タイミングプロパティ 2';
	anim_test.effect.updateTiming(time2); // タイミングプロパティ2 のセット
}, false);

</script>
 
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

画面にアクセスして「Animate Startボタン」を押下します。
初期値ではタイミングプロパティ1がセットされています。
アニメーション中に「タイミングプロパティ2に更新」ボタンを押下すると、
途中でタイミングプロパティ2の値に切り替わり、アニメーションが
「direction : ‘reverse’」の為に逆再生になります。

今回はわかりやすいようにkeyframeは固定しています。
なので、transformでdomの移動方向は変更していません。

また、タイミングプロパティ2の再生中に、ふたたび「タイミングプロパティ1に更新」ボタンを押下すると、アニメーションは順再生に戻ります。

再生時間が終了していなければ、何度でも切り替えて動作することが確認できます。

SVGアニメーション – Web Animations API を使う – KeyFrameEffectのkeyframeを変更する

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – KeyFrameEffectのkeyframeを変更する

KeyFrameEffectのkeyframeをスクリプトから変更することができます。

画面上にkeyframeを変更するボタンを設置し、ボタン押下によってアニメーション再生中にkeyfarmeを変更するサンプルを用意しました。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>

<style>
.animate_timeline_dom {
    background-color: #cccccc;
    width: 150px;
}
</style>

</head>
<body>

<input type="button" id="animate_timline_test" value="animate start">
<input type="button" id="set_keyframe_1"       value="set [keyframe 1]">
<input type="button" id="set_keyframe_2"       value="set [keyframe 2]">

<div class="KeyframeEffectDom">
    <div>keyframe -> <span id="keyframe"></span></div>
</div>

<div class="animate_timeline_dom">TestDom</div>

<script>

let keyf1 = [
    {transform: "translateX(0px)"  },
    {transform: "translateX(400px)"},
    {transform: "translateY(300px)"},
    {transform: "scale(2)"         },
];

let keyf2 = [
    {transform: "scale(1)"         },
    {transform: "scale(0.2)"       },
    {transform: "translateY(300px)"},
    {transform: "scale(3)"         },
    {transform: "translateX(500px)"},
];

let animate_timeline_dom = document.querySelector(".animate_timeline_dom");

let keyframes_test = new KeyframeEffect(
    animate_timeline_dom,
    keyf1,
    {
        duration: 5000,
        delay   : 100,
        endDelay: 300
    }
);

let anim_test = new Animation(
    keyframes_test,
    document.timeline,
);


let keyframedom = document.querySelector("#keyframe");
keyframedom.innerHTML = 'keyframe 1';

animate_timline_test.addEventListener('click', () => {
    anim_test.play();
}, false);

set_keyframe_1.addEventListener('click', () => {
	keyframedom.innerHTML = 'keyframe 1';
	anim_test.effect.setKeyframes(keyf1); // keyframe 1 のセット
}, false);

set_keyframe_2.addEventListener('click', () => {
	keyframedom.innerHTML = 'keyframe 2';
	anim_test.effect.setKeyframes(keyf2); // keyframe 2 のセット
}, false);

</script>
 
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

画面にアクセスし、set [keyframe 1]および、set [keyframe 2]ボタンを押下し、
animate startボタンを押下すると、変更されたkeyframeでアニメーションされることがわかります。

以下の画像は実行時の画面キャプチャです。

アニメーション操作を複数回していますが、最後のアニメーション再生の際には、アニメーション途中でkeyframeの変更ボタンを押下していますが、アニメーション再生中でもkeyframeが動的に切り替わっていることがわかります。

SVGアニメーション – Web Animations API を使う – KeyFrameEffectのタイミングプロパティをgetComputedTimingを使って確認する

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – KeyFrameEffectのタイミングプロパティをgetComputedTimingを使って確認する

KeyFrameEffectのタイミングプロパティをgetComputedTimingを使って取得できるので、その内容がどうなっているのかを確認します。

サンプル全体はこちら

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>

<style>
.animate_timeline_dom {
    background-color: #cccccc;
    width: 150px;
}
</style>

</head>
<body>

<input type="button" id="button_1" value="animate timline test">
<div class="animate_timeline_dom">Test DOM</div>

<div class="KeyframeEffectDom">
    <div>getComputedTiming -> </div>
    <div id="KeyframeEffectDom1"></div>
</div>

<script>

button_1.addEventListener('click', () => {

    // DOM要素を取得
    let animate_timeline_dom = document.querySelector(".animate_timeline_dom");

    let keyframes_test = new KeyframeEffect(
        animate_timeline_dom,
        [
            {transform: "translateX(0px)"  },
            {transform: "translateX(200px)"},
            {transform: "translateY(100px)"},
            {transform: "scale(2)"         },
        ],
        {
            duration: 5000,
            delay   : 100,
            endDelay: 300
        }
    );
    
    let anim_test = new Animation(
        keyframes_test,
        document.timeline,
    );

    // アニメーションの再生
    anim_test.play();
    
    // 画面表示用DOMを取得する
    let keyframeeffectdom1 = document.querySelector("#KeyframeEffectDom1");

    let keyframeeffectdom1obj = anim_test.effect.getComputedTiming();

    for(var key in keyframeeffectdom1obj){
        keyframeeffectdom1.innerHTML += key + ' -> ' + keyframeeffectdom1obj[key] + '<br />';
    }

}, false);
</script>
 
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

実行して内容を確認すると、画面上には以下の出力になります。

getKeyframes ->
delay -> 100
direction -> normal
duration -> 5000
easing -> linear
endDelay -> 300
fill -> none
iterationStart -> 0
iterations -> 1
activeDuration -> 5000
currentIteration -> null
endTime -> 5399.999999999999
localTime -> 0
progress -> null

という出力になります。

この結果から KeyframeEffectのタイミングプロパティで指定した3つのプロパティ

duration: 5000,
delay   : 100,
endDelay: 300

がそれぞれ取得していることがわかります。

SVGアニメーション – Web Animations API を使う – KeyFrameEffectのプロパティをgetKeyframesを使って確認する

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – KeyFrameEffectのプロパティをgetKeyframesを使って確認する

KeyFrameEffectのプロパティをgetKeyframesを使って取得できるので、その内容がどうなっているのかを確認します。

前回のサンプルを少し変えて、getKeyframesで取得した値を画面に出力するものを用意しました。
サンプル全体はこちら

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>

<style>
.animate_timeline_dom {
    background-color: #cccccc;
    width: 150px;
}
</style>

</head>
<body>

<input type="button" id="button_1" value="animate timline test">
<div class="animate_timeline_dom">Test DOM</div>

<div class="KeyframeEffectDom">
    <div>getKeyframes -> </div>
    <div id="KeyframeEffectDom1"></div>
</div>

<script>

button_1.addEventListener('click', () => {

    // DOM要素を取得
    let animate_timeline_dom = document.querySelector(".animate_timeline_dom");

    let keyframes_test = new KeyframeEffect(
        animate_timeline_dom,
        [
            {transform: "translateX(0px)"  },
            {transform: "translateX(200px)"},
            {transform: "translateY(100px)"},
            {transform: "scale(2)"         },
        ],
        { duration: 5000 },
    );
    
    let anim_test = new Animation(
        keyframes_test,
        document.timeline,
    );

    // アニメーションの再生
    anim_test.play();
    
    // 画面表示用DOMを取得する
    let keyframeeffectdom1 = document.querySelector("#KeyframeEffectDom1");

    let keyframeeffectdom1obj = anim_test.effect.getKeyframes();

    // DOMに取得内容を出力する
    keyframeeffectdom1obj.forEach(function(_x) {
        for(var key in _x){
            keyframeeffectdom1.innerHTML += key + ' -> ' + _x[key] + '<br />';
        }
        keyframeeffectdom1.innerHTML += '----------------<br />';
    });

}, false);
</script>
 
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

実行して内容を確認すると、画面上には以下の出力になります。

getKeyframes ->
offset -> null
easing -> linear
composite -> auto
transform -> translateX(0px)
computedOffset -> 0
----------------
offset -> null
easing -> linear
composite -> auto
transform -> translateX(200px)
computedOffset -> 0.3333333333333333
----------------
offset -> null
easing -> linear
composite -> auto
transform -> translateY(100px)
computedOffset -> 0.6666666666666666
----------------
offset -> null
easing -> linear
composite -> auto
transform -> scale(2)
computedOffset -> 1
----------------

という出力になります。

この結果から KeyframeEffectで指定した4つのプロパティ

{transform: "translateX(0px)"  },
{transform: "translateX(200px)"},
{transform: "translateY(100px)"},
{transform: "scale(2)"         },

がそれぞれ取得していることがわかります。

SVGアニメーション – Web Animations API を使う – KeyFrameEffectのプロパティを確認する

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – KeyFrameEffectのプロパティを確認する

前回投稿したAnimationの第一引数「keyframes_test」は

animation の基本構文

var animation = new Animation([effect][, timeline]);

のeffectにあたり、KeyFrameEffectオブジェクトと言えます。

このオブジェクトのプロパティを出力して調べてみます。

前回のスクリプトにconsole.logを追加し、KeyFrameEffectオブジェクトの下記のプロパティを出力してみます。

target
pseudoElement
iterationComposite
composite

サンプル全体はこちら

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>

<style>
.animate_timeline_dom {
    background-color: #cccccc;
    width: 150px;
}
</style>

</head>
<body>

<input type="button" id="button_1" value="animate timline test">
<div class="animate_timeline_dom">Test DOM</div>

<script>

button_1.addEventListener('click', () => {

	// DOM要素を取得
	let animate_timeline_dom = document.querySelector(".animate_timeline_dom");

	let keyframes_test = new KeyframeEffect(
		animate_timeline_dom,
		[
			{transform: "translateX(0px)"  },
			{transform: "translateX(200px)"},
			{transform: "translateY(100px)"},
			{transform: "scale(2)"         },
		],
		{ duration: 5000 },
	);
	
	let anim_test = new Animation(
		keyframes_test,
		document.timeline,
	);

    // アニメーションの再生
    anim_test.play();
    
    console.log("anim_test effect             -> " + anim_test.effect);
    console.log("anim_test target             -> " + anim_test.effect.target);
    console.log("anim_test pseudoElement      -> " + anim_test.effect.pseudoElement);
    console.log("anim_test iterationComposite -> " + anim_test.effect.iterationComposite);
    console.log("anim_test composite          -> " + anim_test.effect.composite);

}, false);
</script>
 
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

実行して内容を確認すると

anim_test effect             -> [object KeyframeEffect]
anim_test target             -> [object HTMLDivElement]
anim_test pseudoElement      -> null
anim_test iterationComposite -> undefined
anim_test composite          -> replace

という出力になります。

SVGアニメーション – Web Animations API を使う – KeyFrameEffectについて

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – KeyFrameEffectについて

KeyFrameEffectを試します。

基本構文

new KeyframeEffect(target, keyframes)
new KeyframeEffect(target, keyframes, options)
new KeyframeEffect(sourceKeyFrames)

サンプル

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>

<style>
.animate_timeline_dom {
    background-color: #cccccc;
    width: 150px;
}
</style>

</head>
<body>

<input type="button" id="button_1" value="animate timline test">
<div class="animate_timeline_dom">Test DOM</div>

<script>

button_1.addEventListener('click', () => {

	// DOM要素を取得
	let animate_timeline_dom = document.querySelector(".animate_timeline_dom");

	let keyframes_test = new KeyframeEffect(
		animate_timeline_dom,
		[
			{transform: "translateX(0px)"  },
			{transform: "translateX(200px)"},
			{transform: "translateY(100px)"},
			{transform: "scale(2)"         },
		],
		{ duration: 5000 },
	);
	
	let anim_test = new Animation(
		keyframes_test,
		document.timeline,
	);

    // アニメーションの再生
    anim_test.play();

}, false);
</script>
 
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

サンプルでは、以下の箇所がKeyframeEffectを使用した定義です。

	let keyframes_test = new KeyframeEffect(
		animate_timeline_dom,
		[
			{transform: "translateX(0px)"  },
			{transform: "translateX(200px)"},
			{transform: "translateY(100px)"},
			{transform: "scale(2)"         },
		],
		{ duration: 5000 },
	);

targetは対象のDOM
keyframesは任意のアニメーションの内容を指定
optionsは任意のタイミングプロパティの内容を指定

前回の投稿で、animation の基本構文を書きました。

var animation = new Animation([effect][, timeline]);

この「effect」については、次の定義になります。

(MDNから抜粋)
このアニメーションに関連付けられた AnimationEffect を取得または設定します。
これはふつう、 KeyframeEffect オブジェクトになります。

上記のサンプルで、アニメーションの定義を次のようにしていますが、

	let anim_test = new Animation(
		keyframes_test,
		document.timeline,
	);

「keyframes_test」という記述が「new KeyframeEffect」したものであり、それが「new Animation([effect][, timeline]);」のeffectとして割り当てていると言えます。

new Animation の引数に new KeyframeEffect を記述する

new Animationの引数に直接new keyframeEffectを記述する例です。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>

<style>
.animate_timeline_dom {
    background-color: #cccccc;
    width: 150px;
}
</style>

</head>
<body>

<input type="button" id="button_1" value="animate timline test">
<div class="animate_timeline_dom">Test DOM</div>

<script>

button_1.addEventListener('click', () => {

	let animate_timeline_dom = document.querySelector(".animate_timeline_dom");

	let anim_test = new Animation(
		new KeyframeEffect(
			animate_timeline_dom,
			[
				{transform: "translateX(0px)"  },
				{transform: "translateX(200px)"},
				{transform: "translateY(100px)"},
				{transform: "scale(2)"         },
			],
			{ duration: 5000 },
		),
		document.timeline,
	);

    // アニメーションの再生
    anim_test.play();

}, false);
</script>
 
</body>
</html>

サーバ上のHTMLはこちら(test2.html)

SVGアニメーション – Web Animations API を使う – 基本的な記述

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – 基本的な記述

Animation()によるAnimationオブジェクトの生成を試します。

animationオブジェクトを生成する際の第二引数に用い、AnimationTimelineというインタフェースを取得している。

var animation = new Animation([effect][, timeline]);

これまで紹介してきた

Element.animate()

とは異なり、play()メソッドでアニメーションを開始する。

まずはこれまでの Element.animate() の書き方でサンプルを用意し、それをもとに、その後 animation = new Animation を試します。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テストHTML</title>

<style>
.animate_timeline_dom {
    background-color: #ebebeb;
}
</style>

</head>
<body>

<input type="button" id="button_1" value="animate test">
<div class="animate_dom">test</div>
<div id="log_disp"></div>

<script>

// アニメーション用の空オブジェクトを定義
let animation;

// 「animate test」ボタンのDOM取得と押下イベントの用意
let dom_button_1 = document.getElementById('button_1');
button_1.addEventListener('click', () => {

    // DOM要素を取得
    let animate_dom = document.querySelector(".animate_dom");

    // アニメーションの effect と timeline を定義
    animation = animate_dom.animate(
        [
            {backgroundColor: '#fff', width:'100px'},
            {backgroundColor: '#999', width:'50px' },
        ],
        {
            duration: 8000
        }
    );

    // アニメーションの再生
    animation.play();

}, false);
</script>
 
</body>
</html>

サーバ上のHTMLはこちら(test1.html)

いままでは Element.animate() という形でアニメーションを定義、再生していましたが、
Web Animation API のインターフェースとしてのAnimationを使用し、

animation.play();

というメソッドでアニメーション再生しています。

画面にアクセスして、animate testボタンを押下した時の動きは以下のようになります。

duration: 8000 の定義をしているので、8秒かけて
{backgroundColor: ‘#fff’, width:’100px’}
から
{backgroundColor: ‘#999′, width:’50px’ }
へアニメーションしていることがわかります。

SVGアニメーション – Web Animations API を使う – Animation

Posted コメントするカテゴリー: javascript

SVGアニメーション – Web Animations API を使う – Animation

ここからは Animation のAPI(インターフェイス) を使用した方法を試します。

Animation のAPIのプロパティとメソッド

プロパティ

Animation.currentTime
Animation.effect
Animation.finished
Animation.id
Animation.pending
Animation.playState
Animation.playbackRate
Animation.ready
animation.replaceState
Animation.startTime
Animation.timeline

メソッド

Animation.cancel()
Animation.finish()
Animation.pause()
Animation.play()
Animation.reverse()

(MDNから抜粋)

Animation()によるAnimationオブジェクトの生成

animationオブジェクトを生成するには以下のように書きます。

var animation = new Animation([effect][, timeline]);

引数はそれぞれ以下の指定をします。

effect
アニメーションに割り当てるエフェクト効果を指定。
現時点ではKeyFrameEffectオブジェクトのみ。

timeline
アニメーションに関連づけるtimelineを指定
現時点ではDocumentTimelineオブジェクトのみ。