项目场景:
在数据可视化里面,我们可以发现一个大屏展示,是有多个小的模块组合而成,然而这个小模块又是有模块边框和图表构成,大多数情况中,这些小模块的边框是一样,然而又有的图案比较复杂,用css是很难直接写出来,办法当然是有的。当然你除了选择用我说的这种方案,也可以选择用svg绘制,也是一个不错的选择。如下图,就是我们要实现的效果
学习过程中的代码
解决方案:
1、基础知识
我们绘制图片边框采用的原理是css3中样式:
值 | 描述 |
---|---|
border-image-source | 用于指定要用于绘制边框的图像的位置 |
border-image-slice | 图像边界向内偏移 |
border-image-width | 图像边界的宽度 |
border-image-outset | 用于指定在边框外部绘制 border-image-area 的量 |
border-image-repeat | 用于设置图像边界是否应重复(repeat)、拉伸(stretch)或铺满(round)。 |
其中border-image-source是表示边框图片的位置,border-image-width是表示指定边的边框图片宽度,border-image-outset 用于指定在边框外部绘制 border-image-area 的量,border-image-repeat 用于设置图像边界是否应重复(repeat)、拉伸(stretch)或铺满(round)。其中最重要的就是border-image-slice属性,它的作用是来指定边框的位置如何来划分。border-image-slice属性有四个参数top,right,bottom,left。它们分别的意思是:
- top:从顶部开始的一段距离
- right:从右侧开始的一段距离
- bottom:从底部开始的一段距离
- left:从左侧开始的一段距离
用四刀把图片分为了四个部分,其作用是为了把四个边角裁剪出来
裁剪之后,红色的部分就为空,而边角就会和容器的边角在一起
2、快速搭建实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
#container {
border: 1px solid transparent;
border-width: 27px 27px 28px 29px;
border-image-source: url("./border.png");
border-image-slice: 27 27 28 29;
border-image-width: 27px 27px 28px 29px;
background: #ddd;
}
</style>
</head>
<body>
<div id="container" style="width: 500px; height: 400px;">
</div>
</body>
</html>
这就是那一张border.png的图片
最后渲染出来的效果是
我们可以明显看出,图片中间被掏空,然后四个边角固定在容器的四个角上,而边则进行了拉伸。从源码中,我有个属性没有设置border-image-repeat,添加上之后的效果:
- border-image-repeat:repeat
- border-image-repeat:round
- border-image-repeat:stretch
这样就可以发现,默认选择就是拉伸,而铺满和重复的区别是:round是进行计算铺满,都是完整图形,repeat是重复,会有不完整图形存在。
3、绘制边框,实现数据可视化模块
我更换了图片为模块的边框,测算出模块的边框为 9 40 9 40
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.container-border {
width: 300px;
height: 200px;
border: 1px solid transparent;
border-width: 9px 40px 9px 40px;
border-image-source: url("./border-2.png");
border-image-slice: 9 40 9 40;
border-image-width: 9px 40px 9px 40px;
background-color: rgba(0, 0, 0, .5);
}
</style>
</head>
<body>
<div class="container-border"></div>
</body>
</html>
这个容器看似没有问题,因为我们的边框选择是一个规则图形,并不会出现很大问题。但是如果是一个不规则的边框图形,很有可能图片的内容区域,还是大小都会出现问题,比如下面的这个
这个如果容器里面再放一个图表,就会出现很大的问题。因此我改变了布局方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
#container {
position: relative;
}
.container-border {
width: 100%;
height: 100%;
border: 9px solid transparent;
border-width: 9px 40px 9px 40px;
border-image-source: url("./border-2.png");
border-image-slice: 9 40 9 40;
border-image-width: 9px 40px 9px 40px;
border-image-repeat: stretch;
background-color: rgba(0, 0, 0, .5);
}
.container-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
</style>
</head>
<body>
<div id="container" style="width:600px;height:300px;">
<div class="container-border"></div>
<div class="container-content">123456789</div>
</div>
</body>
</html>
这样一个图表的边框就绘制好了。只需要在container-content里绘制图表及内容即可。
4、实现通用的可视化模块容器html5
这里我就先直接上代码吧,这里是调用写好的模块代码,在页面先创建一个container容器,在下面还有一个div是用来展示图表或者信息内容用的。js上创建TwokeDatav对象,调用createModeContainer方法。
键 | 含义 |
---|---|
el | 容器id |
source | 边框路径 |
top | 边框图片顶部到上边角的有效区 |
right | 边框图片右侧到右边角的有效区 |
bottom | 边框图片底部到下边角的有效区路径 |
left | 边框图片左侧到左边角的有效区 |
padding | el容器的内边距 |
width | 内容区域的宽度 |
height | 内容区域的高度 |
注:有效区是指切角时,必须把边角边框包含上,以最长的边角边框作临界值,当设置的高度和宽度时,padding会失效,以content的宽高为准
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../dist/index.js"></script>
<style>
#container {
width: 500px;
height: 300px;
background-color: #eee;
overflow: hidden;
}
#content {
background: red;
}
</style>
</head>
<body>
<div id="container">
<div id="content">
1234567890
</div>
</div>
<script>
var datav = new $datav.TwoKeDatav()
datav.createModeContainer({
el: "container",
padding: [50,30],
border: {
source: './border-2.png',
top: 9,
right: 40,
bottom: 9,
left: 40,
},
content: {
width: 480,
height: 280
}
})
</script>
</body>
</html>
当我们不配置宽高时
码云项目地址
其原理和上面的原理是一样的,只是将一些通用操作封装了起来。如果感兴趣可以去码云看一看源码,原理很简单。下一次就用vue3实现一下模块边框绘制的组件。