Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
色字體,選擇“標(biāo)星公眾號(hào)”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
屏幕錄制?截屏?網(wǎng)頁(yè)生成圖片?幀圖?說到錄屏,我一開始想到的是前面這些詞。大致的想法是持續(xù)的生成當(dāng)前頁(yè)面的截圖,然后把這些幀圖再合并成一個(gè)視頻文件。前端頁(yè)面生成圖片我們應(yīng)該比較熟悉的是html2canvas。另外也有一些現(xiàn)成的庫(kù)可以使用來進(jìn)行屏幕的錄制,RecordRTC上就有很多屏幕錄制的實(shí)現(xiàn)。有聲音(Audio)、視頻(Video)、屏幕(Screen)的錄制;有針對(duì)canvas的錄制等等,一共有三十多個(gè)示例。這里主要想簡(jiǎn)單的講一講原生的 Screen Capture API。參見:Using the Screen Capture API
navigator.mediaDevices.getDisplayMedia
該方法會(huì)返回一個(gè)promise, 該promise會(huì)resolve當(dāng)前屏幕內(nèi)容的實(shí)時(shí)數(shù)據(jù)流。
使用 async / await 實(shí)現(xiàn)如下:
async function startCapture(displayMediaOptions) {
let captureStream = ;
try {
captureStream = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
} catch(err) {
console.error("Error: " + err);
}
return captureStream;
}
使用 promise 的方式實(shí)現(xiàn)如下:
function startCapture(displayMediaOptions) {
let captureStream = ;
return navigator.mediaDevices.getDisplayMedia(displayMediaOptions)
.catch(err => { console.error("Error:" + err); return ; });
}
我們?cè)讷@取屏幕數(shù)據(jù)的時(shí)候有可能會(huì)獲取到一些敏感信息,所有在使用getDisplayMedia的時(shí)候,為了安全考慮,會(huì)彈出一個(gè)選擇框,然用戶自己選擇需要共享那一部分的內(nèi)容。可以共享當(dāng)前屏幕,也可以共享其他的應(yīng)用窗口和瀏覽器的其他標(biāo)簽頁(yè)。
二、參數(shù)配置:
我們?cè)谏厦娴膶?shí)現(xiàn)中可以看到, 傳遞給startCapture函數(shù)的參數(shù)為displayMediaOptions。這個(gè)參數(shù)是用于配置返回?cái)?shù)據(jù)流的。數(shù)據(jù)形式如下:
const displayMediaOptions = {
video: {
cursor: "never"
},
audio: false,
logicalSurface: false,
};
開可以針對(duì)音視頻做詳細(xì)的配置:
const gdmOptions = {
video: {
cursor: "always" // 始終顯示鼠標(biāo)信息
},
// audio 配置信息是可選的
audio: {
echoCancellation: true,
noiseSuppression: true,
sampleRate: 44100
}
}
三、示例
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Screen Record</title>
<link rel="stylesheet" href="./css/index.css">
</head>
<body>
<p>This example shows you the contents of the selected part of your display.
Click the Start Capture button to begin.</p>
<p><button id="start">Start Capture</button> <button id="stop">Stop Capture</button></p>
<video id="video" autoplay></video>
<br>
<strong>Log:</strong>
<br>
<pre id="log"></pre>
<script src="./js/index.js"></script>
</body>
</html>
CSS:
#video {
border: 1px solid #999;
width: 98%;
max-width: 860px;
}
.error {
color: red;
}
.warn {
color: orange;
}
.info {
color: darkgreen;
}
JS:
const videoElem = document.getElementById("video");
const logElem = document.getElementById("log");
const startElem = document.getElementById("start");
const stopElem = document.getElementById("stop");
const displayMediaOptions = {
video: {
cursor: "never"
},
audio: false
};
startElem.addEventListener("click", function(evt) {
startCapture;
}, false);
stopElem.addEventListener("click", function(evt) {
stopCapture;
}, false);
console.log = msg => logElem.innerHTML += `${msg}<br>`;
console.error = msg => logElem.innerHTML += `<span class="error">${msg}</span><br>`;
console.warn = msg => logElem.innerHTML += `<span class="warn">${msg}<span><br>`;
console.info = msg => logElem.innerHTML += `<span class="info">${msg}</span><br>`;
async function startCapture {
logElem.innerHTML = "";
try {
videoElem.srcObject = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
dumpOptionsInfo;
} catch(err) {
console.error("Error: " + err);
}
}
function stopCapture(evt) {
let tracks = videoElem.srcObject.getTracks;
tracks.forEach(track => track.stop);
videoElem.srcObject = ;
}
function dumpOptionsInfo {
const videoTrack = videoElem.srcObject.getVideoTracks[0];
console.info("Track settings:");
console.info(JSON.stringify(videoTrack.getSettings, , 2));
console.info("Track constraints:");
console.info(JSON.stringify(videoTrack.getConstraints, , 2));
}
效果如下:
點(diǎn)擊Start Capture 之后選擇需要共享的部分就可以共享如下的內(nèi)容:
點(diǎn)擊Stop Capture即可˙停止錄制共享。
這個(gè)例子只是調(diào)取接口獲取到當(dāng)前分享屏幕的數(shù)據(jù)流,并通過video的形式顯示出來。我們?cè)谀玫綌?shù)據(jù)流信息這個(gè),可以把這些信息上傳到服務(wù)器,生成相應(yīng)的視頻文件。也可以結(jié)合websocket之類的處理方式,實(shí)現(xiàn)實(shí)時(shí)的屏幕共享功能。
作者:飯等米
鏈接:segmentfault.com/a/1190000020267689
如果喜歡本篇文章,歡迎。關(guān)注訂閱號(hào)「Web項(xiàng)目聚集地」,回復(fù)「進(jìn)群」即可進(jìn)入無廣告技術(shù)交流。
1.2. 4. 5.6.
件傳遞有兩種方式,冒泡和捕獲。事件傳遞定義了元素事件觸發(fā)的順序,在冒泡中,內(nèi)部元素的事件會(huì)先被觸發(fā),然后在觸發(fā)外部元素,如先觸發(fā)p元素然后觸發(fā)div元素。在捕獲事件中,外部元素先被觸發(fā),然后內(nèi)部事件觸發(fā)。addEventListener() 方法可以指定 "useCapture" 參數(shù)來設(shè)置傳遞類型:addEventListener(event, function, useCapture);
創(chuàng)建新的HTML元素(節(jié)點(diǎn))appendChild()
HTML Collection與NodeList的區(qū)別:NodeList 與 HTMLCollection 有很多類似的地方。NodeList 與 HTMLCollection 都與數(shù)組對(duì)象有點(diǎn)類似,可以使用索引 (0, 1, 2, 3, 4, ...) 來獲取元素。NodeList 與 HTMLCollection 都有 length 屬性。節(jié)點(diǎn)列表不是一個(gè)數(shù)組!節(jié)點(diǎn)列表無法使用數(shù)組的方法: valueOf(), pop(), push(), 或 join() 。
件流:
事件冒泡
取消冒泡:oEvent.cancelBubble=true
<html>
<head>
<meta charset="utf-8">
<title>無標(biāo)題文檔</title>
<style>
#div1 {width:400px; height:300px; background:#CCC; display:none;}
</style>
<script>
window.onload=function ()
{
var oBtn=document.getElementById('btn1');
var oDiv=document.getElementById('div1');
oBtn.onclick=function (ev)
{
var oEvent=ev||event;
oDiv.style.display='block';
//alert('按鈕被點(diǎn)擊了');
oEvent.cancelBubble=true; //取消事件冒泡,是解決許多問題的方法和手段
};
document.onclick=function ()
{
oDiv.style.display='none';
//alert('document被點(diǎn)擊了');
};
};
</script>
</head>
<body>
<input id="btn1" type="button" value="顯示" />
<div id="div1">
</div>
</body>
</html>
冒泡型事件:
<html>
<head>
<title>冒泡型事件</title>
<script language="javascript">
function add(sText){
var oDiv = document.getElementById("display");
oDiv.innerHTML += sText; //輸出點(diǎn)擊順序
}
</script>
</head>
<body onclick="add('body<br>');">
<div onclick="add('div<br>');">
<p onclick="add('p<br>');">Click Me</p>
</div>
<div id="display"></div>
</body>
</html>
執(zhí)行順序:p對(duì)象 -> div對(duì)象 -> body對(duì)象
冒泡型事件執(zhí)行順序::由內(nèi)到外(p -> div -> body -> document)
注意: DOM 0級(jí)只有冒泡, 沒有捕獲
捕獲型事件
相對(duì)于IE使用冒泡型事件, Netscape使用了另一種稱為捕獲型事件(eventcapturing)的解決方案;
addEventListener(事件名稱,函數(shù), bCapture)
removeEventListener(事件名稱, 函數(shù), bCapture)
事件監(jiān)聽函數(shù)第三個(gè)參數(shù)bCapture確定是冒泡型還是捕獲型事件(true:捕獲 false:冒泡,默認(rèn)值false)
<!DOCTYPE html>
<html>
<head>
<style>
div {
background-color: coral;
border: 1px solid;
padding: 50px;
}
</style>
</head>
<body>
<div id="myDiv2">
<p id="myP2">點(diǎn)擊該段落, 我是捕獲</p>
</div>
<script>
document.getElementById("myP2").addEventListener("click", function() {
alert("你點(diǎn)擊了 P 元素!");
}, true);
document.getElementById("myDiv2").addEventListener("click", function() {
alert("你點(diǎn)擊了 DIV 元素!");
}, true);
</script>
</body>
</html>
執(zhí)行順序: div對(duì)象 -> p對(duì)象
捕獲型事件執(zhí)行順序:由外到內(nèi)(如:document -> body -> div -> p)
冒泡型:事件從內(nèi)部往外部依次執(zhí)行。
捕捉型:事件從外部往內(nèi)部依次執(zhí)行。
事件監(jiān)聽
通用監(jiān)聽方法:
1.直接在HTML標(biāo)簽中分配事件處理函數(shù):
<script language="javascript">
function add(sText){
var oDiv = document.getElementById("display");
oDiv.innerHTML += sText; //輸出點(diǎn)擊順序
}
</script>
</head>
<body onclick="add('body<br>');">
<div onclick="add('div<br>');">
<p onclick="add('p<br>');">Click Me</p>
</div>
<div id="display"></div>
</body>
2.結(jié)構(gòu)與行為的分離:
<html>
<head>
<title>監(jiān)聽函數(shù)</title>
<script language="javascript">
window.onload = function(){
var oP = document.getElementById("myP"); //找到對(duì)象
oP.onclick = function(){ //設(shè)置事件監(jiān)聽函數(shù)
alert('我被點(diǎn)擊了');
}
}
</script>
</head>
<body>
<div>
<p id="myP">Click Me</p>
</div>
</body>
</html>
事件監(jiān)聽的作用:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>無標(biāo)題文檔</title>
<script>
window.onload=function ()
{
alert('a');
};
window.onload=function ()
{
alert('b');
};
</script>
</head>
<body>
</body>
</html>
以上程序只彈出alert('b');
理解:對(duì)同一對(duì)象執(zhí)行兩次事件(同一事件類型)處理函數(shù)時(shí),往往只能只執(zhí)行后一個(gè)
因此如果要讓腳本在本瀏覽器中正常運(yùn)行的話,就必須使用瀏覽器所支持的事件監(jiān)聽器
IE中的監(jiān)聽方法:
attachEvent(事件名稱, 函數(shù)), 綁定事件處理函數(shù) attach: 貼上, 附著
detachEvent(事件名稱, 函數(shù)), 解除綁定 detach: 分離, 拆開
注意:IE只支持冒泡型事件監(jiān)聽, 沒有第三個(gè)參數(shù), 事件名稱前要加"on";
<html>
<head>
<title>多個(gè)監(jiān)聽函數(shù)</title>
<script language="javascript">
function fnClick1(){
alert("我被fnClick1點(diǎn)擊了");
}
function fnClick2(){
alert("我被fnClick2點(diǎn)擊了");
//oP.detachEvent("onclick",fnClick1); //刪除監(jiān)聽函數(shù)1
}
var oP;
window.onload = function(){
oP = document.getElementById("myP"); //找到對(duì)象
oP.attachEvent("onclick",fnClick1); //添加監(jiān)聽函數(shù)1
oP.attachEvent("onclick",fnClick2); //添加監(jiān)聽函數(shù)2
}
</script>
</head>
<body>
<div>
<p id="myP">Click Me</p>
</div>
</body>
</html>
標(biāo)準(zhǔn)DOM的事件監(jiān)聽:
addEventListener(事件名稱,函數(shù), 捕獲)
element.addEventListener(event, function, useCapture)
removeEventListener(事件名稱, 函數(shù), 捕獲)
element.removeEventListener(event, function, useCapture)
bCapture 是用于冒泡階段還是捕獲階段(true:捕獲 false:冒泡,默認(rèn)值false)
注意:IE9版本已支持
<html>
<head>
<title>標(biāo)準(zhǔn)DOM的事件監(jiān)聽</title>
<script language="javascript">
function fnClick1(){
alert("我被fnClick1點(diǎn)擊了");
//oP.removeEventListener("click",fnClick2,false); //刪除監(jiān)聽函數(shù)2
}
function fnClick2(){
alert("我被fnClick2點(diǎn)擊了");
}
var oP;
window.onload = function(){
oP = document.getElementById("myP"); //找到對(duì)象
oP.addEventListener("click",fnClick1,false); //添加監(jiān)聽函數(shù)1
oP.addEventListener("click",fnClick2,false); //添加監(jiān)聽函數(shù)2
}
</script>
</head>
<body>
<div>
<p id="myP">Click Me</p>
</div>
</body>
</html>
編寫兼容性事件監(jiān)聽函數(shù)
function addEvent(obj, ev, fn){
if(obj.addEventListener){
obj.addEventListener(ev, fn, false);
}else{
obj.attachEvent('on'+ev, fn);
}
}
function removeEvent(obj, ev, fn){
if(obj.removeEventListener){
obj.addEventListener(ev, fn, false);
}else{
obj.attachEvent('on'+ev, fn);
}
}
實(shí)例:
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。