背景
筆者最近做一個新手引導功能,這個引導獨立於實際產品,但是卻包含部分產品的功能,是一個單獨的模塊。
在實現上,一開始想的儘量把引導功能和產品功能分開。做的時候也儘量這樣做了。
最外層是引導功能的組件,實現引導相關的邏輯,這個組件包含了產品功能組件,產品功能組件不用不關心引導組件的存在。功能組件只需要實現功能並且提供相應的事件函數就行。
最後爲了能在功能組件裏高亮顯示引導區,直接在引導組件裏通過選擇器把功能組件裏的元素層級改爲比較高。這樣當引導組件遮擋這個屏幕時,功能區元素是高亮顯示且可以點擊的。邊實際操作產品功能邊完成整個引導邏輯。
當筆者做完以後,發現如果要在引導中間加入一個流程,不能通過修改一個配置就完成。需要修改一些代碼,相當於需要熟悉整個引導流程當代碼才能進行修改和添加。這缺乏一定當擴展性,而且維護成本會比較高。於是筆者在網上尋找其他人是如何做新手引導的。
經過
在網上找了一圈,有對引導功能進行總結的:
- 引導功能代碼,不能混入正常遊戲邏輯代碼中,後患無窮,應儘量分離;(難以忍受優雅的代碼被無情的打亂,更難忍受糟糕的代碼被弄的支離破碎)
- 界面只發生簡單的UI位移、Size大小、節點層次的調整,不需要修改具引導代碼;
- 定位UI指引矩形區域應儘可能簡單,能自適應不同的屏幕尺寸;
顯然,我目前的代碼是不能滿足這個總結的。
如果只是讓用戶看引導,並且點擊引導層,然後一步一步說明。這樣做還比較簡單,網上的實現方案找着找着就找到張哥那裏區了。張哥用border做遮罩,元素本身沒背景,所以高亮了引導區。然後可以通過配置定製引導,基本滿足了上面的總結。具體代碼如下:
CSS代碼:
.cover {
display: none;
position: absolute;
width: 0; height: 0;
left: 0; top: 0; right: 0; bottom: 0;
border: 0 solid #000;
opacity: .75;
filter: alpha(opacity=75);
z-index: 9;
/* 過渡效果 */
transition: all .25s;
/* 邊緣閃動問題fix */
box-shadow: 0 0 0 100px #000;
overflow: hidden;
}
.cover::before {
content: '';
width: 100%; height:100%;
border-radius: 50%;
border: 400px solid #000;
position: absolute;
left: -400px; top: -400px;
box-shadow: inset 0 0 5px 2px rgba(0,0,0,.75);
}
/* IE7, IE8 img */
.cover > img {
width: 100%; height: 100%;
}
HTML代碼:
<div id="cover" class="cover"></div>
JS代碼:
var coverGuide = function(cover, target) {
var body = document.body, doc = document.documentElement;
if (cover && target) {
// target size(width/height)
var targetWidth = target.clientWidth,
targetHeight = target.clientHeight;
// page size
var pageHeight = doc.scrollHeight,
pageWidth = doc.scrollWidth;
// offset of target
var offsetTop = target.getBoundingClientRect().top + (body.scrollTop || doc.scrollTop),
offsetLeft = target.getBoundingClientRect().left + (body.scrollLeft || doc.scrollLeft);
// set size and border-width
cover.style.width = targetWidth + 'px';
cover.style.height = targetHeight + 'px';
cover.style.borderWidth =
offsetTop + 'px ' +
(pageWidth - targetWidth - offsetLeft) + 'px ' +
(pageHeight - targetHeight - offsetTop) + 'px ' +
offsetLeft + 'px';
cover.style.display = 'block';
// resize
if (!cover.isResizeBind) {
if (window.addEventListener) {
window.addEventListener('resize', function() {
coverGuide(cover, target);
});
cover.isResizeBind = true;
} else if (window.attachEvent) {
window.attachEvent('onresize', function() {
coverGuide(cover, target);
});
cover.isResizeBind = true;
// IE7, IE8 box-shadow hack
cover.innerHTML = '<img src="guide-shadow.png">';
}
}
}
};
var elCover = document.getElementById('cover');
var arrElTarget = [
document.getElementsByTagName('a')[0],
document.getElementById('backTo'),
document.getElementById('image')
], index = 0;
coverGuide(elCover, arrElTarget[index]);
elCover.onclick = function() {
index++;
if (!arrElTarget[index]) {
index = 0;
}
coverGuide(elCover, arrElTarget[index]);
};
代碼還兼容了IE7和IE8,真的很厲害耶。但是這個模式並不適合在引導過程中,是真的在操作功能。但是稍微改一改能不能滿足呢?
思考
功能組件如果提供自身被用戶操作的各種事件,然後引導功能可以訂閱各種事件,且引導的高亮層是可以被點穿的(但是按照上面的模式,高亮層和遮罩是同一個元素,如果高亮層被點穿,遮罩層可能也被點穿)。
還思考了一種方法,就是假設所有的組件都繼承引導功能。可以通過外部配置激活這些組件的引導功能。從而使得可以配置引導,而且引導是組件的一部分,繼而能獲得組件所有的狀態和操作組件。這個方法前期組件如果做好了,感覺會很好用。