使用svg画引导层、新手引导页

本文详细解释了SVG的viewBox参数设置,path元素中的M、L、H、V、Z指令,以及如何在JavaScript中使用这些元素创建动态路径。还介绍了屏幕定位和节点信息的获取与应用。
摘要由CSDN通过智能技术生成

svg的内容很多,这里只用到了其中的路径,其他内容就不介绍了。

svg的使用方法

svg参数介绍

<svg class="svg" :viewBox="'0 0 100 100" xmlSpace="preserve" preserveAspectRatio="xMinYMin slice">
</svg>
参数是否必须描述
viewBox四个参数分别代表最小 X 坐标、最小 Y 坐标、宽度和高度
xmlSpacepreserve:这个属性告诉 SVG 解析器保留所有空白字符,包括空格、换行符等。如果不设定此属性,XML 解析器可能会忽略多余的空白
preserveAspectRatioxMinYMin 表示图形应该相对于容器的左上角对齐,slice 表示如果图形宽高比与容器不同,则会在必要时裁剪(slice)图形以填满整个可见区域,而不是拉伸(stretch)图形。其他可能的取值还有如 meet(保持纵横比并使图形内部尽可能填充容器)、none(不保持纵横比)等。

path的参数介绍

<path :d="" style="">
参数是否必须描述
d绘制路径的指令以及参数。
style显示样式,主要用于规定了绘制矩形的样式属性,详情参考显示属性。

以下参数中大写的指令代表了绝对定位(以画布左上角为0点),而小写则代表相对定位(以上一个点为0点)
横向为x,纵向为y

指令参数描述
Mx,y路径的其实位置
Lx,y画一条线到指定x,y位置
Hx画一条水平线到指定x位置,y值不变
Vy画一条垂直线到指定y位置,x值不变
Z结束绘制并连接起点

使用的d参数解释

`M${this.bodyWidth},0L0,0L0,${this.bodyHeight}L${this.bodyWidth},${this.bodyHeight}L${this.bodyWidth},0Z M${this.currentItem.left},${this.currentItem.top} h${this.currentItem.width} v${this.currentItem.height} h-${this.currentItem.width} v-${this.currentItem.height} z`

M t h i s . b o d y W i d t h , 0 L 0 , 0 L 0 , {this.bodyWidth},0L0,0L0, this.bodyWidth,0L0,0L0,{this.bodyHeight}L t h i s . b o d y W i d t h , {this.bodyWidth}, this.bodyWidth,{this.bodyHeight}L${this.bodyWidth},0Z

从屏幕左上角开始沿着屏幕的四边划一圈

M t h i s . c u r r e n t I t e m . l e f t , {this.currentItem.left}, this.currentItem.left,{this.currentItem.top} h t h i s . c u r r e n t I t e m . w i d t h v {this.currentItem.width} v this.currentItem.widthv{this.currentItem.height} h- t h i s . c u r r e n t I t e m . w i d t h v − {this.currentItem.width} v- this.currentItem.widthv{this.currentItem.height} z

再沿着我们获取到的节点四边划一圈

字段解释
this.bodyWidth屏幕宽度
this.bodyHeight屏幕高度
this.currentItem.width节点的宽度
this.currentItem.height节点的高度
this.currentItem.left节点距离屏幕左侧的距离
this.currentItem.top节点距离屏幕上面的距离

示例地址

完整代码

<template>
    <div>
        <p class="title">引导层演示</p>
        <ul class="menus">
            <li id="menu1">
                菜单1
            </li>
            <li id="menu2">
                菜单2
            </li>
            <li id="menu3">
                菜单3
            </li>
        </ul>
        <div class="buts">
            <div class="but" @click="init">重新开始</div>
            <div class="but" @click="next">下一步</div>
            <div class="but" @click="skip">跳过引导</div>
        </div>
        <template v-if="haveList.length">
            <div class="info" v-html="info" :style="{top: currentItem.top + currentItem.height + 10 + 'px', left: currentItem.left + 'px'}"></div>
            <svg class="svg" :viewBox="'0 0 ' + bodyWidth + ' ' + bodyHeight" xmlSpace="preserve" preserveAspectRatio="xMinYMin slice">
                <path :d="pathD" style="fill: rgb(0, 0, 0); opacity: 0.7; pointer-events: auto; cursor: auto;"> 
                </path>
            </svg>
        </template>
    </div>
