一个大屏项目的小总结

一、技术总结

1.屏幕适配

通过window.screen.width获取系统屏幕宽度,计算出与设计稿的比例,用document.body.style.zoom对页面进行比例缩放
例如:屏幕1680*1050,zoom = 1680/1920=0.875,页面缩小0.875
(对于某些系统可以放大缩小系统比例的,window.screen.width获取到的值是电脑分辨率/放大比例得到的宽度,所以可以保证浏览器页面内容的大小不受系统放大比例的影响)

1
document.body.style.zoom = window.screen.width / 1920
2.高德地图

为什么选择高德地图呢?经过前期调研,高德地图渲染比较流畅,过渡比较自然,百度地图会出现白色的色块,不太符合大屏项目的气质。而且高德地图api也很完善,能满足开发的需求。

(1)地图引入

脚本引入

1
2
3
4
5
6
7
8
//加载JS API;添加自己申请的key
//key:d87dc2da5a41ae6fd9d135acaa50989d
//现在高德地图api版本已经出到v2.0啦
//通过plugin按需加载插件
<script
src="//webapi.amap.com/maps?v=1.4.15&key=d87dc2da5a41ae6fd9d135acaa50989d&
plugin=AMap.CustomLayer,AMap.DistrictLayer,AMap.DistrictSearch,AMap.Scale,AMap.ToolBar,AMap.Geocoder">
</script>

创建实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//创建地图
let dom = document.getElementById("map")
let map = new AMap.Map(dom, {
pitch: 30, //俯仰角度
viewMode: '3D',//地图效果 2D/3D
zoom: 4.5,//缩放级别 3D模式下可设浮点数
dragEnable: false,//地图是否可通过鼠标拖拽平移
resizeEnable: false,//是否监控地图容器尺寸变化
expandZoomRange: false,//扩展最大缩放级别
zoomEnable: false,//地图是否可缩放
center: [118.590936, 40.784202],//中心点坐标
mapStyle: "amap://styles/41484b023e45e8db0730860bb21ce8de",//自定义地图样式
rotation: 0, //顺时针旋转角度
})

//地图挂载的dom
<div id="map" style={{ width: "1920px", height: "1080px", background: '#000' }}></div>
(2)自定义地图样式

高德地图支持自定义地图样式,根据需要对地图底图样式进行调整,权限越高,能调整的元素就越多,氪金。可以在高德已有的地图模版上进行自定义。

自定义地图样式,发布后就可以在项目中应用了。

通过地图的mapStyle属性引用

1
mapStyle: "amap://styles/41484b023e45e8db0730860bb21ce8de",//地图样式

注意:地图中使用自定义样式,申请key值的账号,要与自定义地图的账号一致,不然貌似样式会不起作用。
(不能自定义中国边界;1.4.x版本的api在缩放比例小于4的时候,不显示外国国界)

(3)图层

高德地图提供了几种图层插件,例如比较熟悉的卫星图,路况图,还支持第三方,自定义等;可以使地图功能性更强,使区域、边界等达到可视化效果。

例子:简易行政区图层

脚本引入

1
2
3
4
//通过plugin加载插件
<script
src="//webapi.amap.com/maps?v=1.4.15&key=d87dc2da5a41ae6fd9d135acaa50989d&plugin=AMap.DistrictLayer">
</script>

创建实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//国家简易行政区图层
//通过添加国家图层,使中国在世界地图中突出显示
this.disCountry = new AMap.DistrictLayer.Country({
SOC: 'CHN',
depth: 1,
styles: {
'province-stroke': 'rgb(112,165,236)',//省份边界颜色
'city-stroke': '#22ffff', // 中国地级市边界
'coastline-stroke': '#22ffff',//中国海岸线
'nation-stroke': '#22ffff',//中国边境线
'fill': function () { //中国特有字段,中国地图填充颜色
return 'rgb(5,40,101)'
}
}
})
this.disCountry.setMap(map)
(4)可视化图层Loca

