前端新手引导功能

申颖逸
2023-12-01

背景

笔者最近做一个新手引导功能,这个引导独立于实际产品,但是却包含部分产品的功能,是一个单独的模块。

在实现上,一开始想的尽量把引导功能和产品功能分开。做的时候也尽量这样做了。

最外层是引导功能的组件,实现引导相关的逻辑,这个组件包含了产品功能组件,产品功能组件不用不关心引导组件的存在。功能组件只需要实现功能并且提供相应的事件函数就行。

最后为了能在功能组件里高亮显示引导区,直接在引导组件里通过选择器把功能组件里的元素层级改为比较高。这样当引导组件遮挡这个屏幕时,功能区元素是高亮显示且可以点击的。边实际操作产品功能边完成整个引导逻辑。

当笔者做完以后,发现如果要在引导中间加入一个流程,不能通过修改一个配置就完成。需要修改一些代码,相当于需要熟悉整个引导流程当代码才能进行修改和添加。这缺乏一定当扩展性,而且维护成本会比较高。于是笔者在网上寻找其他人是如何做新手引导的。

 

经过

在网上找了一圈,有对引导功能进行总结的:

  • 引导功能代码,不能混入正常游戏逻辑代码中,后患无穷,应尽量分离;(难以忍受优雅的代码被无情的打乱,更难忍受糟糕的代码被弄的支离破碎)
  • 界面只发生简单的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,真的很厉害耶。但是这个模式并不适合在引导过程中,是真的在操作功能。但是稍微改一改能不能满足呢?

 

思考

功能组件如果提供自身被用户操作的各种事件,然后引导功能可以订阅各种事件,且引导的高亮层是可以被点穿的(但是按照上面的模式,高亮层和遮罩是同一个元素,如果高亮层被点穿,遮罩层可能也被点穿)。

还思考了一种方法,就是假设所有的组件都继承引导功能。可以通过外部配置激活这些组件的引导功能。从而使得可以配置引导,而且引导是组件的一部分,继而能获得组件所有的状态和操作组件。这个方法前期组件如果做好了,感觉会很好用。

 类似资料: