🔁

JavaScript+ScaledroneでWebSocketの同期処理を作る

2024/02/07に公開

はじめに

WebSocketをベースとしたリアルタイムメッセージサービス「Scaledrone」を使うと、
自分でWebSocket用サーバーを用意せず、簡単に双方向通信処理を作ることができます。

以前、Next.js(React)を使ってチャットアプリを作る記事を投稿しましたが、
今回はJavaScriptによる双方向通信処理で、フォームの同時編集機能を作ってみます。

DEMO

テキスト入力欄にカーソルを入れると、他者画面に「他のメンバーが入力中」と表示します。
カーソルを外すとメッセージを送受信し、他者画面にメッセージの内容が反映されます。
入力状況や入力文字を、リアルタイムにWebSocketで通信しています。

サンプルコード
index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>Scaledrone WebSocket Example</title>
        <script src="https://cdn.scaledrone.com/scaledrone.min.js"></script>
        <script src="websocket-example.js"></script>
        <link rel="stylesheet" type="text/css" href="style.css">
    </head>

    <body>
        <textarea id="textarea" placeholder="メッセージを入力"></textarea>
        <div id="statusarea"></div>
    </body>
</html>
websocket-example.js
'use strict';

const CHANNEL_ID = '{チャンネルID}';
const ROOM_NAME = '{任意のルーム名}';
const drone = new Scaledrone(CHANNEL_ID);

const TEXTAREA_ID = 'textarea';
const STATUSAREA_ID = 'statusarea';
let inputElement;
let editingStatusElement;

document.addEventListener('DOMContentLoaded', init);

function init() {
    inputElement = document.getElementById(TEXTAREA_ID);
    inputElement.addEventListener('focus', handleFocusIn);
    inputElement.addEventListener('focusout', handleFocusOut);
    editingStatusElement = document.getElementById(STATUSAREA_ID);

    initWebSocket();
}

function initWebSocket() {
    drone.on('open', handleWebSocketOpen);
    drone.on('error', handleWebSocketError);

    const room = drone.subscribe(ROOM_NAME);
    room.on('open', handleRoomOpen);
    room.on('message', receiveMessage);
}

function handleWebSocketOpen(error) {
    if (error) {
        return console.error(error);
    }
}

function handleRoomOpen(error) {
    if (error) {
        return console.error(error);
    }

    console.log('Connected to room');
}

function handleWebSocketError(error) {
    console.error('Error with connection:', error);
}

function handleFocusIn() {
    sendMessage(inputElement.value, true);
}

function handleFocusOut() {
    sendMessage(inputElement.value, false);
}

function sendMessage(text, isInputting) {
    drone.publish({
        room: ROOM_NAME,
        message: {text, isInputting}
    });
}

function receiveMessage(message) {
    let statusText = '';
 
    if (!message.data.isInputting) {
        inputElement.value = message.data.text;
        inputElement.classList.remove('inputting');
        editingStatusElement.textContent = statusText;
        return;
    }

    inputElement.classList.add('inputting');
    if (message.clientId !== drone.clientId) {
        statusText = '※他のメンバーが入力中';
    }
    editingStatusElement.textContent = statusText;
}
style.css
#textarea {
    font-size: 18px;
    height: 100px;
    width: 400px;
}

#textarea.inputting {
    background-color: #ffffee;
}

#statusarea {
    color: #ff6666;
}

解説

主にWebSocketの利用に関わる設定や処理を、抜粋してご紹介します。

【1】Scaledroneの準備

Scaledroneのアカウントを作成し、メッセージ中継用のチャンネルを作成します。

  1. scaledrone.comの「CREATE ACCOUNT」からアカウントを作成します。
  2. ログインし、Scaledroneのダッシュボードにアクセスします。
  3. 「+Create channel」ボタンをクリックします。
  4. 以下を入力して、チャンネルを作成します。
    • チャンネル名(Choose a name): 任意
    • 認証(Authentication): 不要(Never require authentication)
    • メッセージ履歴(Message history): 無効(Disable message history)
  5. 「Channel overview」画面に表示されるチャンネルIDを、コピーして控えます。

