应对现在数据可视化的趋势,越来越多企业需要在很多场景(营销数据,生产数据,用户数据)下使用,可视化图表来展示体现数据,让数据更加直观,数据特点更加突出。
一、使用技术
完成该项目需要具备以下知识:
-
div + css 布局
-
flex 布局(分为三列)
-
Less
-
原生js + jquery 使用
-
rem适配
-
echarts基础
基于flexible.js + rem 智能大屏适配
页面分为顶部的header和下面的主体区域。主体区域通过flex布局分为左中右三部分
新建项目目录为my-bigscreen文件夹,该文件夹下面新建css、images、font、js四个文件夹,新建index.html文件,初始内容如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>数据可视化</title> </head> <body> </body> </html>
使用vscode打开项目,如下所示
css目录下新建index.less,保存之后就会生成index.css
将css引入到index.html中,输入link:css就会快速生成link标签
<link rel="stylesheet" href="css/index.css">
css初始化
即在index.less文件中输入以下内容:
// css初始化 * { margin: 0; padding: 0; box-sizing: border-box; }
在body标签内输入123,安装preview on web server插件,然后在index.html中点击鼠标右键,选择vscode-preview-server:Launch on browser,即可浏览器中预览index.html文件
发现能看到123,说明样式引入成功。
二、案例适配方案
当屏幕尺寸发生变化时,页面内容相应缩放,通过flexible.js + rem来实现。
先在js文件夹中放入flexible.js文件,内容如下:
(function flexible(window, document) { var docEl = document.documentElement; var dpr = window.devicePixelRatio || 1; // adjust body font size function setBodyFontSize() { if (document.body) { document.body.style.fontSize = 12 * dpr + "px"; } else { document.addEventListener("DOMContentLoaded", setBodyFontSize); } } setBodyFontSize(); // set 1rem = viewWidth / 10 function setRemUnit() { var rem = docEl.clientWidth / 24; docEl.style.fontSize = rem + "px"; } setRemUnit(); // reset rem unit on page resize window.addEventListener("resize", setRemUnit); window.addEventListener("pageshow", function(e) { if (e.persisted) { setRemUnit(); } }); // detect 0.5px supports if (dpr >= 2) { var fakeBody = document.createElement("body"); var testElement = document.createElement("div"); testElement.style.border = ".5px solid transparent"; fakeBody.appendChild(testElement); docEl.appendChild(fakeBody); if (testElement.offsetHeight === 1) { docEl.classList.add("hairlines"); } docEl.removeChild(fakeBody); } })(window, document);
index.html中引入flexible.js,输入快捷键script:src即可快速输入代码
<body> 123 <script src="js/flexible.js"></script> </body>
保存,如何flexible.js引入成功呢?我们可以在页面审查元素,如果html标签出现font-size,并且font-size的值根据页面尺寸的大小变化而变化,说明flexible.js引入成功。
-
PC端,设计稿是1920px
-
flexible.js 把屏幕分为 24 等份
-
cssrem 插件的基准值是 80px
插件-配置按钮---配置扩展设置--Root Font Size 里面 设置。
但是别忘记重启vscode软件保证生效
注意:flexible.js默认将屏幕划分成10等分,代码如下:
function setRemUnit() { var rem = docEl.clientWidth / 10; docEl.style.fontSize = rem + "px"; }
我们现在将屏幕宽度划分位24等分,修改代码如下:
function setRemUnit() { var rem = docEl.clientWidth / 24; docEl.style.fontSize = rem + "px"; }
即页面的1rem为80px。
安装px to rem & rpx & vw (cssrem)插件,安装完之后,点击设置按钮,如下所示:
选择Extension Settings,将Root Font Size修改为80即可
这样会自动帮我们生成rem,现在来测试是否帮我们生成成功。
在index.html中输入.box命令就会生成class名称为box的div标签,然后在index.less文件中写样式如下:
.box{ width: 1rem; height: 1rem; background-color: pink; }
可以看到自动帮我们转为1rem,当我们输入80px时,会有提示如下:
当我们选择80px->1rem时,则会输入1rem
保存后在浏览器中查看,如下所示:
发现当页面尺寸发生变化时,div的尺寸也会相应变化。
三、基础设置
-
body 设置背景图 ,缩放为 100% , 行高1.15
-
css初始化
先在images目录下添加bg.jpg文件,然后将该图片设置为背景图
body { background: url(../images/bg.jpg); }
浏览器查看如下:
发现背景图平铺了,我们可以通过background-size值为cover设置完全满铺容器
body { /* 背景图定位 / 背景图尺寸 cover 完全铺满容器 contain 完整显示在容器内 */ background: url(../images/bg.jpg) no-repeat #000; background-size: cover; }
效果如下:
这样,背景图片设置成功。
设置行高
body { /* 背景图定位 / 背景图尺寸 cover 完全铺满容器 contain 完整显示在容器内 */ background: url(../images/bg.jpg) no-repeat #000; background-size: cover; /* 行高是字体1.15倍 */ line-height: 1.15; }
四、header 布局
-
高度为100px
-
背景图,在容器内显示
-
缩放比例为 100%
-
h1 标题部分 白色 38像素 居中显示 行高为 80像素
-
时间模块 showTime 定位右侧 right 为 30px 行高为 75px 文字颜色为:rgba(255, 255, 255, 0.7) 而文字大小为 20像素
在index.html中输入头部的盒子
<body> <!-- 头部的盒子 --> <header></header> <script src="js/flexible.js"></script> </body>
将head_bg.png放入images目录中
index.less中编写样式
header { height: 1.25rem; // background-color: pink; background: url(../images/head_bg.png) no-repeat; background-size: 100% 100%; }
用h1标签编写标题
<body> <!-- 头部的盒子 --> <header> <h1>数据可视化-Echarts</h1> </header> <script src="js/flexible.js"></script> </body>
编写样式
header { height: 1.25rem; // background-color: pink; background: url(../images/head_bg.png) no-repeat; background-size: 100% 100%; h1 { color: #fff; font-size: .475rem; text-align: center; line-height: 1rem; } }
效果如下:
编写时间的div,时间先写死
<body> <!-- 头部的盒子 --> <header> <h1>数据可视化-Echarts</h1> <div class="showTime">当前时间:2023年3月15日 11时22分12秒</div> </header> <script src="js/flexible.js"></script> </body>
编写样式
header { position: relative; height: 1.25rem; // background-color: pink; background: url(../images/head_bg.png) no-repeat; background-size: 100% 100%; h1 { color: #fff; font-size: .475rem; text-align: center; line-height: 1rem; } .showTime { position: absolute; top: 0; right: .375rem; line-height: .9375rem; font-size: .25rem; color: rgba(255,255,255,0.7); } }
注意:子绝父相,即子为绝对定位,则父为相对定位。
效果如下:
通过js代码来生成时间
<body> <!-- 头部的盒子 --> <header> <h1>数据可视化-Echarts</h1> <div class="showTime"></div> <script> var t = null; t = setTimeout(time, 1000);//開始运行 function time() { clearTimeout(t);//清除定时器 dt = new Date(); var y = dt.getFullYear(); var mt = dt.getMonth() + 1; var day = dt.getDate(); var h = dt.getHours();//获取时 var m = dt.getMinutes();//获取分 var s = dt.getSeconds();//获取秒 document.querySelector(".showTime").innerHTML = '当前时间:' + y + "年" + mt + "月" + day + "-" + h + "时" + m + "分" + s + "秒"; t = setTimeout(time, 1000); //设定定时器,循环运行 } </script> </header> <script src="js/flexible.js"></script> </body>
即每一秒更改一次数据,循环运行。
五、mainbox 主体区域
-
需要一个上左右的10px 的内边距
-
column 列容器,分三列,占比 3:5:3
index.html中编写主体区域的div,输入section.mainbox快速生成代码
<!-- 主体区域 --> <section class="mainbox">主体区域</section>
编写样式
.mainbox { // 当宽度小于1024就不会再缩放了 min-width: 1024px; max-width: 1920px; margin: 0 auto; padding: .125rem .125rem 0; background-color: pink; }
效果如下:
index.html中输入.column*3就会生成三个class为column的div
<!-- 主体区域 --> <section class="mainbox"> <div class="column">1</div> <div class="column">2</div> <div class="column">3</div> </section>
编写样式
.mainbox { display: flex; // 当宽度小于1024就不会再缩放了 min-width: 1024px; max-width: 1920px; margin: 0 auto; padding: .125rem .125rem 0; background-color: pink; .column { flex: 3; } .column:nth-child(2) { flex: 5; } }
效果如下:
六、公共面板模块 panel
-
高度为 310px
-
1像素的 1px solid rgba(25, 186, 139, 0.17) 边框
-
有line.jpg 背景图片
-
padding为 上为 0 左右 15px 下为 40px
-
下外边距是 15px
-
利用panel 盒子 before 和after 制作上面两个角 大小为 10px 线条为 2px solid #02a6b5
-
新加一个盒子before 和after 制作下侧两个角 宽度高度为 10px
编写一个class为panel的div
<!-- 主体区域 --> <section class="mainbox"> <div class="column"> <div class="panel"></div> </div> <div class="column">2</div> <div class="column">3</div> </section>
编写样式
.mainbox { display: flex; // 当宽度小于1024就不会再缩放了 min-width: 1024px; max-width: 1920px; margin: 0 auto; padding: .125rem .125rem 0; // background-color: pink; .column { flex: 3; .panel { height: 3.875rem; background-color: pink; } } .column:nth-child(2) { flex: 5; } }
效果如下:
继续编写样式
.column { flex: 3; .panel { height: 3.875rem; // background-color: pink; border: .0125rem solid rgba(25,186,139,0.17); background: url(../images/line.png) rgba(255,255,255,0.04); padding: 0 .1875rem .5rem; margin-bottom: .1875rem; } }
效果如下:
编写四个角
.column { flex: 3; .panel { position: relative; height: 3.875rem; // background-color: pink; border: .0125rem solid rgba(25,186,139,0.17); background: url(../images/line.png) rgba(255,255,255,0.04); padding: 0 .1875rem .5rem; margin-bottom: .1875rem; &::before { position: absolute; top: 0; left: 0; width: .125rem; height: .125rem; border-left: 2px solid #02a6b5; border-top: 2px solid #02a6b5; content: "1"; } } }
效果如下:
同理,右上角样式编写
&::after { position: absolute; top: 0; right: 0; width: .125rem; height: .125rem; border-right: 2px solid #02a6b5; border-top: 2px solid #02a6b5; content: "1"; } }
在panel盒子中加一个div
<!-- 主体区域 --> <section class="mainbox"> <div class="column"> <div class="panel"> <div class="panel-footer"></div> </div> </div> <div class="column">2</div> <div class="column">3</div> </section>
编写样式
.panel-footer { position: absolute; bottom: 0; left: 0; width: 100%; &::before { position: absolute; bottom: 0; left: 0; width: .125rem; height: .125rem; border-left: 2px solid #02a6b5; border-bottom: 2px solid #02a6b5; content: ""; } &::after { position: absolute; bottom: 0; right: 0; width: .125rem; height: .125rem; border-right: 2px solid #02a6b5; border-bottom: 2px solid #02a6b5; content: ""; } }
注意:一个盒子只有一个before和一个after
效果如下:
七、柱形图 bar 模块(布局)
-
标题模块 h2 高度为 48px 文字颜色为白色 文字大小为 20px
-
图标内容模块 chart 高度 240px
-
以上可以作为panel公共样式部分
每个panel盒子分为上下两部分,上部为标题,下部为内容
添加标题和内容的盒子
<!-- 主体区域 --> <section class="mainbox"> <div class="column"> <div class="panel bar"> <h2>柱形图-就业行业</h2> <div class="chart">图表</div> <div class="panel-footer"></div> </div> </div> <div class="column">2</div> <div class="column">3</div> </section>
编写样式
h2 { height: .6rem; font-size: .25rem; line-height: .6rem; color: #fff; text-align: center; } .chart{ height: 3rem; background-color: pink; }
效果如下:
由于左侧的三个图样式相同,我们复制三分
<!-- 主体区域 --> <section class="mainbox"> <div class="column"> <div class="panel bar"> <h2>柱形图-就业行业</h2> <div class="chart">条形图</div> <div class="panel-footer"></div> </div> <div class="panel line"> <h2>柱形图-就业行业</h2> <div class="chart">折线图</div> <div class="panel-footer"></div> </div> <div class="panel pie"> <h2>柱形图-就业行业</h2> <div class="chart">饼图</div> <div class="panel-footer"></div> </div> </div> <div class="column">2</div> <div class="column">3</div> </section>
效果如下:
由于右侧三个图和左侧三个图样式是一样的,我们也复制一份
<!-- 主体区域 --> <section class="mainbox"> <div class="column"> <div class="panel bar"> <h2>柱状图-就业行业</h2> <div class="chart">柱状图</div> <div class="panel-footer"></div> </div> <div class="panel line"> <h2>折线图-人员变化</h2> <div class="chart">折线图</div> <div class="panel-footer"></div> </div> <div class="panel pie"> <h2>饼形图-年龄分布</h2> <div class="chart">饼图</div> <div class="panel-footer"></div> </div> </div> <div class="column">2</div> <div class="column"> <div class="panel bar"> <h2>柱状图-技能掌握</h2> <div class="chart">柱状图</div> <div class="panel-footer"></div> </div> <div class="panel line"> <h2>折线图-播放量</h2> <div class="chart">折线图</div> <div class="panel-footer"></div> </div> <div class="panel pie"> <h2>饼形图-地区分布</h2> <div class="chart">饼图</div> <div class="panel-footer"></div> </div> </div> </section>
效果如下:
八、中间布局
-
上面是no 数字模块
-
下面是map 地图模块
-
数字模块 no 有个背景颜色 rgba(101, 132, 226, 0.1); 有个15像素的内边距
-
注意中间列 column 有个 左右 10px 下 15px 的外边距
-
no 模块里面上下划分 上面是数字(no-hd) 下面 是 相关文字说明(no-bd)
-
no-hd 数字模块 有一个边框 1px solid rgba(25, 186, 139, 0.17)
-
no-hd 数字模块 里面分为两个小li 每个小li高度为 80px 文字大小为 70px 颜色为 #ffeb7b 字体是图标字体 electronicFont
-
no-hd 利用 after 和 before制作2个小角, 边框 2px solid #02a6b5 宽度为 30px 高度为 10px
-
小竖线 给 第一个小li after 就可以 1px宽 背景颜色为 rgba(255, 255, 255, 0.2); 高度 50% top 25% 即可
-
no-bd 里面也有两个小li 高度为 40px 文字颜色为 rgba(255, 255, 255, 0.7) 文字大小为 18px 上内边距为 10px
/* 声明字体*/ @font-face { font-family: electronicFont; src: url(../font/DS-DIGIT.TTF); }
中间部分分为上下两个模块
编写两个盒子
<div class="column"> <div class="no">上面数字模块</div> <div class="map">下面地图模块</div> </div>
编写样式
.column:nth-child(2) { flex: 5; margin: 0 0.125rem 0.1875rem; .no { background: rgba(101,132,226,0.1); // background: pink; padding: .1875rem; } }
效果:
上面的num部分也分为上面的数字和下面的文字两部分
编写上下两个盒子
<div class="column"> <div class="no"> <div class="no-hd">数字</div> <div class="no-bd">文字</div> </div> <div class="map">下面地图模块</div> </div>
编写样式
.column:nth-child(2) { flex: 5; margin: 0 0.125rem 0.1875rem; .no { background: rgba(101,132,226,0.1); // background: pink; padding: .1875rem; .no-hd { border: 1px solid rgba(25,186,139,0.17); } } }
两个数字通过ul下面的两个li来表示
<div class="column"> <div class="no"> <div class="no-hd"> <ul> <li>124124</li> <li>253242</li> </ul> </div> <div class="no-hd">文字</div> </div> <div class="map">下面地图模块</div> </div>
效果如下:
编写样式
.column:nth-child(2) { flex: 5; margin: 0 0.125rem 0.1875rem; .no { background: rgba(101,132,226,0.1); // background: pink; padding: .1875rem; .no-hd { border: 1px solid rgba(25,186,139,0.17); ul { display: flex; li { flex: 1; } } } } }
效果如下:
no-hd 数字模块 里面分为两个小li 每个小li高度为 80px 文字大小为 70px 颜色为 #ffeb7b
.column:nth-child(2) { flex: 5; margin: 0 0.125rem 0.1875rem; .no { background: rgba(101,132,226,0.1); // background: pink; padding: .1875rem; .no-hd { border: 1px solid rgba(25,186,139,0.17); ul { display: flex; li { flex: 1; line-height: 1rem; font-size: .875rem; color: #ffeb7b; } } } } }
效果如下:
下面去掉小圆点,并让文字居中显示,
li { list-style: none; }
字体是图标字体 electronicFont
先在font文件夹下面添加DS-DIGIT.TTF文件,再声明图标字体,如下所示:
/* 声明字体*/ @font-face { font-family: electronicFont; src: url(../font/DS-DIGIT.TTF); }
在通过font-family使用该字体
.column:nth-child(2) { flex: 5; margin: 0 0.125rem 0.1875rem; .no { background: rgba(101,132,226,0.1); // background: pink; padding: .1875rem; .no-hd { border: 1px solid rgba(25,186,139,0.17); ul { display: flex; li { flex: 1; line-height: 1rem; font-size: .875rem; color: #ffeb7b; text-align: center; font-family: "electronicFont"; } } } } }
效果如下:
编写左上和右下两个角,样式如下:
.no-hd { position: relative; border: 1px solid rgba(25,186,139,0.17); ul { display: flex; li { flex: 1; line-height: 1rem; font-size: .875rem; color: #ffeb7b; text-align: center; font-family: "electronicFont"; } } &::before { position: absolute; top: 0; left: 0; width: .375rem; height: .125rem; border-left: 2px solid #02a6b5; border-top: 2px solid #02a6b5; content: ""; } &::after { position: absolute; bottom: 0; right: 0; width: .375rem; height: .125rem; border-right: 2px solid #02a6b5; border-bottom: 2px solid #02a6b5; content: ""; } }
效果如下
中间的竖线样式编写:给第一个li添加after伪元素
ul { display: flex; li { position: relative; flex: 1; line-height: 1rem; font-size: .875rem; color: #ffeb7b; text-align: center; font-family: "electronicFont"; &::after { content: ""; position: absolute; top: 25%; right: 0; height: 50%; width: 1px; background: rgba(255,255,255,0.2); } } }
注意:子绝父相。
效果如下:
先在编写下面的文字部分:
<div class="no-bd"> <ul> <li>前端需求人数</li> <li>市场供应人数</li> </ul> </div>
编写样式
.no-bd { ul { display: flex; li { flex: 1; height: 0.5rem; line-height: .5rem; font-size: .225rem; color: rgba(255,255,255,0.7); text-align: center; font-family: "electronicFont"; padding-top: .125rem; } } }
效果如下:
地图模块制作:
-
地图模块高度为 810px 里面包含4个盒子: chart 放图表模块(地图后面再做)、 球体盒子、 旋转1、 旋转2。
-
球体图片模块 map1 大小为 518px 要加背景图片 因为要缩放100% 定位到最中央 透明度 .3
-
旋转1 map 2 大小为 643px 要加背景图片 因为要缩放100% 定位到中央 透明度 .6 做旋转动画 利用z-index压住球体
-
旋转2 map3 大小为 566px 要加背景图片 因为要缩放100% 定位到中央 旋转动画 注意是逆时针
<div class="map">下面地图模块</div>
编写map的样式
.map { height: 10.125rem; background-color: pink; }
效果如下:
编写球体盒子
<div class="map"> <div class="map1"></div> </div>
images文件夹中放入背景图片map.png
编写样式
.map { position: relative; height: 10.125rem; // background-color: pink; .map1 { width: 6.475rem; height: 6.475rem; background: url(../images/map.png); position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); background-size: 100% 100%; opacity: 0.3; } }
background-size第一个值指定图片的宽度,第二个值指定图片的高度。
效果如下:
旋转1编写
<div class="map2"></div>
images文件夹下添加lbx.png,
编写样式如下:
.map2 { width: 8.0375rem; height: 8.0375rem; position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); background: url(../images/lbx.png); background-size: 100%, 100%; }
效果如下:
通过animation动画来实现不停的旋转,刚开始为0度,再旋转360度
.map2 { width: 8.0375rem; height: 8.0375rem; position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); background: url(../images/lbx.png); background-size: 100%, 100%; // 调用动画 linear为匀速 infinite为无线循环 animation: rotate1 15s linear infinite; } // 定义动画 translate(-50%,-50%)保证居中旋转,从0度开始旋转,360结束 @keyframes rotate1 { from { transform: translate(-50%,-50%) rotate(0deg); } to { transform: translate(-50%,-50%) rotate(360deg); } }
发现背景图片旋转起来了
由于map2的div在map1的div下面,这样map2默认会压住map1,故不利用z-index压住球体。
编写旋转的箭头
<div class="map"> <div class="map1"></div> <div class="map2"></div> <div class="map3"></div> </div>
images文件夹中放入jt.png
如何实现逆时针旋转?从0度开始,到-360度结束
编写样式
.map3 { width: 7.075rem; height: 7.075rem; position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); background: url(../images/jt.png); background-size: 100%, 100%; // 调用动画 linear为匀速 infinite为无线循环 animation: rotate2 10s linear infinite; } // 定义动画 translate(-50%,-50%)保证居中旋转,从0度开始旋转,360结束 @keyframes rotate2 { from { transform: translate(-50%,-50%) rotate(0deg); } to { transform: translate(-50%,-50%) rotate(-360deg); } }
效果如下:
编写地图模块
<div class="map"> <div class="map1"></div> <div class="map2"></div> <div class="map3"></div> <div class="chart">地图模块</div> </div>
编写样式
.chart { position: absolute; top: 0; left: 0; background-color: pink; width: 100%; height: 10.0125rem; }
效果如下:
这样地图就占据了map的大小。