Loca是一个基于高德 JS API 地图、纯 JavaScript 实现的地理空间数据可视化渲染引擎,可视化图层种类可能没有echarts丰富,但是基本够用。其特点是在无需了解高德 JS API 的情况下,通过灵活的配置,可以快速制作出如散点、轨迹、区面、热力图等地理位置相关的可视化作品。(区别于高德图层插件,可视化图层更加灵活,更加多样)

例子:散点图层

脚本引入

1
2
//引入图层JS API
<script src="//webapi.amap.com/loca?v=1.3.2&key=d87dc2da5a41ae6fd9d135acaa50989d"></script>

创建实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//创建 图层
let layer = new Loca.PointLayer({
map: this.myAmap
});

//点坐标
let pointData = [
{"lnglat": [99.151158,19.181328]},
{"lnglat": [100.36538,18.093919]},
{"lnglat": [99.335131,16.396768]},
{"lnglat": [103.640098,16.183533]},
{"lnglat": [101.395628,15.185395]},
{"lnglat": [99.592693,14.935099]},
{"lnglat": [99.592693,14.935099]},
{"lnglat": [99.592693,14.935099]},
{"lnglat": [101.616396,13.535306]},
{"lnglat": [102.830576,16.541832]},
{"lnglat": [103.124933,15.116973]},
{"lnglat": [101.947506,17.532988]},
{"lnglat": [101.064436,16.541832]},
{"lnglat": [105.038252,15.545493]}
]

// 传入原始数据
layer.setData(pointData, {
lnglat: 'lnglat' // 指定坐标数据的来源,数据格式: 经度在前,维度在后,数组格式。
});

// 配置样式
layer.setOptions({
unit: 'px',
style: {
radius: 2.2, // 圆形半径,单位像素
color: '#1e5ef6', // 填充颜色
borderWidth: 0.5, // 边框宽度
opacity: function (params) {//随机计算每个点的透明度
if (params.index > 1) {
let num = (Math.floor(Math.random() * 10) + 1) / 10;
if (num < 0.4) num = num + 0.2;
return num;
}
},
}
});
layer.render();
layer.setMap(this.myAmap);
(5)其它的一些api应用

例子1:自定义覆盖物

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//那个点点点的光  
let size = 250;
// 自定义一个Icon
let Icon = new window.AMap.Icon({
size: new window.AMap.Size(size, size),// 图标尺寸
image: image,// 图标的取图地址
imageSize: new window.AMap.Size(size, size),// 图标所用图片大小
imageOffset: new window.AMap.Pixel(-9, -3) // 图标取图偏移量
});
let marker = new window.AMap.Marker({
position: centerPoint,//覆盖物中心坐标点
icon: Icon,//自定义的Icon
offset: new window.AMap.Pixel(-size / 2, -size / 2),//覆盖物偏移量
});
marker.setMap(this.myAmap);//添加到地图上

//自定义一个信息框覆盖物
const div =
`<div class='continentCard continentCardadeInRight'>
<div class="continentCardInfo">
<p class="continentCardCountry">${name}</p>
<p class="continentCardContent">
<span><i>${countrys}</i>个国家</span>
<span><i>${citys}</i>个城市</span>
</p>
</div>
</div>`
let marker1 = new window.AMap.Marker({
position: centerPoint,
content: div,
offset: new window.AMap.Pixel(cardPosition.left, cardPosition.top)
});
marker1.setMap(this.myAmap);

例子2:行政区查询服务

通过行政区查询服务可以获取到查询对象的行政区信息,通过查询到的信息,重新绘制行政区边界

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 let opts = {
subdistrict: 0,
extensions: "all",
level: 'coutry'
};
let district = new window.AMap.DistrictSearch(opts);
let bounds;
//搜索中国行政区信息,保存边界
district.search("中国", (status, result) => {
bounds = result.districtList[0].boundaries;
});

