1 wang編輯器效果圖
1、npm安裝
安裝過程比較簡單,不做重復(fù),說一下使用過程遇到的問題
插入hr
import E from 'wangeditor'
mounted () {
const editor = new E('#div1')
const menuKey = 'hrMenuKey'
const { BtnMenu } = E
// 第一,菜單 class ,Button 菜單繼承 BtnMenu class
class HrMenu extends BtnMenu {
constructor (editor) {
// data-title屬性表示當(dāng)鼠標(biāo)懸停在該按鈕上時(shí)提示該按鈕的功能簡述
const $elem = E.$(
`<div class="w-e-menu" data-title="分割線">
<i class='w-e-icon-split-line'></i>
</div>`
)
super($elem, editor)
}
// 菜單點(diǎn)擊事件
clickHandler () {
editor.cmd.do('insertHtml', '<hr>')
}
tryChangeActive () {
// 激活菜單
// 1. 菜單 DOM 節(jié)點(diǎn)會(huì)增加一個(gè) .w-e-active 的 css class
// 2. this.this.isActive === true
this.active()
// // 取消激活菜單
// // 1. 菜單 DOM 節(jié)點(diǎn)會(huì)刪掉 .w-e-active
// // 2. this.this.isActive === false
// this.unActive()
}
}
// 注冊(cè)菜單
E.registerMenu(menuKey, HrMenu)
editor.config.placeholder = ''
editor.config.uploadImgServer = '/public/sss/admin.php/ajaxweb/uppic.html'
editor.config.uploadImgMaxSize = 1024 * 1024
editor.config.uploadImgAccept = ['jpg', 'jpeg', 'png', 'gif']
editor.config.height = 300
editor.config.focus = true
editor.config.menus = [
'source',
'head',
'bold',
'fontSize',
'fontName',
'italic',
'underline',
'strikeThrough',
'indent',
'lineHeight',
'foreColor',
'backColor',
'link',
'list',
'justify',
'quote',
'image',
'video',
'table',
'undo',
'redo']
editor.create()
}
查看源碼
圖2 查看源碼效果圖
實(shí)現(xiàn)目標(biāo):
點(diǎn)擊查看的時(shí)候,遮蓋其它的按鈕,防止查看源碼的時(shí)候,點(diǎn)擊了別的按鈕進(jìn)行了誤操作。
新加的菜單默認(rèn)都是在最后全屏前邊,分割線還可以,但是查看源碼我個(gè)人還是習(xí)慣在最前邊,使用的是jquery prepend感覺更加簡單一些,代碼如下:
import $ from 'jquery'
mounted () {
$(document).ready(function () {
$('#div1 .w-e-toolbar').prepend('<div class=\'w-e-menu\' style=\'z-index:991;\' data-title=\'查看源碼\'><a style=\' display:block;width:100%;height:100%;\' ct=1 id=\'viewsource\'><i class=\'fa fa-file-text-o\'></i></a></div>')
$(document).delegate('#viewsource', 'click', function () {
var editorHtml = editor.txt.html()
// console.log($(this).attr('ct'))
if (parseInt($(this).attr('ct')) === 1) {
$('#div1 .w-e-toolbar').prepend('<div id=\'zzc\' style=\'position:absolute;left:0;top:0;z-index:99;background-color:rgba(0,0,0,0.5);width:100%;height:40px;\'></div>')
$(this).parent().parent().parent().find('.w-e-text').css('width', $('.w-e-text').width() + 'px')
editorHtml = editorHtml.replace(/</g, '<').replace(/>/g, '>').replace(/ /g, ' ')
$(this).attr('ct', '2')
$(this).css({'background-color': '#EEE'})
} else {
editorHtml = editor.txt.text().replace(/</ig, '<').replace(/>/ig, '>').replace(/ /ig, ' ')
$(this).attr('ct', '1')
$(this).parent().parent().parent().find('.w-e-text').css('width', '100%')
$(this).parent().parent().find('#zzc').remove()
$(this).css({'background-color': '#FFF'})
}
editor.txt.html(editorHtml)
// editor.change && editor.change()
})
})
}
說明:
彈出的窗口,點(diǎn)擊關(guān)閉無效不能關(guān)閉
圖3 彈出菜單無法關(guān)閉
如圖,點(diǎn)擊關(guān)閉的時(shí)候會(huì)切換到了網(wǎng)絡(luò)圖片的表單,這應(yīng)該是菜單同級(jí)別,遮蓋了關(guān)閉的按鈕,所以我們要寫css樣式加上z-index使關(guān)閉的菜單在其他的上層;
css代碼
.w-e-icon-close{
display:block;
z-index:999999 !important;
}
2、cdn引用js
我是下載到本地的
圖4 多圖上傳F12查看效果
圖片上傳,如果選擇多個(gè)圖片,可能會(huì)出現(xiàn)以上圖片的情況style="font-size: 14px; font-family: "Helvetica Neue", Helvetica, "PingFang SC", Tahoma, Arial, sans-serif; max-width: 100%;"
復(fù)制html發(fā)現(xiàn)是如下代碼:
<img src="/public/upload/image/20211201/111.jpg" style="font-size: 14px; font-family: & quot;Helvetica Neue& quot;, Helvetica, & quot;PingFang SC& quot;, Tahoma, Arial, sans-serif; max-width: 100%;">
很明顯style內(nèi)的 css樣式,不是應(yīng)該出現(xiàn)的,沒有找到在哪里修改。
雙引號(hào)換成了& quot;如果不用查看源碼,頁面直接顯示沒有問題,但是查看源碼,切換回來以后,會(huì)發(fā)現(xiàn)圖片亂了,所以查看源碼的時(shí)候,需要把& quot;替換成空。
以下使用jquery實(shí)現(xiàn)自定義菜單(查看源碼、插入分割線):
import $ from 'jquery'
mounted () {
$(document).ready(function () {
// 查看源碼菜單
$('#div1 .w-e-toolbar').prepend('<div class=\'w-e-menu\' style=\'z-index:991;\' data-title=\'查看源碼\'><a style=\' display:block;width:100%;height:100%;\' ct=1 id=\'viewsource\'><i class=\'fa fa-file-text-o\'></i></a></div>')
// 分割線菜單
var menl=$('#div1 .w-e-toolbar .w-e-menu').length;
$("#div1 .w-e-toolbar .w-e-menu").eq(menl-1).before('<div class=\'w-e-menu\' data-title=\'分割線\'><a style=\'display:block;width:100%;height:100%;\' id=\'splitline\'><i class=\'w-e-icon-split-line\'></i></a></div>')
// 查看源碼點(diǎn)擊
$(document).delegate('#viewsource', 'click', function () {
var editorHtml = editor.txt.html()
// console.log($(this).attr('ct'))
if (parseInt($(this).attr('ct')) === 1) {
$('#div1 .w-e-toolbar').prepend('<div id=\'zzc\' style=\'position:absolute;left:0;top:0;z-index:99;background-color:rgba(0,0,0,0.5);width:100%;height:40px;\'></div>')
$(this).parent().parent().parent().find('.w-e-text').css('width', $('.w-e-text').width() + 'px')
editorHtml = editorHtml.replace(/</g, '<').replace(/>/g, '>').replace(/ /g, ' ').replace(/& quot;/g, '')
$(this).attr('ct', '2')
$(this).css({'background-color': '#EEE'})
} else {
editorHtml = editor.txt.text().replace(/</ig, '<').replace(/>/ig, '>').replace(/ /ig, ' ')
$(this).attr('ct', '1')
$(this).css('border', '0px solid #DDD')
$(this).parent().parent().parent().find('.w-e-text').css('width', '100%')
$(this).parent().parent().find('#zzc').remove()
$(this).css({'background-color': '#FFF'})
}
editor.txt.html(editorHtml)
// editor.change && editor.change()
})
//分割線插入hr點(diǎn)擊
$(document).delegate('#splitline', 'click', function () {
editor.cmd.do('insertHtml', '<hr>');
});
})
}
如果我們把插入分割線的代碼單獨(dú)拿出來,只是下面幾行,相對(duì)使用官方提供的方法會(huì)簡單很多
// 插入分割線菜單
var menl=$('#div1 .w-e-toolbar .w-e-menu').length;
$('#div1 .w-e-toolbar .w-e-menu').eq(menl-1).before('<div class=\'w-e-menu\' data-title=\'分割線\'><a style=\'display:block;width:100%;height:100%;\' id=\'splitline\'><i class=\'w-e-icon-split-line\'></i></a></div>')
// 分割線插入 hr點(diǎn)擊事件
$(document).delegate('#splitline', 'click', function () {
editor.cmd.do('insertHtml', '<hr>');
});
說明:
editorHtml = editorHtml.replace(/</g, '<').replace(/>/g, '>').replace(/ /g, ' ').replace(/& quot;/g, '')
附錄一下jquery after,before,append,prepend用法:
向class=w-e-toolbar的div頭部插入html
$(" .w-e-toolbar").prepend("<div >頭部插入html</div>")
向class=w-e-toolbar的div底部插入html
$(" .w-e-toolbar").append("<div >底部插入html</div>")
向class=w-e-toolbar的div之前插入html
$(" .w-e-toolbar").before("<div >之前插入html</div>")
向class=w-e-toolbar的div之后插入html
$(" .w-e-toolbar").after("<div >后面插入html</div>")
可以做一下延伸:像我們上邊用到的向第幾個(gè)元素之后或者之前插入代碼。
隨著用于訪問萬維網(wǎng)的設(shè)備的數(shù)量和種類呈指數(shù)級(jí)增長,無論用于瀏覽網(wǎng)頁的設(shè)備如何,確保正確呈現(xiàn)網(wǎng)頁都是一項(xiàng)重要且具有挑戰(zhàn)性的任務(wù)。當(dāng)開發(fā)人員采用響應(yīng)式網(wǎng)頁設(shè)計(jì)(RWD)技術(shù)時(shí),網(wǎng)頁會(huì)修改其外觀以適應(yīng)設(shè)備的顯示限制。但是,目前缺乏自動(dòng)化支持意味著在針對(duì)不同窗口大小進(jìn)行渲染時(shí),可能無法檢測到頁面布局中的顯示故障。一個(gè)核心問題是難以提供自動(dòng)Oracle來驗(yàn)證RWD布局,這意味著在實(shí)踐中檢查故障主要是手動(dòng)過程,這導(dǎo)致許多實(shí)時(shí)響應(yīng)式網(wǎng)站的布局故障。本文介紹了一種自動(dòng)故障檢測技術(shù),該技術(shù)可檢查響應(yīng)頁面布局在一系列窗口寬度上的一致性,從而避免了對(duì)顯式Oracle的需求。在一項(xiàng)實(shí)證研究中,該方法在所研究的26個(gè)真實(shí)產(chǎn)品頁面中的16個(gè)中發(fā)現(xiàn)了故障,總共檢測到33個(gè)不同的故障。
響應(yīng)式網(wǎng)頁設(shè)計(jì),顯示故障,布局故障
響應(yīng)式網(wǎng)頁設(shè)計(jì)(RWD)是最近的一種設(shè)計(jì)和實(shí)現(xiàn)方法,它使開發(fā)人員能夠構(gòu)建無論設(shè)備大小如何都能提供等效用戶體驗(yàn)的網(wǎng)頁。RWD使網(wǎng)頁能夠動(dòng)態(tài)地修改其布局以適應(yīng)或“響應(yīng)”設(shè)備顯示器的大小,而不是要求用戶平移太寬而不適合較小屏幕的頁面,或縮放在桌面顯示器上清晰可讀,但在手機(jī)上太小的部分。如果內(nèi)容多于可用空間,則用戶只需垂直滾動(dòng)頁面。因此,在RWD的上下文中,瀏覽器窗口寬度是關(guān)于網(wǎng)頁布局應(yīng)如何調(diào)整的關(guān)鍵決定因素。
鑒于RWD的明顯優(yōu)勢,自動(dòng)檢查顯示故障的問題——網(wǎng)頁渲染中的視覺差異導(dǎo)致其偏離其預(yù)期外觀——是一個(gè)重要的問題。由于網(wǎng)站的美學(xué)和布局已被證明會(huì)影響其感知的可用性[26]和可訪問性,提高其可信度,并產(chǎn)生用戶忠誠度,因此在一個(gè)組織的網(wǎng)站中可見的故障可能導(dǎo)致收入損失并不令人驚訝。
針對(duì)這些問題,我們提出了一種自動(dòng)化技術(shù),可以檢測在實(shí)際生產(chǎn)頁面中普遍存在的五種類型的響應(yīng)式布局故障,而無需顯式的Oracle,例如一系列模型映像,復(fù)雜的布局規(guī)范或要比較的頁面的圖形模型。我們的方法依賴于常見的響應(yīng)故障類型的隱式Oracle知識(shí),自動(dòng)檢查響應(yīng)式網(wǎng)頁的布局,并在不同的窗口寬度上比較元素相對(duì)于彼此的位置。
例如,兩個(gè)網(wǎng)頁元素可能由于預(yù)期的圖形效果而重疊,或者因?yàn)樗鼈円呀?jīng)“碰撞”,因?yàn)樗狡聊豢臻g已經(jīng)減少。我們的方法通過檢查連續(xù)窗口寬度的布局行為來區(qū)分兩者。如果元素總是重疊,則開發(fā)人員在手動(dòng)檢查中可能意圖和/或容易注意到該效果。然而,如果元素經(jīng)常重疊,則可能發(fā)生微妙的RLF。我們的方法應(yīng)用類似的原則來檢測不一致的元素包裝;僅存在一個(gè)或兩個(gè)窗口寬度的布局;和內(nèi)容的間歇性突出到其他元素,或完全從窗口出來。總的來說,我們定義了四種檢測這五種RLF的算法。
我們將自動(dòng)化技術(shù)應(yīng)用于26個(gè)真實(shí)產(chǎn)品網(wǎng)頁。實(shí)驗(yàn)表明,我們的方法可以在16個(gè)網(wǎng)頁中找到故障,總共檢測到33個(gè)不同的故障。我們的評(píng)估進(jìn)一步表明,在當(dāng)前可用工具的幫助下應(yīng)用手動(dòng)檢查過程錯(cuò)過了我們的技術(shù)可以自動(dòng)檢測的19%至34%的RLF。
總結(jié)來說,本位的重要貢獻(xiàn)如下:
(1)可發(fā)現(xiàn)的五種不同類型的響應(yīng)布局故障(RLF)的分類,而不需要明確的Oracle。
(2)四種算法,可以自動(dòng)檢測響應(yīng)設(shè)計(jì)的網(wǎng)頁中的五種類型的布局故障。
(3)包含26個(gè)隨機(jī)選擇的產(chǎn)品網(wǎng)頁的實(shí)證研究,顯示所識(shí)別的RLF類型在實(shí)時(shí)站點(diǎn)中普遍存在,并且我們的算法能夠檢測它們,總共發(fā)現(xiàn)了33個(gè)不同的故障。
本節(jié)定義了五種不同類型的響應(yīng)式布局故障(RLF),它們對(duì)于RWD都是有問題的,并且可以通過不需要顯式Oracle的算法自動(dòng)識(shí)別,例如網(wǎng)頁的替代參考版本,模型圖像或布局約束的復(fù)雜規(guī)范。
相反,我們的方法通過自動(dòng)提取網(wǎng)頁響應(yīng)布局的模型,然后通過在不同的窗口寬度交叉檢查其布局來分析該模型的潛在故障。我們首先介紹網(wǎng)頁模型(第3.1節(jié))。然后,我們介紹了響應(yīng)式網(wǎng)頁中普遍存在的五種類型的RLF,以及可用于通過網(wǎng)頁模型檢測它們的四種算法的定義(第3.2節(jié))。
2.1基本概念:rRLG
我們的RLF檢測過程的基礎(chǔ)是頁面的響應(yīng)布局模型,它是響應(yīng)布局圖(RLG)的改進(jìn)。RLG不同于網(wǎng)頁的其他布局圖模型(例如,Choudhary等人的“對(duì)齊圖”[38]和Alameer等人的“布局圖”),因?yàn)樗M了一個(gè)窗口寬度范圍內(nèi)的網(wǎng)頁——而不是一個(gè)靜態(tài)寬度——以捕獲其響應(yīng)式設(shè)計(jì)。
通過查詢網(wǎng)頁的文檔對(duì)象模型(DOM)來自動(dòng)獲得RLG,以在不同的窗口寬度處找到頁面中涉及的HTML元素及其坐標(biāo)。RLG根據(jù)其響應(yīng)式設(shè)計(jì),組織此信息以跟蹤這些HTML元素的動(dòng)態(tài)可見性和相對(duì)對(duì)齊,因?yàn)轫撁娌季指鶕?jù)窗口寬度進(jìn)行調(diào)整。“精制RLG”(rRLG)與我們?cè)嫉腞LG的不同之處在于它不通過“寬度約束”對(duì)網(wǎng)頁元素的寬度進(jìn)行建模。雖然設(shè)計(jì)用于在頁面中捕獲回歸問題,但寬度約束無助于檢測本文中介紹的五種常見類型的RLF;因此rRLG不對(duì)它們進(jìn)行建模。
圖2提供了一個(gè)rRLG的示例,用于通過線框在兩個(gè)不同的窗口寬度上繪制的網(wǎng)頁。該頁面涉及HTML元素div[1]-div[3],這些元素相互堆疊用于窄窗口,要求用戶滾動(dòng)以將每個(gè)視圖放入視圖中。對(duì)于更寬的窗口,它們并排排列,并附有橫幅圖像img。
2.2響應(yīng)式布局故障的常見類型及檢測算法
一旦為響應(yīng)式網(wǎng)頁創(chuàng)建了rRLG,就可以檢查我們接下來介紹的五種RLF中的每種類型以及可用于檢測它們的算法。 每種算法的目的不僅是識(shí)別故障,還要識(shí)別涉及哪些HTML元素以及特定窗口寬度,以幫助開發(fā)人員診斷故障。
RLF1:元素碰撞。當(dāng)響應(yīng)設(shè)計(jì)的頁面的窗口變得更窄時(shí),考慮寬度損失的一種設(shè)計(jì)策略是將水平對(duì)齊的頁面元素移動(dòng)得更靠近。但是,當(dāng)窗口收縮時(shí),元素可能會(huì)相互碰撞,導(dǎo)致其內(nèi)容重疊。這可能導(dǎo)致意外的效果,例如重疊的文本或圖像,或隱藏的內(nèi)容或功能元素,從而損害頁面可用性。
圖1和MidwayMeetup頁面顯示了一個(gè)示例。在更寬的窗口(第1d部分),輸入框和按鈕并排存在。然而,當(dāng)窗口變得太窄時(shí),元素發(fā)生碰撞,使最左邊的按鈕變暗(第1c部分)。
RLF2:元素突出。在實(shí)現(xiàn)響應(yīng)式設(shè)計(jì)時(shí),一個(gè)問題是確保隨著窗口變得更窄,HTML元素的大小也會(huì)適應(yīng),因此它們?nèi)匀蛔阋匀菁{其內(nèi)容。當(dāng)元素沒有正確調(diào)整大小時(shí),它們的內(nèi)容可能不再“適合”,因此會(huì)突出到頁面的周圍部分。圖1的PDFescape示例及其導(dǎo)航鏈接塊顯示在頁面的右上角(第1f部分)。隨著窗口變窄,水平空間不再足以適合徽標(biāo)旁邊的鏈接塊。鏈接突出顯示包含的HTML元素,對(duì)用戶(第1e部分)不可見,因?yàn)槿萜骶哂蠧SS屬性“overflow: hidden”集。因此,鏈接變得不可點(diǎn)亮,并且頁面在特定大小的設(shè)備上是不可用的,并且窗口尺寸是固定的。
RLF3:窗口突出。當(dāng)窗口空間被擠壓時(shí),元素可能不僅開始溢出其容器,而且還開始從頁面的根表示HTML元素(即身體標(biāo)簽)中突出,從而出現(xiàn)在水平可視部分之外。這頁紙。如第2節(jié)所介紹的圖1的ConsumerReports網(wǎng)頁展示了這種故障類型
RLF4:小范圍布局。響應(yīng)式設(shè)計(jì)的網(wǎng)站傾向于使用許多CSS規(guī)則,這些規(guī)則由不同的媒體查詢激活和停用。對(duì)于給定的窗口寬度,CSS規(guī)則中的多個(gè)媒體查詢可以同時(shí)評(píng)估為真。例如,兩個(gè)規(guī)則,一個(gè)在窗口寬度超過768像素時(shí)激活,另一個(gè)規(guī)則在窗口低于1024像素時(shí)激活,將在769-1023像素范圍內(nèi)激活。對(duì)于給定的元素集和窗口大小,當(dāng)一系列規(guī)則“開啟”或“關(guān)閉”時(shí),管理的邏輯可能很快變得復(fù)雜,開發(fā)人員可能經(jīng)常犯錯(cuò)誤導(dǎo)致在窗口中應(yīng)用CSS規(guī)則寬度與意想的不同。當(dāng)開發(fā)人員混合使用最小寬度和最大寬度限定符來定義布局中的更改時(shí),就會(huì)出現(xiàn)這種類型的常見錯(cuò)誤。例如,開發(fā)人員可以編碼媒體查詢“@media(max-width:768px){...} “和另一個(gè)”@media(min-width:768px){...}”。由于這兩個(gè)表達(dá)式定義的窗口范圍是包含的,因此兩者都將在768像素窗口寬度處激活。媒體查詢的這種沖突可能導(dǎo)致奇怪的布局效果,因?yàn)楫?dāng)只有一個(gè)集合時(shí),將激活兩組規(guī)則。開發(fā)人員難以發(fā)現(xiàn)這些類型的故障,因?yàn)樗鼈儍H出現(xiàn)在可以查看頁面的整個(gè)窗口寬度范圍的小范圍內(nèi)。圖1的Cloudconvert示例是小范圍RLF的示例。在單個(gè)窗口寬度處,頁面的頂部菜單模糊了公司的徽標(biāo)和標(biāo)語(第1g部分)。
RLF5:包裝元素。如果網(wǎng)頁上的包含元素不足以容納其子元素,但仍然“足夠”或具有靈活的高度,則其中包含的水平對(duì)齊元素將“換行”以形成其他元素,不受歡迎的元素行 - 以及不需要的表現(xiàn)效果。圖1和BugMeNot網(wǎng)頁顯示了錯(cuò)誤包裝的示例。在較寬的窗口(第1j部分),放大鏡按鈕出現(xiàn)在搜索框旁邊。然而,隨著窗口變窄,按鈕會(huì)包裹到下一行(第1i部分)。
為了評(píng)估我們技術(shù)的有效性和效率,我們將其應(yīng)用于生產(chǎn)使用的26個(gè)真實(shí)網(wǎng)頁,最終目的是回答以下三個(gè)研究問題:
RQ1:我們的算法在檢測故障方面的效果如何?
我們的算法在響應(yīng)式設(shè)計(jì)頁面中檢測常見類型的響應(yīng)式布局故障的效果如何?
RQ2:使用“spotchecking”工具可以檢測到多少次故障?
在窗口大小調(diào)整工具的幫助下,與我們的方法相比,“抽樣檢查”過程的效果如何?
RQ3:應(yīng)用于響應(yīng)式設(shè)計(jì)的網(wǎng)頁時(shí),我們的技術(shù)需要運(yùn)行多長時(shí)間?
對(duì)于將在實(shí)際的RWD網(wǎng)頁開發(fā)過程中應(yīng)用該技術(shù)的開發(fā)人員來說,檢測布局故障的時(shí)間是否合理?
RQ1:表1b使用每種檢測算法分解了ReDeCheck針對(duì)每個(gè)網(wǎng)頁生成的故障報(bào)告的分類。我們的每個(gè)算法都發(fā)現(xiàn)了TP(即實(shí)際的RLF),26個(gè)受試者中有16個(gè)至少有一個(gè)TP。鑒于對(duì)象是包括Duolingo和StumbleUpon等已建立的商業(yè)運(yùn)營的實(shí)時(shí)和運(yùn)營網(wǎng)站,這是一個(gè)令人信服的結(jié)果,因?yàn)槲覀兺茰y,這些網(wǎng)站將進(jìn)行內(nèi)部測試過程,錯(cuò)過了由ReDeCheck。我們的算法報(bào)告的五個(gè)故障是我們用于激勵(lì)示例的故障。這些故障是消費(fèi)者端口離屏突出的內(nèi)容示例(圖1a,算法2檢測到);表單元素相互遮擋,降低了MidwayMeetup的功能(圖1c,由算法1檢測到);導(dǎo)航鏈接由于其父元素為PDFescape的突出而消失(圖1e,也被算法1檢測到);為Cloudconvert打破媒體查詢和模糊布局(圖1g,由算法3檢測),以及BugMeNot的包裝元素(圖1i,由算法4檢測)。
TP的進(jìn)一步分析顯示,這些報(bào)告總共匯總了33個(gè)不同的故障,如表1b的“Distinct RLFs”欄所報(bào)告,從而揭示了ReDeCheck結(jié)果的重復(fù)程度。一個(gè)極端的例子是Accountkiller。在這里,由算法3生成的147個(gè)小范圍報(bào)告是TP,但更仔細(xì)的分析顯示所有報(bào)告都對(duì)應(yīng)于單個(gè)明顯的故障。該頁面涉及圖標(biāo)網(wǎng)格,每個(gè)圖標(biāo)對(duì)應(yīng)于rRLG節(jié)點(diǎn),其中對(duì)齊約束連接每對(duì)。對(duì)于一個(gè)小窗口范圍,ReDeCheck檢測網(wǎng)格中未按一致排列的元素,導(dǎo)致相對(duì)對(duì)齊的變化和一些小范圍約束,這些約束導(dǎo)致算法觸發(fā)每個(gè)單獨(dú)的故障報(bào)告。另外,可以通過不同的算法檢測相同的不同故障。例如,對(duì)于Cloudconvert,元素僅在小范圍內(nèi)發(fā)生碰撞,從而觸發(fā)算法1和算法3的報(bào)告。
RQ2:Spotcheck分析顯示我們的方法已經(jīng)發(fā)現(xiàn)RLF作為RQ1的一部分,但沒有新的額外故障。表2報(bào)告了在每個(gè)工具建議的窗口寬度之一處或通過隨機(jī)選擇窗口寬度而顯示的這些不同RLF的數(shù)量。如表所示,工具顯示了66%到81%的這些故障,具體取決于他們建議檢查的窗口寬度集。由于這些預(yù)設(shè)寬度對(duì)應(yīng)于常用設(shè)備,因此該結(jié)果顯示ReDeCheck能夠顯示將在這些設(shè)備上顯示的故障以供用戶查看。盡管點(diǎn)測工具提示了檢查網(wǎng)頁的窗口寬度,但仍然需要手動(dòng)識(shí)別故障——相比之下,Re-DeCheck減輕了開發(fā)人員的一些努力。結(jié)果還表明,我們的技術(shù)確定的故障率為19%至34%。即使使用了所有的抽查工具,并通過一定程度的進(jìn)一步隨機(jī)抽查來補(bǔ)充,我們的方法最初確定的五種不同的RLF也找不到。
RQ3:圖3顯示了ReDeCheck在30個(gè)試驗(yàn)中每個(gè)網(wǎng)頁的執(zhí)行時(shí)間中位數(shù)。幾乎所有頁面(26個(gè)中的25個(gè))都是由我們的工具在中位數(shù)大約三分鐘內(nèi)處理的,15頁需要60秒或更少。 Airbnb花了將近4.5分鐘——但它是最復(fù)雜的對(duì)象頁面之一,如表1a所示。一般來說,圖表顯示,花費(fèi)最多時(shí)間的對(duì)象要么是最復(fù)雜的一部分(即,就CSS元素而言是Airbnb,而在CSS聲明方面是Duolingo),或者產(chǎn)生最多的問題(即Accountkiller和PepFeed),因此,在捕獲故障屏幕截圖和生成帶注釋的圖形報(bào)告(針對(duì)TP,F(xiàn)P和NOI)或者網(wǎng)頁復(fù)雜性和報(bào)告的問題數(shù)量(即,ConsumerReports)的混合時(shí),會(huì)產(chǎn)生額外的時(shí)間成本。
響應(yīng)式網(wǎng)頁設(shè)計(jì)(RWD)倡導(dǎo)創(chuàng)建具有跨多個(gè)窗口寬度的增強(qiáng)用戶體驗(yàn)的網(wǎng)頁[32]。由于根據(jù)RWD原則[20]實(shí)施網(wǎng)頁具有挑戰(zhàn)性,我們之前的工作提出了一種回歸檢查技術(shù),該技術(shù)比較了頁面的兩個(gè)模型并提出了任何差異[40]。專注于處理與檢查響應(yīng)式網(wǎng)頁的問題正交的問題,其他先前的工作(例如,[17,18])沒有采用設(shè)計(jì)用于檢查多個(gè)窗口的Oracle。相比之下,本文的自動(dòng)化方法利用隱式Oracle信息來檢測響應(yīng)式布局故障。實(shí)驗(yàn)表明它是有效的:在8個(gè)網(wǎng)頁中發(fā)現(xiàn)2個(gè)或更多的故障,它在一個(gè)網(wǎng)頁中發(fā)現(xiàn)了6個(gè)故障,在26個(gè)對(duì)象中檢測到總共33個(gè)不同的故障。
此文由南京大學(xué)軟件學(xué)院2016級(jí)本科生虞圣呈翻譯轉(zhuǎn)述。
讀
本文內(nèi)容是筆者最近實(shí)現(xiàn)的 web 端彈幕組件—— Barrage UI(https://github.com/parksben/barrage) 的一個(gè)延伸。在閱讀本文的實(shí)例和相關(guān)代碼之前,不妨先瀏覽項(xiàng)目文檔,對(duì)組件的使用方式和相關(guān)接口進(jìn)行了解。
各位童鞋如果經(jīng)常上 B 站(bilibili.com) ,應(yīng)該對(duì) 蒙版彈幕 這個(gè)概念并不陌生。
蒙版彈幕是由知名彈幕視頻網(wǎng)站 bilibili 于 2018 年中推出的一種彈幕渲染效果,可以有效減少彈幕文字對(duì)視頻主體信息的干擾。
關(guān)于 B 站蒙版彈幕的實(shí)現(xiàn)原理,其實(shí)網(wǎng)上已經(jīng)有很多細(xì)致的討論和研究。個(gè)人總結(jié)了一下,大致要點(diǎn)如下:
客戶端方面,由于 B 站彈幕是基于 div+css 的實(shí)現(xiàn),因而采用了 svg 格式來傳輸矢量蒙版(至少目前是這樣),通過 CSS 遮罩的方式實(shí)現(xiàn)渲染。
■乎上有一篇關(guān)于這個(gè)方案的討論,感興趣的童鞋可以移步 這里(https://www.zhihu.com/question/279446628) 進(jìn)行了解。
Barrage UI(https://github.com/parksben/barrage) 是個(gè)人最近實(shí)現(xiàn)的一個(gè)前端彈幕組件,主要用于在前端頁面中掛載彈幕動(dòng)畫。
組件提供了一系列的操作接口以方便用戶對(duì)彈幕的相關(guān)特性進(jìn)行定制。你也可以在渲染層面對(duì)動(dòng)畫中的每一幀圖像進(jìn)行處理,比如:
下面是基于 Barrage UI 組件實(shí)現(xiàn)的蒙版彈幕效果:
編者注:圖片這么糊是微信公眾平臺(tái)的鍋。
由于文中不方便嵌入視頻,Demo 的實(shí)際效果請(qǐng)移步到 此處(https://parksben.github.io/masking-danmaku-demo) 查看。
下面我們來介紹如何實(shí)現(xiàn)上圖的動(dòng)畫效果。
Demo 中使用了初音小姐姐跳舞的視頻。最主要的特點(diǎn)是除了人物外,視頻的背景是比較一致的純色。對(duì)于這種類型的圖像,我們可以使用 色鍵 的方式進(jìn)行摳圖(生成“蒙版”)。
色度鍵控(https://zh.wikipedia.org/zh/%E8%89%B2%E9%94%AE),又稱色彩嵌空,是一種去背合成技術(shù)。Chroma 為純色之意,Key 則是抽離顏色之意。把被拍攝的人物或物體放置于綠幕的前面,并進(jìn)行去背后,將其替換成其他的背景。此技術(shù)在電影、電視劇及游戲制作中被大量使用,色鍵也是虛擬攝影棚(Virtual studio)與視覺效果(Visual effects)當(dāng)中的一個(gè)重要環(huán)節(jié)。
下圖是色鍵技術(shù)的一個(gè)示例:在綠幕前穿著藍(lán)色衣服的小姐姐,左圖為去背前,右圖為去背后的新背景。
在瀏覽器環(huán)境中,我們可以通過 canvas 畫布實(shí)時(shí)地繪制視頻的每一幀,并從畫布中讀取到圖像中每個(gè)像素的 RGBA 信息,檢測每個(gè)點(diǎn)的 R(red)、G(green)、B(blue) 值是否滿足要求,最終將需要扣除的像素的 A(alpha) 值置為 0,即可得到用于合成蒙版彈幕的蒙版圖像。
注意:
Barrage UI 組件的蒙版功能是基于 Canvas 2D API 的 CanvasRenderingContext2D.globalCompositeOperation 屬性實(shí)現(xiàn)的(使用了 source-in 的混合模式),因而只需將不需要的像素設(shè)置為透明(alpha=0)即可,并不需要改變圖像的 RGB 色值。
下面介紹此案例的代碼實(shí)現(xiàn)。
直接使用 yarn 或 npm 安裝此組件:
yarn add barrage-ui or npm install --save barrage-ui
準(zhǔn)備一個(gè) video 元素用于播放視頻,video 的父級(jí)元素用于掛載彈幕:
<div id="container">
<video id="video" src="videos/demo.mp4" controls>
</video>
</div>
根據(jù)視頻的實(shí)際尺寸(880×540)設(shè)置 #container 與 #video 的樣式:
html,
body {
font: 14px/18px Helvetica, Arial, 'Microsoft Yahei', Verdana, sans-serif;
width: 100%;
margin: 0;
padding: 0;
background: #eee;
overflow: hidden;
}
#container,
#video {
width: 880px;
height: 540px;
}
#container {
margin: 0 auto;
margin-top: 50vh;
margin-left: 50vw;
transform: translate(-50%, -50%);
background-color: #ddd;
}
import Barrage from 'barrage-ui';
import data from 'utils/mockData';
// 獲取父級(jí)容器
const container = document.getElementById('container');
// 創(chuàng)建彈幕實(shí)例
const barrage = new Barrage({
container: container,
});
// 重置畫布高度,避免彈幕遮擋視頻播放控件
barrage.canvas.height = container.clientHeight - 80;
// 裝填彈幕數(shù)據(jù)
barrage.setData(data);
其中,mockData 是用于生成隨機(jī)彈幕數(shù)據(jù)的方法。
關(guān)于彈幕數(shù)據(jù)的內(nèi)容與格式,詳見 Barrage UI 項(xiàng)目文檔(https://github.com/parksben/barrage#%E8%A3%85%E5%A1%AB%E5%BC%B9%E5%B9%95)
// 獲取 video 元素
const video = document.getElementById('video');
// 新建一個(gè)畫布來實(shí)時(shí)繪制視頻(純繪圖,不用添加進(jìn)頁面)
const vCanvas = document.createElement('canvas');
vCanvas.width = video.clientWidth;
vCanvas.height = video.clientHeight;const vContext = vCanvas.getContext('2d');
// 實(shí)時(shí)繪制視頻到畫布
barrage.afterRender = () => {
vContext.drawImage(video, 0, 0, vCanvas.width, vCanvas.height);
};
使用組件提供的渲染周期鉤子 .afterRender() 可以在彈幕動(dòng)畫的每一幀圖像渲染后,將視頻圖像繪制到中間畫布 vCanvas 上。注意這里的 vCanvas 畫布主要用于實(shí)時(shí)地獲取視頻圖像,并不需要添加到頁面中。
// 渲染前讀取畫布 vCanvas 的數(shù)據(jù),并處理為蒙版圖像
barrage.beforeRender = () => {
// 讀取圖像
const frame = vContext.getImageData(0, 0, vCanvas.width, vCanvas.height);
// 圖像總像素個(gè)數(shù)
const pxCount = frame.data.length / 4;
// 將 frame 構(gòu)造成我們需要的蒙版圖像
for (let i = 0; i < pxCount; i++) {
// 這里不用 ES6 解構(gòu)賦值的寫法,主要為了保證性能
// PS: 這里如果用解構(gòu)賦值語法將導(dǎo)致大量新對(duì)象的創(chuàng)建,是個(gè)很耗時(shí)的過程
const r = frame.data[i * 4 + 0];
const g = frame.data[i * 4 + 1];
const b = frame.data[i * 4 + 2];
// 將黑色區(qū)域以外的內(nèi)容設(shè)為透明
if (r > 15 || g > 15 || b > 15) {
frame.data[4 * i + 3] = 0;
}
}
// 設(shè)置蒙版
barrage.setMask(frame);
};
使用組件提供的渲染周期鉤子 .beforeRender() 可以在彈幕動(dòng)畫的每一幀圖像渲染前計(jì)算出蒙版圖像。其中,用于更新蒙版的接口為 .setMask()。
最后,為了讓彈幕的行為與視頻播放的操作協(xié)同,還需要進(jìn)行一些綁定的操作:
// 綁定播放事件
video.addEventListener( 'play',
( ) => {
barrage.play();
}, false);
// 綁定暫停事件
video.addEventListener( 'pause',
( ) => {
barrage.pause();
}, false);
// 切換播放進(jìn)度
video.addEventListener( 'seeked',
( ) => {
barrage.goto(video.currentTime * 1000);
}, false);
這里分別用到 Brrage UI 組件的 .play() .pause .goto() 三個(gè)接口,分別用于播放、暫停和切換彈幕動(dòng)畫的進(jìn)度。需要注意的是,通過 video.currentTime 屬性獲取到的視頻播放進(jìn)度是一個(gè)單位為 秒 的浮點(diǎn)數(shù),需要轉(zhuǎn)換為 毫秒數(shù) 再傳給彈幕組件。
本文的案例已上傳 Github,感興趣的童鞋可以點(diǎn)擊 這里(https://github.com/parksben/masking-danmaku-demo) 查看源碼細(xì)節(jié)。
關(guān)于 Barrage UI 組件如果有什么建議和疑問,歡迎大家在項(xiàng)目中提 issue 給我,幫助我持續(xù)改進(jìn)和迭代,更歡迎 star 和 PR。
作者:Parksben
來源:微信公眾號(hào):創(chuàng)宇前端
出處:https://mp.weixin.qq.com/s/0HwQh5nUaK6Vi0EWbU8ENQ
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。