大屏可视化:完美自适应的解决方案

你好,我是沐爸,欢迎点赞、收藏、评论和关注。

昨天我们聊到阿里 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,看看一样吗?

四、总结

今天的分享的主题是完美适配的解决方案,其实有些名不符实,最多是一种理念或思想,但可操作性很强。

关于大屏可视化的六种自适应方式,已经分享完了,你学会了吗?欢迎评论区告诉我

好了,分享结束,谢谢点赞,下期再见。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沐爸muba

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值