【2】Scaledroneライブラリの読み込み

Scaledroneのライブラリを、CDNから読み込みます。

<script src='https://cdn.scaledrone.com/scaledrone.min.js'></script>

【3】初期化と接続開始

Scaledroneで作成したチャンネルのIDと任意のルーム名を設定した後、
Scaledroneのチャンネル、及びルームに接続します。
WebSocketのメッセージはルーム宛に送信し、ルーム参加者に共有されます。

const CHANNEL_ID = '{チャンネルID}';
const ROOM_NAME = '{任意のルーム名}';

const drone = new Scaledrone(CHANNEL_ID);

function initWebSocket() {
    drone.on('open', handleWebSocketOpen);
    drone.on('error', handleWebSocketError);

    const room = drone.subscribe(ROOM_NAME);
    room.on('open', handleRoomOpen);
    room.on('message', receiveMessage);
}

openで接続成功時、errorで接続失敗時、
messageでメッセージ受信時のイベントを定義します。

また、テキスト入力欄にカーソルを入れた時と、外した時にメッセージを送信したいので、
addEventListenerでfocusfocusoutのイベントを設定しておきます。

function init() {
    inputElement = document.getElementById(TEXTAREA_ID);
    inputElement.addEventListener('focus', handleFocusIn);
    inputElement.addEventListener('focusout', handleFocusOut);
    editingStatusElement = document.getElementById(STATUSAREA_ID);

    initWebSocket();
}

【4】メッセージの送信処理

カーソルを入れた時はhandleFocusIn()で「入力中」という状況を送信し、
カーソルを外した時はhandleFocusOut()で「非入力中」の状況と入力文字を送信します。

function handleFocusIn() {
    sendMessage(inputElement.value, true);
}

function handleFocusOut() {
    sendMessage(inputElement.value, false);
}

function sendMessage(text, isInputting) {
    drone.publish({
        room: ROOM_NAME,
        message: {text, isInputting}
    });
}

送信する内容は構造体で自由に定義可能で、publish()で送信をおこないます。超シンプル♪

【5】メッセージの受信処理

メッセージ受信イベントとして定義したreceiveMessage()に、
受信メッセージの内容を画面に反映させる処理を記述します。
今回は、以下の処理を書きました。

  • 入力中の場合
    • テキスト入力欄をCSSで着色する。
    • 他者が入力中の場合、「他のメンバーが入力中」と表示する。
  • 入力が終わった場合
    • テキスト入力欄の値を、受信した内容で書き換える。
    • 着色のCSSを外し、テキスト入力欄を白に戻す。
    • 「他のメンバーが入力中」の表示を消す。
function receiveMessage(message) {
    let statusText = '';
 
    if (!message.data.isInputting) {
        inputElement.value = message.data.text;
        inputElement.classList.remove('inputting');
        editingStatusElement.textContent = statusText;
        return;
    }

    inputElement.classList.add('inputting');
    if (message.clientId !== drone.clientId) {
        statusText = '※他のメンバーが入力中';
    }
    editingStatusElement.textContent = statusText;
}

チャンネル接続時に自動発行される自分のクライアントIDdrone.clientId
メッセージ送信者のクライアントIDmessage.clientIdを比較し、
他者が送信したメッセージかどうかを判断しています。

また、受信メッセージの本文は、message.dataから取得しています。
その他、メッセージのユニークIDはmessage.id
送信日時はmessage.timestampで取得し、利用することもできます。

おわりに

DEMOでは2画面での操作でしたが、3名以上でも同期可能です。
「〇〇さんが入力中」と名前を表示させると、より同時編集の状況が分かりやすいですね。

WebSocket用のサーバーを構築せず、お手軽にリアルタイムの同期処理を実装できるので
やはりScaledroneは非常に便利なサービスでした。
以下が、JavaScriptで実装する場合の公式ドキュメントです。

Java、Swift、React Native、Node.js、Go、Ruby、Python、PHP、RESTの
ライブラリやAPIクライアントも用意されているので、
また別の機会でもWebSocketを試してみたいと思います。

https://www.scaledrone.com/docs/

コラボスタイル Developers

Discussion