手写 功能引导实现

功能实现

实现效果一

效果
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

配置

// 首屏引导
function loadHomeLead(){

    // 是否已经加载过了
    const is = localStorage.getItem('loadHomeLead')
    if(is) return
    const step1pos = document.querySelectorAll('.info')[1].getBoundingClientRect()
    const config = {
        done(){
            localStorage.setItem('loadHomeLead', 'true')
        },
        steps:[
            {   
                top: step1pos.top,
                left: step1pos.left,
                width: step1pos.width,
                height: 66,
                // 文本区域
                text:{
                    value:'同一地区存在多个项目时会合并显示<br/>点击显示所有合并的项目',
                    className:'', // 自定义类 控制样式
                },
                // 按钮
                btn:{
                    next:{text:'我知道了'},
                    // prev:{text:'上一步'},
                    // done:{text:'结束'},
                }, 
                end(nextIndex){
                    console.log('第一步移动完成')
                }

            },
            {
                height: 66,
                start(index){
                    return new Promise((resolve, reject)=>{
                        map.on("mapmove",move)
                        function move(){
                            map.off("mapmove",move)
                            setTimeout(()=>{
                                const steppos = document.querySelectorAll('.info')[0].getBoundingClientRect()
                                config.steps[index].top = steppos.top
                                config.steps[index].left = steppos.left
                                config.steps[index].width = steppos.width
                                resolve()
                            }, 100);
                        }
                        // 展开地图
                        map.setZoomAndCenter(14, [120.6522,27.75407],)
                    })
                },
                text:{
                    value:'项目图标,点击查看项目详情',
                    className:'', 
                },
                btn:{
                    next:{text:'我知道了'},
                }, 
            },
            {
                start(index){
                    const steppos = document.querySelectorAll('.search')[0].getBoundingClientRect()
                    config.steps[index].top = steppos.top
                    config.steps[index].left = steppos.left
                    config.steps[index].width = steppos.width
                    config.steps[index].height = steppos.height
                },
                text:{
                    value:'点击搜索按钮输入项目名称可以搜索定位项目',
                    className:'', 
                },
                btn:{
                    next:{text:'我知道了'},
                }, 
            },
            {
                start(index){
                    const steppos = document.querySelectorAll('.layers')[0].getBoundingClientRect()
                    config.steps[index].top = steppos.top
                    config.steps[index].left = steppos.left
                    config.steps[index].width = steppos.width
                    config.steps[index].height = steppos.height
                },
                text:{
                    value:'点击可以切换地图的样式',
                    className:'', 
                },
                btn:{
                    next:{text:'我知道了'},
                }, 
            }
        ]
    }
    const lead = new Lead(config)
    lead.start()
}

实现效果二

效果
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

配置

// 项目详情引导
function projectDetailsLead(){
    const is = localStorage.getItem('DetailsLead')
    if(is) return;

    function getPos(obj, {top, left, width, height}, offset={top:0, left:0, width:0, height:0}){
        obj.top = top + offset.top
        obj.left = left + offset.left
        obj.width = width + offset.width
        obj.height = height + offset.height
    }
    const config = {
        done(){
            localStorage.setItem('DetailsLead', 'true')
        },
        steps:[
            {
                start(index){
                    return new Promise((resolve, reject)=>{
                        setTimeout(()=>{
                            const steppos = document.querySelectorAll('.navigation')[0].getBoundingClientRect()
                            getPos(config.steps[index], steppos, {
                                top: -5, left: -5, width:10, height: 5
                            })
                            resolve()
                        }, 200)
                    })
                },
                text:{
                    value:'导航按钮:点击可以导航到项目所在位置',
                    className:'', // 自定义类 控制样式
                },
                btn:{
                    next:{text:'我知道了'},
                }, 
            },
            {
                start(index){
                    const steppos = document.querySelectorAll('.btns>.list')[0].getBoundingClientRect()
                    getPos(config.steps[index], steppos, {
                        top: 0, left: -5, width:10, height: 0
                    })
                },
                text:{
                    value:'点击显示该项目所有时间段的项目列表,用户可以选择<br/>想要了解的时间段查看项目进展,再次点击隐藏列表',
                    className:'', // 自定义类 控制样式
                },
                btn:{
                    next:{text:'我知道了'},
                }, 
            },
            {
                start(index){
                    const steppos = document.querySelectorAll('.btns>.btn_full')[0].getBoundingClientRect()
                    getPos(config.steps[index], steppos, {
                        top: 0, left: -5, width:10, height: 0
                    })
                },
                text:{
                    value:'点击该按钮可以把全景图分屏进行对比,配合列表按钮<br/>上下两屏选择不同时间点的项目,可以清楚了解项目的<br/>进展情况,再次点击取消分屏',
                    className:'', // 自定义类 控制样式
                },
                btn:{
                    next:{text:'我知道了'},
                }, 
            },
            {
                start(index){
                    const steppos = document.querySelectorAll('.btns>.btn_sync')[0].getBoundingClientRect()
                    getPos(config.steps[index], steppos, {
                        top: 0, left: -5, width:10, height: 0
                    })
                },
                text:{
                    value:'分屏按钮是配合分屏使用的,可以让分屏时的两个全景<br/>图在滑动、放大等操作时是否同步操作的设置',
                    className:'', // 自定义类 控制样式
                },
                btn:{
                    next:{text:'我知道了'},
                }, 
            },
            {
                start(index){
                    const steppos = document.querySelectorAll('.btns>.btn_des')[0].getBoundingClientRect()
                    getPos(config.steps[index], steppos, {
                        top: 0, left: -5, width:10, height: 5
                    })
                },
                text:{
                    value:'点击查看该项目的详细信息',
                    className:'', // 自定义类 控制样式
                },
                btn:{
                    next:{text:'我知道了'},
                }, 
            }
        ]
    }
    const lead = new Lead(config)
    lead.start()
}