//重新绘制中国边界
for (var i = 0; i < bounds.length; i += 1) {
let item =bounds[i];
new AMap.Polyline({
path: item,
strokeColor: '#4eb2f4',
strokeWeight: 2,
map: myAmap
})
}
3.Web Worker多线程

因为javascript是在单线程环境中的,无法同时运行多个脚本,也就是说要等一段代码执行完后,才能继续往下执行,假如要做复杂计算的话,页面就可能会出现卡顿几秒的情况。
Web Worker的作用,就是为 JavaScript 创造多线程环境,在主线程外创建一个worker线程,web worker是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能。
所以复杂的计算任务可以丢到worker线程执行,从而避免主线程阻塞。

使用限制

(1)同源限制
分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。
(2)DOM 限制
Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document、window、parent这些对象。但是,Worker 线程可以navigator对象和location对象。
(3)通信联系
Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。
(4)脚本限制
Worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。
(5)文件限制
Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。

例子:中国边界点计算

因为要重新绘制中国边界,但是获取到的点非常多,导致渲染需要很长时间,所以通过计算排除掉一部分的点,减少点的数量,提高渲染速度。因为计算也要很长时间,所以创了个worker线程来计算,避免阻塞主线程。

主线程js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//主线程采用new命令,调用Worker()构造函数,新建一个Worker线程
let worker = new Worker("./calc.js");
let opts = {
subdistrict: 0,
extensions: "all",
level: 'coutry'
};
let district = new window.AMap.DistrictSearch(opts);
district.search("中国", (status, result) => {
let bounds = result.districtList[0].boundaries;
//主线程调用worker.postMessage()方法,向 Worker 发消息
//worker.postMessage()方法的参数,就是主线程传给Worker的数据
worker.postMessage(bounds);
})
//主线程通过监听worker的message,接收子线程发回来的消息
worker.addEventListener('message', (event) => {
window.chinaBounds = event.data;
//Worker 完成任务以后,主线程就可以把它关掉
worker.terminate();
}, false);

子线程脚本calc.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//calc.js脚本文件
//用来计算的方法
var calc = function (data) {
let bounds = [];
for(let i in data){
if(data[i].length > 50){
let item = [];
for (let j in data[i]) {
if (j % 10 === 0) item.push(data[i][j]);
}
bounds.push(item);
}
}
return bounds;
};
//Worker线程内部需要有一个监听函数,监听message事件
onmessage = function (event) {
//postMessage方法用来向主线程发送消息,即系计算后的结果
postMessage(calc(event.data));
};
参考链接

Web Worker使用教程–阮一峰
MDN-Web Workers概念与用法
JavaScript 工作原理之七-Web Workers 分类及 5 个使用场景

4.地图图层转图片

由于地图加载、渲染的耗时比较久,用户体验不大好,所以第一次加载后,把地图图层转换成图片,用localStorage存储起来,后续直接用图片作为地图的底图,从而提高页面的性能与加载速度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//地图渲染完后,获取到渲染后的canvas,将其转换成jpg格式的图片,并用localStorage存储在浏览器中
function setLayoutPic(params) {
let canvas = document.getElementsByClassName('amap-layer')[0]
var base64Img = canvas.toDataURL('image/jpg');
localStorage.setItem('a', base64Img)
}

//第二次加载地图时,把图片绘制成图层,添加到地图上
function getLayout(params) {
let canvas = document.createElement('canvas')
let img = document.createElement('img')
var size = keDatasMap.getSize();//resize
var width = size.width;
var height = size.height;

canvas.style.width = width + 'px'
canvas.style.height = height + 'px'
canvas.width = width;
canvas.height = height;//清除画布

var ctx = canvas.getContext("2d");
img.src = localStorage.getItem('a')

//把缓存的图片绘制在画布上
//图片绘制完,通过高德地图的自定义图层的方法,将canvas作为图层添加到地图上
img.onload = () => {
ctx.drawImage(img, 0, 0, width, height)
var customLayer = new AMap.CustomLayer(canvas, {
zIndex:5
});
window.customLayer = customLayer
customLayer.setMap(keDatasMap)
}
}
5.echarts
(1)echarts的高德地图扩展echarts-amap

