WeChatなどのソーシャルアプリの台頭により、音声チャットは、音声チャットを主な機能とするソーシャルアプリから、音声カスタマーサービスや店舗ウェイター機能を備えた電子商取引アプリに至るまで、多くのアプリにとって必須の機能となっています。チャットは欠かせないものになっていますね。
しかし、Web 側の音声は遠いものであり、ローカル アプリケーションの仕事であると多くの人が感じていますが、実際にはそうではありません。HTML5 の開発により、音声機能は徐々に機能の 1 つになりました。フロントエンドに必須の機能。
なぜ HTML5 音声を学ぶ必要があるのでしょうか?
1. HTML5の 仕様が進み、携帯電話のアップデートによりOSの更新が加速しており、音声機能は現在のキャンバスと同様にフロントエンドの主要なタスクの1つになるでしょう。音声機能開発のフロントエンド実装はより速く、より多くの人的資源を節約します (これは上司のためにお金を節約することを意味し、上司のためにお金を節約することは自分の給料を増やすことになります)
2. ローカル アプリケーションに音声機能が備わっている場合でも、フロントエンドの音声対話のさまざまな落とし穴をよく理解しておくと、同僚同士が争うのではなく、より調和のとれた関係を築き、よりスムーズなコラボレーションを実現できるようになります。
3. 新しいテクノロジーを理解することで、一方では面接官の質問に圧倒されることを防ぐことができ、他方ではテクノロジーのトレンドを予測して、ドラゴン退治のスキルを大量に習得したり、やり方に固執したりすることを避けることができます。このルールは、知識と専門的なコア競争力を維持するのに役立ちます。
4. フロントエンドの多くの人は、音声機能について誤解を持っており、音声機能は単なる HTML5 の audio タグであると考えていますが、実際には、それほど単純な「だけ」ではありません。
インクの量はそれほど多くなく、小さなプロジェクトを開発するだけで、すべてが明確になります。最初にレンダリングを見てみましょう。
HTML5 マルチメディアを使用してWeChat 音声機能を実装する
ビジネスロジックは非常にシンプルで、
WeChatのやり方と全く同じで、押し続けると言葉が離れて終了し、音声が録音され、手を離すと押し続けて終了し、音声が相手に送信されます。同時に相手も。
まずは HTML ページを完成させましょう。
<!DOCTYPE html>
<html>
<頭>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width、initial-scale=1.0">
<meta http-equiv="X-UA 互換" content="ie=edge">
<title>WeChat 音声</title>
<link rel="stylesheet" href="css/record.css">
</head>
<本文>
<div id="ラップ">
<ヘッダー id="ヘッダー">
<div id="左">
<i class="material-icons">
シェブロン_左
</i>
微信(184)
</div>
<div id="mid">エイダ・ワン</div>
<div id="right">
<i class="material-icons">
もっと見る
</i>
</div>
</ヘッダ>
<div id="contentWrap">
<ul id="チャットリスト">
<li class="item_me">
<div class="chatContent">あなたが一番愛しているのは私ですか?
<span class="bot"></span>
<span class="top"></span>
</div>
<div class="アバター">
<img src="images/ava1.png" alt="">
</div>
</li>
<li class="item_you">
<div class="アバター">
<img src="images/ava2.jpg" alt="">
</div>
<div class="chatContent">兄さん、逃げてください!(怒る)
<span class="bot"></span>
<span class="top"></span>
</div>
</li>
<li class="item_me">
<div class="chatContent">ここでは多くは言いません。コードの束だけです...
<span class="bot"></span>
<span class="top"></span>
</div>
<div class="アバター">
<img src="images/ava1.png" alt="">
</div>
</li>
<li class="item_you">
<div class="アバター">
<img src="images/ava2.jpg" alt="">
</div>
<div class="chatContent">ダビン兄弟、なぜ自分はそんなに優れていると思いますか? あなたを見ると海を感じる
<span class="bot"></span>
<span class="top"></span>
</div>
</li>
<li class="item_me">
<div class="chatContent">お嬢さん、私に恋をしてしまったのでしょうか...
<span class="bot"></span>
<span class="top"></span>
</div>
<div class="アバター">
<img src="images/ava1.png" alt="">
</div>
</li>
<li class="item_you">
<div class="アバター">
<img src="images/ava2.jpg" alt="">
</div>
<div class="chatContent">いいえ、船酔いしてあなたを見ると吐きそうになります...
<span class="bot"></span>
<span class="top"></span>
</div>
</li>
</ul>
</div>
<フッター id="フッター">
<div id="キーボード">
<i class="material-icons">
キーボード
</i>
</div>
<div id="sayBtn">
<span id="sendBtn" class="sendBtn">押して話してください</span>
</div>
<div id="icon"><i class="material-icons">
センチメント満足
</i></div>
<div id="add"><i class="material-icons">
add_circle_outline
</i></div>
</フッター>
</div>
</body>
</html>
CSS部分、
*{
マージン: 0;
パディング: 0;
}
ul li{ リストスタイル: なし;}
html,ボディ{
高さ: 100%;
幅: 100%;
オーバーフロー: 非表示;
}
体{
背景: #ebebeb;
}
@フォントフェイス {
font-family: 'マテリアルアイコン';
フォントスタイル: 通常;
フォントの太さ: 400;
src: url(../css/iconfont/マテリアルアイコン-レギュラー.eot); /* IE6~8の場合 */
src: local('マテリアル アイコン'),
local('マテリアルアイコン-レギュラー'),
url(../css/iconfont/マテリアルアイコン-レギュラー.woff) 形式('woff2')、
url(../css/iconfont/マテリアルアイコン-レギュラー.woff2) フォーマット('woff'),
url(../css/iconfont/materialIcons- Regular.ttf) format('truetype');
}
.material-icons {
font-family: 'マテリアルアイコン';
フォントの太さ: 通常;
フォントスタイル: 通常;
フォントサイズ: 32px; /* 推奨アイコン サイズ */
表示: インラインブロック;
/* 行の高さ: 0.01rem; */
テキスト変換: なし。
文字間隔: 通常;
ワードラップ: 通常;
ホワイトスペース: ナラップ;
方向: ltr;
/* すべての WebKit ブラウザをサポートします。*/
-webkit-font-smoothing: アンチエイリアス;
/* Safari と Chrome のサポート。*/
テキストレンダリング:読みやすさの最適化;
/* Firefox のサポート。*/
-moz-osx-font-smoothing: グレースケール;
/* IE のサポート。*/
フォント機能設定: 'liga';
}
#包む{
ディスプレイ: フレックス;
フレックス方向: 列;
コンテンツの位置揃え: 間のスペース;
高さ: 100%;
}
#ヘッダ{
高さ: 46px;
行の高さ: 46px;
背景: #363539;
ディスプレイ: フレックス;
整列項目: 中央;
色: #fff;
コンテンツの位置揃え: 間のスペース;
}
#ヘッダー #左{
ディスプレイ: フレックス;
整列項目: 中央;
フォントサイズ: 14px;
幅: 100ピクセル;
}
#ヘッダー #右{
ディスプレイ: フレックス;
整列項目: 中央;
幅: 100ピクセル;
justify-content: フレックスエンド;
}
#ヘッダー #右私{
パディング右: 6px;
}
#ヘッダー #mid{
テキスト整列: 中央;
フレックス: 1;
}
#コンテンツラップ{
フレックス: 1;
オーバーフロー-y:自動;
}
.item_me、.item_audio{
ディスプレイ: フレックス;
align-items: flex-start;
justify-content:flex-end;
パディング: 8px;
}
.item_you{
ディスプレイ: フレックス;
align-items: flex-start;
justify-content:flex-start;
パディング: 8px;
}
.アバター{
幅: 40ピクセル;
高さ: 40ピクセル;
}
.avatar img{幅: 100%;}
.item_me .chatContent{
パディング: 10px;
背景: #a0e75a;
ボーダー: 1px ソリッド #6fb44d;
マージン右: 15px;
境界半径: 5px;
位置: 相対的;
}
.chatContent スパン{幅:0; 高さ:0; フォントサイズ:0; オーバーフロー:非表示; 位置:絶対;}
.item_me .chatContentspan.bot{
ボーダー幅:8px;
境界線のスタイル:実線 破線 破線;
境界線の色: 透明 透明 透明 #6fb44d;
右:-17px;
上:10ピクセル;
}
.item_me .chatContentspan.top{
ボーダー幅:8px;
境界線のスタイル:実線 破線 破線;
境界線の色:透明 透明 透明 #a0e75a ;
右:-15px;
上:10ピクセル;
}
.item_you .chatContent{
パディング: 10px;
背景: #a0e75a;
ボーダー: 1px ソリッド #6fb44d;
左マージン: 15px;
境界半径: 5px;
位置: 相対的;
}
.item_you .chatContentspan.bot{
ボーダー幅:8px;
境界線のスタイル:実線 破線 破線;
境界線の色: 透明 #6fb44d 透明 透明 ;
左:-17px;
上:10ピクセル;
}
.item_you .chatContentspan.top{
ボーダー幅:8px;
境界線のスタイル:実線 破線 破線;
境界線の色:透明 #a0e75a 透明 透明 ;
左:-15px;
上:10ピクセル;
}
#フッター{
高さ: 46px;
パディング: 0 4px;
背景: #f4f5f6;
ボーダートップ: 1px ソリッド #d7d7d8;
ディスプレイ: フレックス;
整列項目: 中央;
色: #7f8389;
コンテンツの位置揃え: 周囲のスペース;
}
#sayBtn{
フレックス: 1;
ディスプレイ: フレックス;
マージン: 0 5px;
色:#565656;
フォントの太さ: 太字;
}
.sendBtn{
表示ブロック;
フレックス: 1;
パディング: 8px;
背景: #f4f5f6;
ボーダー:1px ソリッド #bec2c1;
境界半径: 5px;
テキスト整列: 中央;
}
.activeBtn{
表示ブロック;
フレックス: 1;
パディング: 8px;
背景: #c6c7ca;
ボーダー:1px ソリッド #bec2c1;
境界半径: 5px;
テキスト整列: 中央;
}
.item_audio .chatContent{
パディング: 6px;
背景: #fff;
ボーダー: 1px ソリッド #999;
境界半径: 5px;
マージン右: 15px;
位置: 相対的;
幅:120ピクセル;
最小高さ: 20px;
}
.item_audio .chatContentspan.bot{
ボーダー幅:8px;
境界線のスタイル:実線 破線 破線;
ボーダーカラー: 透明 透明 透明 #999;
右:-17px;
上:10ピクセル;
}
.item_audio .chatContentspan.top{
ボーダー幅:8px;
境界線のスタイル:実線 破線 破線;
ボーダーカラー:透明 透明 透明 #fff ;
右:-15px;
上:10ピクセル;
}
.material-icons_wifi{
変換: 回転(90度);
色: #a5a5a5;
フォントサイズ: 22px;
}
。赤い点{
背景: #f45454;
境界半径: 50%;
幅: 8px;
高さ: 8px;
右マージン: 10px;
}
ここで注意点を2つ挙げたいと思います。
1.html部分:
トラブルを避けるために、画像をピクセル レベルで切り取らず、SVG アイコンを直接使用しました。使用したライブラリは次のとおりです。
https://material.io/tools/icons/?style=outline
2.CSS部分:フレックスレイアウトを使用します。Html5の機能を説明したいだけなので、flexでは互換性の記述方法は書いていません また、よく使われるAppヘッダ部分の記述方法にも注目してください。
重要な js 部分について話しましょう。
<!DOCTYPE html>
<html>
<頭>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width、initial-scale=1.0">
<meta http-equiv="X-UA 互換" content="ie=edge">
<title>WeChat 音声</title>
<link rel="stylesheet" href="css/record.css">
<スクリプト>
document.addEventListener('DOMContentLoaded', function () {
var oSendBtn = document.getElementById('sendBtn');
var soundClips = document.querySelector('.sound-clips');
var mediaRecorder;
var oChatList = document.getElementById('chatList');
navigator.getUserMedia = (navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia(
// 制約 - このアプリに必要な音声のみ
{
オーディオ: true
})
// 成功のコールバック
.then(関数 (ストリーム) {
記録(ストリーム);
})
// エラーコールバック
.catch(関数 (エラー) {
}
);
} それ以外 {
}
関数rec(ストリーム) {
mediaRecorder = 新しい MediaRecorder(ストリーム);
oSendBtn.addEventListener('touchstart', function (ev) {
ev.preventDefault();
this.innerHTML = '公開終了';
this.classList.add('activeBtn');
mediaRecorder.start();
}、 間違い);
oSendBtn.addEventListener('touchend', function (ev) {
ev.preventDefault();
this.innerHTML = '押して話します';
this.classList.remove('activeBtn');
mediaRecorder.stop();
}、 間違い);
mediaRecorder.ondataavailable = 関数 (e) {
var ClipContainer = document.createElement('li');
var audio = document.createElement('audio');
ClipContainer.classList.add('item_audio');
ClipContainer.innerHTML = `
<div class = "redDot"></div>
<div class="チャットコンテンツ">
<i class="material-iconsmaterial-icons_wifi">Wi-Fi</i>
<span class="bot"></span>
<span class="top"></span>
</div>
<div class="アバター">
<img src="images/ava1.png" alt="">
</div>`;
audio.setAttribute('コントロール', '');
oChatList.appendChild(clipContainer);
var audioURL = window.URL.createObjectURL(e.data);
audio.src = オーディオURL;
oChatList.addEventListener('touchstart', function (ev) {
if (ev.srcElement.parentNode.className!== 'item_audio') return;
audio.play();
ev.srcElement.parentNode.removeChild(ev.srcElement.parentNode.children[0])
}、 間違い);
};
}
}、 間違い);
</script>
</head>
<本文>
<div id="ラップ">
<ヘッダー id="ヘッダー">
<div id="左">
<i class="material-icons">
シェブロン_左
</i>
微信(184)
</div>
<div id="mid">エイダ・ワン</div>
<div id="right">
<i class="material-icons">
もっと見る
</i>
</div>
</ヘッダ>
<div id="contentWrap">
<ul id="チャットリスト">
<li class="item_me">
<div class="chatContent">あなたが一番愛しているのは私ですか?
<span class="bot"></span>
<span class="top"></span>
</div>
<div class="アバター">
<img src="images/ava1.png" alt="">
</div>
</li>
<li class="item_you">
<div class="アバター">
<img src="images/ava2.jpg" alt="">
</div>
<div class="chatContent">兄さん、逃げてください!(怒る)
<span class="bot"></span>
<span class="top"></span>
</div>
</li>
<li class="item_me">
<div class="chatContent">ここでは多くは言いません。コードの束だけです...
<span class="bot"></span>
<span class="top"></span>
</div>
<div class="アバター">
<img src="images/ava1.png" alt="">
</div>
</li>
<li class="item_you">
<div class="アバター">
<img src="images/ava2.jpg" alt="">
</div>
<div class="chatContent">ダビン兄弟、なぜ自分はそんなに優れていると思いますか? あなたを見ると海を感じる
<span class="bot"></span>
<span class="top"></span>
</div>
</li>
<li class="item_me">
<div class="chatContent">お嬢さん、私に恋をしてしまったのでしょうか...
<span class="bot"></span>
<span class="top"></span>
</div>
<div class="アバター">
<img src="images/ava1.png" alt="">
</div>
</li>
<li class="item_you">
<div class="アバター">
<img src="images/ava2.jpg" alt="">
</div>
<div class="chatContent">いいえ、船酔いしてあなたを見ると吐きそうになります...
<span class="bot"></span>
<span class="top"></span>
</div>
</li>
</ul>
</div>
<フッター id="フッター">
<div id="キーボード">
<i class="material-icons">
キーボード
</i>
</div>
<div id="sayBtn">
<span id="sendBtn" class="sendBtn">押して話してください</span>
</div>
<div id="icon"><i class="material-icons">
センチメント満足
</i></div>
<div id="add"><i class="material-icons">
add_circle_outline
</i></div>
</フッター>
</div>
</body>
</html>
動画撮影機能を実装する際の注意点は数多くありますので、一つずつ説明していきます。
最初のもの、
navigator.getUserMedia = (navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia(
{
オーディオ: true
})
// 成功のコールバック
.then(関数 (ストリーム) {
記録(ストリーム);
})
// エラーコールバック
.catch(関数 (エラー) {
}
);
} それ以外 {
}
記録用の HTML5 インターフェイスをいくつか見ると、次のようになります。
Navigator.getUserMedia()
これは古い規格なので廃止されました。新しい規格は
navigator.mediaDevices.getUserMedia
HTML5マルチメディアの音声部分は何度か変更されていて非常に乱雑です、ブラウザに実装されていないタグもあり、開花する前に枯れてしまいました、気にする必要はありませんし、無駄にする必要もありません「時間があります。私の言うことを知っていればいいのです。これだけで十分です。捨てられた過去は何の役にも立たないことを知っているからです。時間があれば、LOL や King of Glory のゲームをしたほうがいいでしょう (私はやりませんが) 2 つの違いはわかりませんが、これら 2 つのゲームは楽しいはずです)。
中身を理解する必要はありません。Promise が何なのか、メディア ストリームが何なのかを知る必要もありません。ただ、この 1 つのことを知っておく必要があります。
上記のコードは、蛇口をひねる(またはレコーダーの録音ボタンを押す)ことと同じなので、水をキャッチする何かが必要になります。炊飯器(レコーダーの場合はテープ)を使用して、水を入れることができます。それを蛇口の下に置いて、入っていくのを見てください。次のコードを撮影してください。
mediaRecorder = 新しい MediaRecorder(ストリーム);
次のステップは、ボタンを押すことです。すべての準備が整います。対応するレコーダーは、録音後にボタンを押して再生することを意味します。ただし、プログラムで再生するには、テープだけでなくレコーダーも必要です. レコーダーはaudioタグですが、簡単にできるものがなければ新しく作成します。この世界には、プログラマがあえて新しくしないオブジェクトはありません。新しいオブジェクトが 1 つでは不十分な場合、新しいオブジェクトは 2 つだけです。コードの残りの部分には、恐ろしい以外に欠点はなく、単にとんでもないものです。
mediaRecorder.ondataavailable = 関数 (e) {
var ClipContainer = document.createElement('li');
var audio = document.createElement('audio');
ClipContainer.classList.add('item_audio');
ClipContainer.innerHTML = `
<div class = "redDot"></div>
<div class="チャットコンテンツ">
<i class="material-iconsmaterial-icons_wifi">Wi-Fi</i>
<span class="bot"></span>
<span class="top"></span>
</div>
<div class="アバター">
<img src="images/ava1.png" alt="">
</div>`;
audio.setAttribute('コントロール', '');
oChatList.appendChild(clipContainer);
var audioURL = window.URL.createObjectURL(e.data);
audio.src = オーディオURL;
oChatList.addEventListener('touchstart', function (ev) {
if (ev.srcElement.parentNode.className!== 'item_audio') return;
audio.play();
ev.srcElement.parentNode.removeChild(ev.srcElement.parentNode.children[0])
}、 間違い);
};
実は録画して放送されているんです。
OK、とても簡単ですか? プロジェクト全体に関するいくつかのポイントについて話しましょう。
1. 切断図に合理的な構造があることは、後で機能を実行するための前提条件です。構造がよくできていれば、後でトラブルを避けることができます。諸葛孔明を思い出してください。彼は駆け出しになる前に HTML5 構造を構築しました。 . 3 つのセクションがあります。
2. ネイティブ JS と ES6 の強固な基盤により、さまざまなアイデアが得られます。たとえば、ここではイベント委任と ES6 テンプレート エンジンを使用します。特にイベントデリゲーションは使わないとノードを探すのが面倒ですし、繰り返すとコードがめちゃくちゃになりやすいです。
3. 新しい知識や技術は実際には複雑ではなく、むしろ非常に単純です。新しい技術が機能を向上させたり、問題を解決したりするためではないのであれば、なぜ新しい技術を開発するのでしょうか? ひげを生やした大男たちは、何もすることがないのに上司から「仕事量が足りない」と言われるのを心配しているからでしょうか?テクノロジーは問題を解決し、私たちの生活をより良くするために存在します。
4. このプロジェクトは、IOS 11.2 以前ではサポートされておらず、IOS ローカル アプリケーション開発者がサポートを提供する必要があるため、IOS 11 以下では機能しませんが、Android では問題ありません。また、数年以内に、IOS はサポートを提供しなくてもネイティブにサポートされるようになることが予想されるため、開発効率は大幅に向上します。これらのテクノロジが遠い先の話だとは思わないでください。HTML5 が商用化されるまであと 15 年ほどかかります (vue、react、angular が大規模に使用されるようになるには何年かかりますか?)。準備ができている人。
プロジェクト全体にはまだ詳細や注意すべき点がたくさんあります。私の記事を理解していることと、このテクノロジーを使用する能力は別のことであるため、実際に自分で入力できることを願っています。皆さんがさらに前進することを願っています。さらにフロントエンドへの道を進んでください (頻繁に戻って読んでください ^_^ を参照してください)。
著作権は作者に属します: 51CTO ブロガー Bread Ideal のオリジナル作品です。転載する必要がある場合は、出典を明記してください。そうでない場合は、法的責任が追及されます。