实现过程

lead.js

class Lead{
    constructor({steps=[], done=()=>{}}){
        this.moveIndex = -1;// 当前进度
        this.container = document.body; // 挂载区域
        this.steps = steps
        this.doneFun = done
    }

    start(){
        // 容器
        this.el = this.createDom('div','lead-container')
        
        // 高亮
        this.highlight = this.createDom('div','lead-highlight')
        this.highlight.appendChild(this.createDom('div','lead-highlight-border'))

        // 信息区
        this.contextdom = this.createDom('div','lead-context')
        // 文本区
        this.textdom = this.createDom('div','lead-text')
        // 按钮区
        this.btnDom = this.createDom('div','lead-btns') 
        this.btnDom.btnChild = {}
        // 指向区
        this.oriented = this.createDom('div','lead-oriented')
      
        

        this.el.appendChild(this.highlight)
        this.el.appendChild(this.contextdom)
        this.container.appendChild(this.el)

        // 第一次聚焦,高亮
        if(this.steps.length){
            this.next()
            this.showTextDom()
            this.steps[0].end && this.steps[0].end(this.moveIndex)
        }
       

        // 添加过渡
        this.highlight.style.transition = 'all .2s'
    }

    next(){
        this.moveIndex ++;
        if(this.moveIndex >= this.steps.length){
            return this.done()
        }
        this.step()
    }

    prev(){
        if(this.moveIndex <= 0){
            return this.done()
        }
        this.moveIndex --;
        this.step()
    }

    done(){ // 结束
        this.el.remove()
        this.doneFun()
    }

    async step(){
        const currConfig = this.steps[this.moveIndex]
        if(currConfig.start){
            await currConfig.start(this.moveIndex)
        }
        this.hideTextDom()
        console.log('step',this.moveIndex, currConfig)
        this.highlightMove(currConfig)
        this.moveTextDom(currConfig)
        this.highlight.addEventListener("transitionend",()=>{
            setTimeout(()=>{this.showTextDom()},100)
            currConfig.end && currConfig.end(this.moveIndex)
        },{once:true})
    }