</template>

<script>
export default {
    data() {
        return {
            bodyHeight: document.body.clientHeight,
            bodyWidth: document.body.clientWidth,
            allList: [
                {
                    id: 'menu1',
                    title: '菜单1',
                    info: '第一个菜单'
                },
                {
                    id: 'menu2',
                    title: '菜单2',
                    info: '第二个菜单'
                },
                {
                    id: 'menu3',
                    title: '菜单3',
                    info: '第三个菜单'
                },
            ],
            haveList: [],
            currentIndex: 0,
        }
    },
    computed: {
        currentItem () {
            return this.haveList[this.currentIndex] || {}
        },
        isLast () {
            return this.currentIndex === this.haveList.length - 1
        },
        pathD() {
            // 有时获取到的位置和想留出空白的位置不太一样,可以增加些偏移量
            const leftSkewing = 0
            const topSkewing = 0
            const str = `M${this.bodyWidth},0L0,0L0,${this.bodyHeight}L${this.bodyWidth},${this.bodyHeight}L${this.bodyWidth},0Z M${this.currentItem.left + leftSkewing},${this.currentItem.top + topSkewing} h${this.currentItem.width} v${this.currentItem.height} h-${this.currentItem.width} v-${this.currentItem.height} z`
            console.log('str', str)
            return str
        },
        info() {
            return this.currentItem.title + '<br>' + this.currentItem.info
        },
    },
    methods: {
        init() {
            this.currentIndex = 0
            this.haveList = this.allList.filter(item => {
                item.dom = document.querySelector('#' + item.id)
                if (item.dom) {
                const rect = item.dom.getBoundingClientRect()
                item.left = rect.left
                item.top = rect.top
                item.width = item.dom.clientWidth
                item.height = item.dom.clientHeight
                }
                return Boolean(item.dom)
            })
            // 监听屏幕大小和滚动变化来重置框的位置
            window.addEventListener('resize', this.resize, true)
            window.addEventListener('scroll', this.resize, true)
        },
        next () {
            if (this.isLast) {
                this.skip()
                // this.haveList = []
            } else {
                this.currentIndex += 1
            }
        },
        skip () {
            this.haveList = []
            this.currentIndex = 0
            // 结束时移除
            window.removeEventListener('resize', this.resize, true)
            window.removeEventListener('scroll', this.resize, true)
        },
        resize () {
            this.bodyWidth = document.body.clientWidth
            this.bodyHeight = document.body.clientHeight
            this.init()
        }
    },
    mounted() {
        this.$nextTick(() => {
            this.resize()
        })
    },
}
</script>

<style lang="less" scoped>
.title { 
    font-size: 20px;
    text-align: center;
    padding: 100px 0 20px;
}

.svg{
    fill-rule: evenodd;
    clip-rule: evenodd;
    stroke-linejoin: round;
    stroke-miterlimit: 2;
    z-index: 1000;
    position: fixed;
    top: 0px;
    left: 0px;
    width: 100%;
    height: 100%;
  }
  .buts{
    position: relative;
    z-index: 1001;
    padding: 20px;
    display: flex;
    justify-content: space-around;
    top: 300px;
    background-color: #fff;
    .but{
        cursor: pointer;
    }
  }

  .menus{
    position: relative;
    z-index: 1001;
    padding: 20px;
    display: flex;
    justify-content: space-around;
    li{
        padding: 30px;
    }
  }

  .info{
    position: absolute;
    z-index: 1001;
    background-color: #fff;
    padding: 10px;
    width: 300px;
  }
</style>
  • 30
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晓_枫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值