因为想要用echarts的飞线,但是呢,echarts自带的地图扩展只有百度地图,最后用了这个别人写的一个库。
echarts-amap支持echarts所有类型图表,只是将地理坐标系换成amap,其他跟geo,bmap基本一致,是基于extension-bmap改造的。所以echarts官网图表怎么结合bmap使用,就怎么结合amap使用。
(注意:因为是个人写的库,所以遇到问题要等修复的话,会比较久,目前好像还不支持高德v2.0版本,所以还是要慎重使用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//引入
require('echarts-amap');

//结合echarts使用,直接在option里面配置,amap的配置项与高德地图api保持一致
let chart = echarts.init(document.getElementById("keDatasMap"));
let option = {
amap: {
maxPitch: 60,
pitch: 15, //45 俯仰角
viewMode: '3D',
zoom: 3.22,
expandZoomRange: true,
zooms: [3, 20],
mapStyle: "amap://styles/61705e9081eaf0787d3e504a15209c64",//地图主题
center: [121.52223, 31.489085], //中心点
rotation: 0, //顺时针旋转角度
dragEnable: false,//禁止拖动
zoomEnable: false,//禁止缩放
showLabel: false,//隐藏地图的标签
},
series: []
}
chart.setOption(option);

//获取amap实例,通过这个实例任意使用高德地图的api
let myAmap = chart.getModel().getComponent('amap').getAMap();
(2)飞线图-lines

用的是echarts-lines(注意:飞行太快的话,那个线可能就一卡一卡的,点点分明,当然也有可能是我电脑太烂带不动。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
this.option.series.push(
{
name: "线路",
type: "lines",//echarts图表类型
coordinateSystem: "amap",//结合amap使用
zlevel: 2,
large: true,
animationDuration: 1000,
animationEasing: "linear",//飞线动画的缓动效果,就是加速,匀速,减速这些
effect: {
show: true,
loop: false,//是否循环飞
period: 1, //箭头指向速度,值越小速度越快
trailLength: 0, //特效尾迹长度[0,1]值越大,尾迹越长重
symbol: `image://${lightImg}`, //线最前面的样式,可以是一张图片
symbolSize: [50, 100] //图标大小
},
lineStyle: {
normal: {
color: {
type: 'linear',//线的颜色渐变
x: 0.5,
y: 0.5,
x2: 1,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(4,237,254,0.5)' },
{ offset: 0.25, color: 'rgba(4,237,254,1)' },
{ offset: 1, color: 'rgba(10,247,255,0)' }
],
},
width: 5,
opacity: 1,
curveness: name === "欧洲" || name === "亚洲" ? -0.3 : 0.3,//线的弯曲方向,负数与正数反向
}
},
data: moveLines
});
this.chart.setOption(this.option);
6.图片预加载

因为大屏各个页面用到的图片比较多,比较大,在浏览器没缓存图片的情况下,进入页面再加载图片会导致页面渲染完,但是图片资源还没准备好,首屏效果、体验不大好,所以对图片进行预加载。
例子:通过webpack插件获取所有图片资源,对图片资源集中进行预加载,加载完成后再显示页面

webpack插件

webpack 插件有以下特点:
(1)独立的 JS 模块,暴露相应的函数
(2)函数原型上的 apply 方法会注入 compiler 对象
(3)compiler 对象上挂载了相应的 webpack 事件钩子
(4)事件钩子的回调函数里能拿到编译后的 compilation 对象,如果是异步钩子还能拿到相应的 callback,’emit’就是异步钩子
compiler对象:compiler即webpack的编辑器对象,包含了所有webpack可配置的内容,compiler对象中拿到所有和webpack主环境相关的内容。
compilation对象:compilation 对象代表了一次单一的版本构建和生成资源,一个编译对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。

事件钩子:事件钩子其实就是类似 MVVM 框架的生命周期函数,在特定阶段能做特殊的逻辑处理。

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
//webpack.config.js中引入插件
const BasicPlugin = require('./BasicPlugin.js');

plugins: [
new BasicPlugin(() => {
console.log(123, 'end');
// process.exit()
// Webpack 构建成功,并且文件输出了后会执行到这里,在这里可以做发布文件操作
}, (err) => {
// Webpack 构建失败,err 是导致错误的原因
console.error(err);
}),
]

BasicPlugin.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//BasicPlugin.js插件文件
const fs = require('fs')
const path = require('./paths')
const paths = require('path')
const appPublic = path.appPublic

const servedPath = path.servedPath
const JSonPath = paths.resolve(appPublic, 'imgUrl.js');
class BasicPlugin {
constructor(doneCallback, failCallback) {
// 存下在构造函数中传入的回调函数
this.doneCallback = doneCallback;
this.failCallback = failCallback;
}

apply(compiler) {
//通过emit事件,获取图片资源相对路径,并写进imgUrl.js文件中
compiler.plugin('emit', function (compilation, callback) {
// 在生成资源并输出到目录之前完成某些逻辑
const ImgPattern = /.(jpg|png)$/
let arr = []
for (var filename in compilation.assets) {
if (ImgPattern.test(filename)) {
arr.push(servedPath + filename)
}
}

fs.writeFileSync(JSonPath, `window.imageUrl = ${JSON.stringify(arr)}`);
// 将这个列表作为一个新的文件资源,插入到 webpack 构建中:
compilation.assets['imgUrl.js'] = {
source: function () {
return `window.imageUrl = ${JSON.stringify(arr)}`
},
size: function () {
return arr.length;
}
};
callback();
});
}
}
// 导出 Plugin
module.exports = BasicPlugin;
图片预加载

项目中,图片加载与加载中的进度结合,加载完成后,进度显示100%,正式加载页面组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//arr即所有图片资源的路径
//每加载完成一张,更新进度
imageUpload = (arr) => {
let { load, process } = this.state
const len = arr.length
const part = (100 - process) / arr.length
arr.forEach(src => {
this.creatImage(src, () => {
load++
process += part;
if (load === len) {
process = 100
setTimeout(() => {
this.setState({
loaded: true
})
}, 1200)
}
this.setState({
load,
process
})
})
});
}

//加载图片
creatImage = (src, callback) => {
const img = new Image()
img.src = src
img.onload = () => {
callback()
}
img.onerror = () => {
callback()
}
}

也可以用上面提到的Web Worker,进行图片预加载,参考Web Worker在项目中的妙用

参考链接

探寻webpack插件机制
webpack原理-编写Plugin
图片预加载的三个方法

7.一些动画效果
(1)边框动画:逆时针画出边框
大概逻辑就是定义4个高度、宽度为0的元素,相对定位在父元素的四个顶点top0,left:0,bottom:0,right:0,给每条边添加不同时间的动画,绘制元素的高度/宽度
width: 0px; height:0px; width: 100%; height: 1px; 0.4s
width: 0px; height:0px; width: 1px; height: 100%; 0.8s
width: 0px; height:0px; with: 100%; height: 1px; 1.2s
width: 0px; height:0px; with: 1px; height: 100%; 1.6s

页面代码

1
2
3
4
5
6
7
//页面代码
<div className={styles.tab}>
<span className={styles.top}></span>
<span className={styles.left}></span>
<span className={styles.bottom}> </span>
<span className={styles.right}></span>
</div>

css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//主要css
//$定义变量
$color: rgba(22, 233, 252, 0.36);
.tab {
position: absolute;
background: rgba(0, 0, 0, 0.3);
top: 214px;
right: 90px;
width: 510px;
height: 730px;
background-size: 100% 100%;
}

//%占位选择器,如果不调用,则不会有任何多余的css产生
%after {
content: "";
position: absolute;
width: 8px;
height: 8px;
border-radius: 100%;
border-top: 1px solid $color;
opacity: 0;
}

.left {
left: 0;
top: 4px;
//边框拐角,圆角
&:after {
@extend %after;//@extend 继承另一个选择器
transform: rotate(-45deg);
top: -4px;
left: 1px;
}
}

.bottom {
left: 4px;
bottom: 0px;

&:after {
@extend %after;
transform: rotate(-135deg);
top: -9px;
left: -3px;
}
}

.right {
right: 0;
bottom: 4px;

&:after {
@extend %after;
transform: rotate(135deg);
bottom: -4px;
left: -8px;
}
}

.top {
right: 4px;
top: 0;

&:after {
@extend %after;
transform: rotate(45deg);
bottom: -8px;
right: -4px;
}
}
//高度动画
@keyframes lr {
from {
height: 0;
width: 1px;
}

to {
height: calc(100% - 8px);
width: 1px;
}
}
//宽度动画
@keyframes tb {
from {
width: 0;
height: 1px;
}

to {
width: calc(100% - 8px);
height: 1px;
}
}

@keyframes after {
from {
opacity: 0;
}

to {
opacity: 1;
}
}
//@mixin 定义一个代码块
@mixin set-styles($arg) {
//@if 条件语句
@if #{$arg} {
@extend .#{$arg};
}
}

