svg的内容很多,这里只用到了其中的路径,其他内容就不介绍了。
svg的使用方法
svg参数介绍
<svg class="svg" :viewBox="'0 0 100 100" xmlSpace="preserve" preserveAspectRatio="xMinYMin slice">
</svg>
参数 | 是否必须 | 描述 |
---|---|---|
viewBox | 是 | 四个参数分别代表最小 X 坐标、最小 Y 坐标、宽度和高度 |
xmlSpace | 否 | preserve:这个属性告诉 SVG 解析器保留所有空白字符,包括空格、换行符等。如果不设定此属性,XML 解析器可能会忽略多余的空白 |
preserveAspectRatio | 否 | xMinYMin 表示图形应该相对于容器的左上角对齐,slice 表示如果图形宽高比与容器不同,则会在必要时裁剪(slice)图形以填满整个可见区域,而不是拉伸(stretch)图形。其他可能的取值还有如 meet(保持纵横比并使图形内部尽可能填充容器)、none(不保持纵横比)等。 |
path的参数介绍
<path :d="" style="">
参数 | 是否必须 | 描述 |
---|---|---|
d | 是 | 绘制路径的指令以及参数。 |
style | 否 | 显示样式,主要用于规定了绘制矩形的样式属性,详情参考显示属性。 |
以下参数中大写的指令代表了绝对定位(以画布左上角为0点),而小写则代表相对定位(以上一个点为0点)
横向为x,纵向为y
指令 | 参数 | 描述 |
---|---|---|
M | x,y | 路径的其实位置 |
L | x,y | 画一条线到指定x,y位置 |
H | x | 画一条水平线到指定x位置,y值不变 |
V | y | 画一条垂直线到指定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>