    moveTextDom({left, top, height, width, text, btn}){ // 文本区移动
        const {w, h} = this.getScreen();
        const bottom = h - height - top;
        const direction = top > bottom ? 'top' : 'bottom';
        const orientedW = this.oriented.clientWidth || 64
        if(text){
            this.textdom.display = 'block'
            const {value, className} = text
            this.textdom.innerHTML = value
            className && this.textdom.classList.add(className)
        }else{
            this.textdom.display = 'none'
        }
      
        if(btn){
            Object.keys(this.btnDom.btnChild).forEach((key)=>{
                this.btnDom.btnChild[key].style.display = 'none'
            })
            Object.keys(btn).reverse().forEach((key)=>{
                if(this.btnDom.btnChild[key]){
                    this.btnDom.btnChild[key].innerText = btn[key].text
                    this.btnDom.btnChild[key].style.display = 'block'
                }else{
                    let _btn = this.createDom('div', `lead_btn btn_${key}`)
                    _btn.innerText = btn[key].text
                    _btn.addEventListener('click',()=>this[key]())
                    this.btnDom.btnChild[key] = _btn
                    this.btnDom.appendChild(_btn)
                }
            });
            this.btnDom.display = 'flex'
        }else{
            this.btnDom.display = 'none'
        }
      
        const pos = this.getPos({left, top, height, width})
        this.oriented.style.transform = `rotate(${pos * 90}deg)`;
        let orientedLeft = left + width/2;
        if(orientedLeft + orientedW >= w || orientedLeft > w/2){
            orientedLeft -= orientedW;
        }
        this.oriented.style.marginLeft = orientedLeft + 'px';
        if(direction === 'top'){
            this.contextdom.style.top = 'auto'
            this.contextdom.style.bottom = bottom + height + 10 + 'px'
            this.contextdom.appendChild(this.textdom) 
            this.contextdom.appendChild(this.btnDom)
            this.btnDom.style.marginBottom = '-10px'
            this.contextdom.appendChild(this.oriented)
        }

        if(direction === 'bottom'){
            this.contextdom.style.top = top + height + 10 + 'px'
            this.contextdom.style.bottom = 'auto'
            this.contextdom.appendChild(this.oriented)
            this.contextdom.appendChild(this.textdom)
            this.btnDom.style.marginBottom = '0px'
            this.contextdom.appendChild(this.btnDom)
        }
    }

    getScreen(){
        return {
            h: this.container.clientHeight,
            w: this.container.clientWidth
        }
    }

    getPos({left, top, height, width}){ // 返回位置 0 左上角 1右上角 2左下角 3右下角 
        const {w, h} = this.getScreen()
        let _left = w/2 >= left + width/2,
            _top = h/2 >= top + height/2,
            result = null
    
        if(_left && _top) result = 0
        if(!_left && _top) result = 1
        if(!_left && !_top) result = 2
        if(_left && !_top) result = 3
       
        return result
    }

    hideTextDom(){ // 隐藏文本区
        this.contextdom.style.transition = 'none';
        this.contextdom.style.height = 0;
    }

    showTextDom(){ // 显示文本区
        this.contextdom.style.transition = 'height 0.2s'
        this.contextdom.style.height = 'auto';
    }

    createDom(tag, className){
        const TAG = document.createElement(tag)
        TAG.className = className
        return TAG
    }

    highlightMove({left, top, height, width}){
        const {w, h} = this.getScreen()

        top = top <= 0 ? 0 : top
        left = left <= 0 ? 0 : left

        let right = w - left - width
        let bottom = h - height - top

        bottom = bottom <= 0 ? 0 : bottom
        right = right <= 0 ? 0 : right
      
        this.highlight.style.width = width + 'px'
        this.highlight.style.height = height + 'px'
        this.highlight.style.borderWidth = `${top}px ${right}px ${bottom}px ${left}px`;

        // this.highlight.style.borderTopWidth = top + 'px'
        // this.highlight.style.borderRightWidth = right + 'px'
        // this.highlight.style.borderBottomWidth = bottom + 'px'
        // this.highlight.style.borderLeftWidth = left + 'px'

        window.highlight = this.highlight
    }
    
}

lead.css

.lead-container{
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    z-index: 99999;
}
.lead-container .lead-context,
.lead-container .lead-highlight{
    position: absolute;
}

.lead-container .lead-highlight{
    border-color: rgba(0, 0, 0, 0.49);
    border-style: solid;
}

.lead-container .lead-highlight .lead-highlight-border{
    border: 1px solid gray;
    width: calc(100% + 2px);
    height: calc(100% + 2px);
    margin-left: -1px;
    margin-top: -1px;
    border-radius: 3px;
    box-sizing: border-box;
}

.lead-container .lead-text{
    color: #fff;
    font-size: 12px;
    margin: 10px;
    text-align: center;
}

.lead-container .lead-context{
    margin: auto;
    width: 100vw;
    /* transition: all 0.2s; */
    /* outline: 1px solid red; */
    overflow: hidden;
}

.lead-container .lead-oriented{
    width: 64px;
    height: 50px;
    background: url(../img/lead.svg);  
}
.lead-container .lead-btns{
    display: flex;
    justify-content: center;
    margin-top: 10px;
    /* margin-bottom: -10px; */
}
.lead-container .lead-btns .lead_btn{
    color: #fff;
    background: rgba(52, 98, 255, 1);
    font-size: 12px;
    border-radius: 20px;
    margin-left: 10px;
    padding: 4px 14px;
}

图片素材
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值