$delay:0;
$iconFont: ((cl:top),
(cl:left),
(cl:bottom),
(cl:right),
);
//循环语句,为四条边添加动画
@for $i from 1 through length($iconFont) {
$item: nth($iconFont, $i);//获取$iconFont,$i下标的值
$cl: map-get($item, cl);//Sass Maps的函数-map-get($map,$key),返回 $key 在 $map 中对应的 value 值;获取cl对应的值
$an: if($i % 2==1, tb, lr);//奇数tb,偶数lr
$duration: 0.4s;//每个动画执行的时间
$delay: $delay + $duration;//每个动画的延迟执行时间

.#{$cl} {s
animation: $an $duration ease $delay 1 normal forwards;
position: absolute;
background: $color;
height: 0;
width: 0;
@include set-styles($cl);// @include调用@mixin定义的一个代码块
}

.#{$cl}:after {
animation: after 0.1s ease $delay 1 normal;
animation-fill-mode: forwards;
}
}

参考链接:Sass中文文档

(2)圆圈动画

大概逻辑就是通过动画改变透明度和旋转角度实现效果。
主要技术点:
1.data-*属性:
HTML5新属性,html全局属性,用于存储页面或应用程序的私有自定义数据;允许在所有 HTML 元素上嵌入自定义 data 属性。
属性名不应该包含任何大写字母,并且在前缀 “data-“ 之后必须有至少一个字符。
属性值可以是任意字符串。
2.css3选择器[attribute=value]:
div[data-anim
=base];选择自定义属性data-anim包含单词 “base” 的所有元素。
3.clip属性:
剪裁绝对定位元素,即position:absolute;的元素。

