你好,我是沐爸,欢迎点赞、收藏、评论和关注。
昨天我们聊到阿里 DataV 大屏的五种自适应方案,每一种多少都有些瑕疵,如果没有看过昨天的博客,回头可以了解下:链接
可视化大屏如何完美适配屏幕?有一种方案,或者说一种思想,那就是随着屏幕的缩放,页面元素(字体、宽高、间距等)也随之缩放。虽然称为完美方案,但也不能自适应特别极端的情况,如屏幕过宽或过长,我们尽量考虑正常尺寸。
今天的分享很简单,只提供代码和预览效果,需要你亲自去运行,去尝试。
一、效果预览
二、代码实现
使用的最容易上手的 Vue2 框架,安装了两个依赖 `echarts`和 `lodash`npm install echarts lodash
src/App.vue
<template>
<div id="app">
<div class="bg">
<div class="preview-main">
<ChartComponent v-for="item in list" :key="item.id" :item="item" :scaleWidth="scaleWidth"
:scaleHeight="scaleHeight"></ChartComponent>
</div>
</div>
</div>
</template>
<script>
import ChartComponent from './components/ChartComponent.vue'
import { list } from './data'
export default {
components: {
ChartComponent
},
data() {
return {
list,
scaleWidth: window.innerWidth / 1600,
scaleHeight: window.innerHeight / 900
}
},
mounted() {
window.addEventListener('resize', () => {
this.scaleWidth = window.innerWidth / 1600
this.scaleHeight = window.innerHeight / 900
})
}
}
</script>
<style scoped>
.bg {
width: 100%;
height: 100vh !important;
min-width: 200px;
min-height: 300px;
background-color: rgb(242, 242, 242);
padding: 5px;
box-sizing: border-box;
}
.preview-main {
width: 100%;
height: 100%;
position: relative;
}
</style>
<style>
html {
height: 100%;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
overflow: hidden;
}
</style>
src/data.js
export const list = [
{
id: 'c0',
style: {
width: 1590,
height: 100,
left: 0,
top: 0
},
type: 'html',
template: `
<div style="font-size:28px;width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;">
2021年全国GDP数据
</div>
`
},
{
id: 'c1',
style: {
width: 397,
height: 395,
left: 0,
top: 100
},
type: 'chart',
option: {
title: {
text: '今年上半年GDP产业分布(亿元)',
left: 10,
top: 10,
textStyle: {
color: '#303133',
fontWeight: 'normal',
fontSize: 18
}
},
color: ['#EF8B07', '#FAE37C', '#F5C021',],
tooltip: {
trigger: 'item'
},
legend: {
bottom: '10',
left: 'center',
icon: 'rect',
itemWidth: 10,
itemHeight: 10
},
series: [
{
name: 'GDP',
type: 'pie',
radius: ['41%', '55%'],
avoidLabelOverlap: false,
label: {
show: true,
position: 'inside',
formatter: '{c}'
},
data: [
{ value: 28402, name: '第一产业' },
{ value: 207154, name: '第二产业' },
{ value: 296611, name: '第三产业' }
]
}
]
},
},
{
id: 'c2',
style: {
width: 397,
height: 395,
left: 0,
top: 495
},
type: 'chart',
option: {
color: ['#F8B65E'],
title: {
text: 'GDP前十强城市',
left: 10,
top: 10,
textStyle: {
color: '#303133',
fontWeight: 'normal',
fontSize: 18
}
},
grid: {
left: '15',
right: '15',
containLabel: true
},
legend: {
bottom: '10',
left: 'center',
icon: 'rect',
itemWidth: 10,
itemHeight: 10
},
xAxis: {
type: 'value',
splitLine: {
show: false
},
axisLabel: {
show: false
}
},
yAxis: {
type: 'category',
axisLine: {
show: false
},
axisTick: {
show: false
},
data: ['南京市', '武汉市', '杭州市', '成都市', '苏州市', '重庆市', '广州市', '深圳市', '北京市', '上海市'].reverse()
},
series: [
{
name: 'GDP(亿元)',
type: 'bar',
label: {
show: true,
position: 'right',
formatter: '{c}'
},
data: [7622.77, 8251.5, 8656.03, 9602.72, 10684.66, 12903, 13101.89, 14324.47, 19228, 20102].reverse()
}
]
}
},
{
id: 'c3',
style: {
width: 663,
height: 698,
left: 397,
top: 190
},
type: 'map',
option: {
title: {
text: '今年上半年GDP',
left: 10,
top: 10,
textStyle: {
color: '#303133',
fontWeight: 'normal',
fontSize: 18
}
},
visualMap: {
min: 0,
max: 1000,
left: 'right',
top: 'bottom',
text: ['Hign', 'Low'],
calculable: true,
color: ['#B77702', '#FCF4E5']
},
geo: {
map: "china",
zoom: 1,
roam: true,
top: "20%",
label: {
show: true
},
},
series: [
{
type: "map",
geoIndex: 0,
data: [
{ name: '上海市', value: 20102 },
{ name: '云南省', value: 12680.22 },
{ name: '内蒙古自治区', value: 8312.84 },
{ name: '北京市', value: 19228 },
{ name: '吉林省', value: 6075.01 },
{ name: '四川省', value: 25232.36 },
{ name: '天津市', value: 7309 },
{ name: '安徽省', value: 20565.77 },
{ name: '山东省', value: 38610.25 },
{ name: '山西省', value: 9606.81 },
{ name: '广东省', value: 57226.27 },
{ name: '广西壮族自治区', value: 5525.09 },
{ name: '新疆维吾尔自治区', value: 7329 },
{ name: '江苏省', value: 54890.37 },
{ name: '江西省', value: 13977.22 },
{ name: '河北省', value: 18754.04 },
{ name: '河南省', value: 22719.34 },
{ name: '浙江省', value: 34514.84 },
{ name: '海南省', value: 2884.31 },
{ name: '湖北省', value: 22732.1 },
{ name: '湖南省', value: 21671.12 },
{ name: '甘肃省', value: 4748.17 },
{ name: '福建省', value: 22899.31 },
{ name: '西藏自治区', value: 926.05 },
{ name: '贵州省', value: 9075.48 },
{ name: '辽宁省', value: 12641.2 },
{ name: '重庆市', value: 12903 },
{ name: '陕西省', value: 13454.73 },
{ name: '青海省', value: 1557 },
{ name: '黑龙江省', value: 5990 }
]
}
]
}
},
{
id: 'c4',
style: {
width: 530,
height: 395,
left: 1060,
top: 100
},
type: 'chart',
option: {
color: ['#F6EA51'],
title: {
text: '全国百强县分布',
left: 'center',
top: 10,
textStyle: {
color: '#303133',
fontWeight: 'normal',
fontSize: 18
}
},
grid: {
left: '15',
right: '15',
containLabel: true
},
legend: {
bottom: '10',
left: 'center',
icon: 'rect',
itemWidth: 10,
itemHeight: 10,
data: ['GDP(亿元)']
},
xAxis: {
data: ['内蒙古自治区', '安徽省', '山东省', '广东省', '新疆维吾尔自治区', '江苏省', '江西省', '河北省', '河南省', '浙江省', '湖北省', '湖南省', '福建省', '贵州省', '辽宁省', '陕西省']
},
yAxis: {
name: '百强县个数'
},
tooltip: {
type: 'category',
formatter: function (value) {
// GDP数据
let gdpArr = [8312.84, 20565.77, 38610.25, 57226.27, 7329, 54890.37, 13977.22, 18754.04, 22719.34, 34514.84, 22732.1, 21671.12, 22899.31, 9075.48, 12641.2, 13454.73];
return `${value.name},${value.value},${gdpArr[value.dataIndex]}`
}
},
series: [
{
type: 'scatter',
name: 'GDP(亿元)',
data: [2, 2, 13, 4, 1, 26, 1, 2, 7, 18, 7, 5, 9, 1, 1, 1], // 百强县数量
symbolSize: function (value, item) {
// GDP数据
let gdpArr = [8312.84, 20565.77, 38610.25, 57226.27, 7329, 54890.37, 13977.22, 18754.04, 22719.34, 34514.84, 22732.1, 21671.12, 22899.31, 9075.48, 12641.2, 13454.73];
return gdpArr[item.dataIndex] / 1000
}
}
]
}
},
{
id: 'c5',
style: {
width: 530,
height: 395,
left: 1060,
top: 495
},
type: 'chart',
option: {
color: ['#EF8B07'],
title: {
text: '历年GDP数据',
left: 10,
top: 10,
textStyle: {
color: '#303133',
fontWeight: 'normal',
fontSize: 18
}
},
grid: {
left: '15',
right: '15',
containLabel: true
},
legend: {
bottom: '10',
left: 'center',
icon: 'rect',
itemWidth: 10,
itemHeight: 10
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['2011', '2012', '2013', '2014', '2015', '2016', '2017', '2018', '2019', '2020']
},
yAxis: {
type: 'value',
splitLine: {
show: false
}
},
tooltip: {
type: 'value'
},
series: [
{
name: 'GDP(亿元)',
data: [48.94, 55.3, 62.04, 67.94, 71.7, 72.8, 79.8, 90.04, 92.57, 95.42],
type: 'line',
label: {
show: true,
position: 'top',
formatter: '{c}'
}
}
]
}
},
{
id: 'c6',
style: {
width: 220,
height: 90,
left: 397,
top: 100
},
type: 'html',
template: `
<div style="width: 100%;height: 100%;display: flex;flex-direction: column;justify-content: space-around;">
<div style="font-size:22px;text-align:center;color:#FF0000;">532,167</div>
<div style="font-size:16px;text-align:center;color:#807F7F;">今年上半年GDP(亿元)</div>
</div>
`
},
{
id: 'c7',
style: {
width: 220,
height: 90,
left: 618,
top: 100
},
type: 'html',
template: `
<div style="width: 100%;height: 100%;display: flex;flex-direction: column;justify-content: space-around;">
<div style="font-size:22px;text-align:center;color:#FF9900;">463,324.76</div>
<div style="font-size:16px;text-align:center;color:#807F7F;">去年上半年GDP(亿元)</div>
</div>
`
},
{
id: 'c8',
style: {
width: 220,
height: 90,
left: 839,
top: 100
},
type: 'html',
template: `
<div style="width: 100%;height: 100%;display: flex;flex-direction: column;justify-content: space-around;">
<div style="font-size:22px;text-align:center;color:#00CC00;">12.7</div>
<div style="font-size:16px;text-align:center;color:#807F7F;">同比增长率(%)</div>
</div>
`
}
]
components/ChartComponent.vue
<template>
<div class="box" :style="styleObj">
<div class="chart" :id="item.id"></div>
</div>
</template>
<script>
import * as echarts from 'echarts'
const geoJSON = require('./geojson.json')
import _ from 'lodash'
export default {
props: {
item: Object,
scaleWidth: Number,
scaleHeight: Number
},
data() {
return {
itemData: this.item,
myChart: null
}
},
computed: {
styleObj() {
return {
width: this.itemData.style.width * this.scaleWidth + 'px',
height: this.itemData.style.height * this.scaleHeight + 'px',
top: this.itemData.style.top * this.scaleHeight + 'px',
left: this.itemData.style.left * this.scaleWidth + 'px'
}
}
},
watch: {
scaleWidth() {
this.renderAgain()
},
scaleHeight() {
this.renderAgain()
},
},
mounted() {
this.renderChart()
},
methods: {
renderAgain() {
if (this.myChart) {
this.myChart.resize()
this.myChart.dispose()
}
this.renderChart()
},
changeChildNodeStyle(childNodes) {
for (let i = 0; i < childNodes.length; i++) {
let child = childNodes[i]
if (child.childNodes.length > 0) {
this.changeChildNodeStyle(child.childNodes)
}
if (child.nodeType === 1) {
if (child.oldFontSize !== undefined) {
child.style.fontSize = child.oldFontSize * this.scaleWidth + 'px'
} else {
if (!child.style.fontSize) {
return
}
let fontSize = child.style.fontSize.replace('px', '')
fontSize = Number(fontSize)
child.oldFontSize = fontSize
fontSize *= this.scaleWidth
child.style.fontSize = fontSize + 'px'
}
}
}
},
renderChart() {
let scale = this.scaleWidth // Math.min(this.scaleWidth, this.scaleHeight)
if (this.itemData.type === 'html') {
let el = document.getElementById(this.itemData.id)
let childNodes = el.childNodes
if (childNodes.length === 0) {
el.innerHTML = this.itemData.template
}
childNodes = el.childNodes
this.changeChildNodeStyle(childNodes)
} else if (this.itemData.type === 'chart') {
this.myChart = echarts.init(document.getElementById(this.itemData.id))
let option = _.cloneDeep(this.itemData.option)
option.title.textStyle.fontSize *= scale
this.myChart.setOption(option)
} else if (this.itemData.type === 'map') {
// 中国地图数据来源:https://geo.datav.aliyun.com/areas_v3/bound/geojson?code=100000_full
echarts.registerMap('china', { geoJSON })
this.myChart = echarts.init(document.getElementById(this.itemData.id))
let arr = this.itemData.option.series[0].data
let values = arr.map(item => item.value)
let min = Math.min(...values)
let max = Math.max(...values)
let option = _.cloneDeep(this.itemData.option)
option.title.textStyle.fontSize *= scale
option.visualMap.min = min
option.visualMap.max = max
this.myChart.setOption(option)
}
}
}
}
</script>
<style scoped>
.box {
position: absolute;
padding: 5px;
box-sizing: border-box;
}
.chart {
width: 100%;
height: 100%;
background-color: rgb(255, 255, 255);
border-radius: 4px;
}
</style>
components/geojson.json
中国地图经纬度相关数据,数据量太大,请自行下载:https://geo.datav.aliyun.com/areas_v3/bound/geojson?code=100000_full
说明
代码纯手写,时间紧,功能封装和容错等并没有很好地处理,有不足之处还请不要见怪,主要是理解自适应的理念。
三、DataEase
本文案例来自DataEase,今天介绍的理念也是受它启发,在此表示感谢!DataEase提供了开源的大屏可视化解决方案,包括前后台,开源项目地址:https://github.com/dataease/dataease,感兴趣的同学可以去看下源码。
本文案例的相关链接在这里:https://dataease.fit2cloud.com/#/preview/e29030f5-212f-462f-b43a-3ccdd983446b,看看一样吗?
四、总结
今天的分享的主题是完美适配的解决方案,其实有些名不符实,最多是一种理念或思想,但可操作性很强。
关于大屏可视化的六种自适应方式,已经分享完了,你学会了吗?欢迎评论区告诉我
好了,分享结束,谢谢点赞,下期再见。