rect (top, right, bottom, left);clip: rect(0px, 42px, 100px, 0px),即右边裁剪42px,底部裁剪100px。

页面代码

1
2
3
4
5
6
7
8
9
10
11
//页面代码
//meteorology 左边跑动的光
//wrapper left画右半圆
//wrapper1 right画左半圆
<div className={styles.meteorology}></div>
<div className="wrapper">
<div className="circle" data-anim="base left"></div>
</div>
<div className="wrapper1">
<div className="circle" data-anim="base right"></div>
</div>

css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
//css代码
//左半圆光线跑半个圆的动画,就是调整meteorology盒子的旋转角度
.meteorology {
background-image: url('~@/assets/meteorology.png');
width: 82px;
height: 82px;
background-repeat: no-repeat;
background-size: 45px;
position: absolute;
background-position: -3px -4px;
z-index: 1;
transform: rotate(-150deg);
animation: rotate 1s linear 1.8s forwards;
opacity: 0;
}

@keyframes rotate {
from {
transform: rotate(-150deg);
opacity: 1;
}

to {
transform: rotate(0deg);
opacity: 1
}
}

.wrapper {
width: 80px;
/* Set the size of the progress bar */
height: 80px;
position: absolute;
/* Enable clipping */
clip: rect(0px, 100px, 100px, 42px);
/* Hide half of the progress bar */
}

.wrapper1 {
width: 80px;
/* Set the size of the progress bar */
height: 80px;
position: absolute;
clip: rect(0px, 46px, 100px, 0px);
/* clip: rect(0px, 100px, 100px, 42px); */
-webkit-animation-name: close-wrapper;
animation-delay: 1.8s;
/* Enable clipping */
/* Hide half of the progress bar */
}


/* Set the sizes of the elements that make up the progress bar */
.circle {
width: 80px;
height: 80px;
border: 1px solid #16694D;
border-radius: 40px;
position: absolute;
clip: rect(0px, 42px, 100px, 0px);
}

/* Using the data attributes for the animation selectors. */
/* Base settings for all animated elements */
//设置div中自定义属性data-anim的值包含base的元素的样式
div[data-anim~=base] {
-webkit-animation-iteration-count: 1;
/* Only run once */
-webkit-animation-fill-mode: forwards;
/* Hold the last keyframe */
-webkit-animation-timing-function: linear;
/* Linear animation */
}
//设置类circle中自定义属性data-anim的值包含left的元素的样式
.circle[data-anim~=left] {
-webkit-animation-duration: 1s;
/* Full animation time */
-webkit-animation-name: left-spin;
animation-delay: 1.8s;
}
//设置类circle中自定义属性data-anim的值包含right的元素的样式
.circle[data-anim~=right] {
-webkit-animation-duration: 1s;
/* Half animation time */
-webkit-animation-name: left-spin1;
animation-delay: 1.8s;
opacity: 0;
}


/* Rotate the left side of the progress bar from 0 to 360 degrees */
@-webkit-keyframes left-spin {
from {
-webkit-transform: rotate(0deg);
}

to {
-webkit-transform: rotate(180deg);
}
}

@-webkit-keyframes left-spin1 {
from {
opacity: 1;
-webkit-transform: rotate(180deg);
}

to {
opacity: 1;
-webkit-transform: rotate(360deg);
}
}

参考链接:
HTML data-* 属性
CSS3选择器
css clip属性

二、问题总结

1.性能问题
(1)定时器太多

解决方案:每个页面只使用一个定时器,移除组件时清除定时器。

(2)每次切换页面都重新加载渲染地图

解决方案:页面初始加载时,加载地图,切换页面不销毁地图,清除地图上面的覆盖物,只对地图图层做显示/隐藏操作。

2.屏幕适配/浏览器兼容问题
(1)屏幕适配问题

设计稿尺寸是1920*1080,用户客户端屏幕大小不一
解决方案:document.body.style.zoom等比缩放页面大小

(2)浏览器兼容

地图渲染要支持webgl,还有好的动画效果对浏览器有 一定的要求
解决方案:只兼容google浏览器,其它浏览器打开项目弹出“请用google浏览器打开”的提示语。

3.开发效果与UI预期效果不一致

例如全国地图的显示与UI设计稿的效果 是有差别的,有些可能不能完全还原UI的效果
解决方案:与UI沟通协调,最好的方案就是找出比UI更好的效果。

4.层级问题

解决方案:

三、多说一句

因为是挺久之前做的大屏项目,可能高德地图、百度地图,echarts等等都已经越来越完善,推出了很多新功能,或者有更好的库,更好的解决方案,需要进一步去探索研究。