zoukankan      html  css  js  c++  java
  • Salesforce Lightning组件中使用Chart.JS实现数据可视化(二)

    上篇博客提到,如何在Salesforce Lightning组件下借助ChartJS来实现数据可视化,并以Line Chart完成了一个案例,接下来我们进一步优化这个案例

    之前的博客中,ChartJS显示的画面如下,

    在这张表当中,很显然,2014与2020对应的两个数据点并不能将数值显示完全,同时可以看到,2020的最高点被遮住了一部分,对应的数字更是没有显示出来,所以我们修复下这几个问题:

    • 最高点显示数据不完整
    • 最左边更最右边的数据显示不完整

    同时需要优化的地方包括:

    • 显示的数字因为是货币,所以给加上对应的货币分隔符
    • 动态显示画布的大小

    问题1:最高点显示数据不完整

    数值最大的点显示不完整,其本质上是这个数值达到了Y轴的最高点,所以,解决方法就是设置Y的高度始终高于数据集中的最大值,而考虑到数据的量级可能是个位数,又或者百万甚至千万,直接设置Y轴的大小显然并不可取,那么我们可以将Y轴最大值 = 数据集最大值 * 130%,这样动态的保证数据点用于囊括在Y轴里面,从而确保了最高点不会被遮住,那么上代码:

    // 取出数据集中的最大值
    var MULTIPLE = 1.3; // 设置Y轴 在最大值的基础上延长多少
    var maxValue = temp[0]["firstValue"];
            
    for(var a=0; a< temp.length; a++){
      label.push(temp[a]["label"]);
      firstValue.push(temp[a]["firstValue"]);
      secondValue.push(temp[a]["secondValue"]); 
      if(maxValue < temp[a]["firstValue"]) maxValue = temp[a]["firstValue"];
      if(maxValue < temp[a]["secondValue"]) maxValue = temp[a]["secondValue"];
    }    
    // 设置Y轴参数
    yAxes: [{
        gridLines: {display:false},
        display: false, // 隐藏Y轴
        ticks: { // 设置Y轴最大最小坐标点
            min: 0, 
            max: maxValue * MULTIPLE,
        },
    }]

    展示效果:

    问题2:最左边更最右边的数据显示不完整

    X轴左右两边的数据点显示不完整,根据问题一的结果,第一反应毫无疑问是延长X轴线来解决问题,但是在查阅了相关文档后,并没有合适的,可以用来操作的空间,所以常规手段,将最左边的点往右挪,最右边的点往左挪,不过这种情况就需要注意,当数据点仅有一个时该如何显示,话不多说,上代码

    animation:{
        "duration":1,
        "onComplete": function() {
            var chartInstance = this.chart,
            ctx = chartInstance.ctx;
            ctx.fillStyle = 'black';
            ctx.textAlign = 'center';
            ctx.textBaseline = 'bottom';
            this.data.datasets.forEach(function(dataset, i) {
                var meta = chartInstance.controller.getDatasetMeta(i);
                var len = meta.data.length;
                meta.data.forEach(function(bar, index) {
                    var data = '$' + dataset.data[index];
                    var Offset = data.length * 2; // 偏移量
              // 注意,Y轴上 - 5是向上移动 if(len >= 2){ if(index == 0){// 第一个数据点的偏移 ctx.fillText(data, bar._model.x + Offset, bar._model.y - 5); }else if(index == len -1){ // 最后一个点的偏移 ctx.fillText(data, bar._model.x - Offset, bar._model.y) - 5; }else{ ctx.fillText(data, bar._model.x, bar._model.y - 5); } }else{// 当只有一个数据点的时候 ctx.fillText(data, bar._model.x + Offset, bar._model.y - 5); } }); }); } }

    这里面我引入了一个偏移量的概念,原理同问题一,数据量级的问题,个位数和百位千位十万位需要挪动的像素大小是完全不一样的,所以我根据当前数据本身的长度,分别向左右各偏移两倍的长度,这样显示的效果在各种量级下自然没有差别,当然你喜欢也可以自己设定合适的大小

    展示效果:

     做到这里,为了测试数据展示的效果,我尝试只传入一个点的时候发现一个问题,

     可以看到,当只有一条数据时,Line类型的图表会将数据点显示在Y轴上,虽然数据显示没问题,但是在展示的时候,会显得非常奇怪,相对来说,将这个点移动到图表的中间效果更合适,但是在我翻遍文档后,发现这一点并不能通过配置来实现,所以只能另想办法,最后我将只有一个点的时候,传入两个空值,在前端显示时,隐藏空值的点,这样根据三个点的显示效果,这唯一一个数据点就会出现在正中间了,那么怎么做呢?

    首先,ChartJS中,坐标点传入 null值是不会显示的,所以利用这一点,我们在第一个点的位置前后各传递一个null,因为 LineChartVar下的fisrtValue和secondValue都是Integer的数据,所以假设传入值为-1,在Aura组件的JS文件中将-1替换成null即可

    Controller & Helper 核心代码:

    //  此处仅作为展示,假设数据不包括 -1 ,实际项目中应改成其他值,因为LineChartVar的中firstValue都是整数类型,而Integer类型不支持传null进去,
    // 所以我设置值为-1,在JS中将-1替换掉
    List<LineChartVar> myLineChartVarList = new List<LineChartVar>(); myLineChartVarList.add(new LineChartVar('ISNULL', -1, -1)); myLineChartVarList.add(new LineChartVar('2014', 100,120)); myLineChartVarList.add(new LineChartVar('ISNULL', -1, -1)); return JSON.Serialize(myLineChartVarList);
    var MULTIPLE = 1.3; // 设置Y轴 在最大值的基础上延长多少
    // 注意 -1 是假设的的值,所以最大值不能取
    var maxValue = temp[0]["firstValue"] == -1 ? 0 : temp[0]["firstValue"]; 
    
    for(var a=0; a < temp.length; a++){
        //  label为ISNULL说明是我们传入的占位点,所以将Label设置为''
        var tempLabel = temp[a]["label"] == 'ISNULL' ? '' : temp[a]["label"];
        if(tempLabel == '') MULTIPLE = 2;
        label.push(tempLabel);
        
        //  value为-1时,说明是传入的占位符,所以将其设定为null
        var tempValue = temp[a]["firstValue"] == -1 ? null : temp[a]["firstValue"];
        firstValue.push(tempValue);
    
        var tempValue2 = temp[a]["secondValue"] == -1 ? null : temp[a]["secondValue"];
        secondValue.push(tempValue2);
    
        // setting the yAxes ticks 
        var FValue = temp[a]["firstValue"] == -1 ? 0 : temp[a]["firstValue"];
        var SValue = temp[a]["secondValue"] == -1 ? 0 : temp[a]["secondValue"];
        if(FValue > maxValue) maxValue = FValue; // get MaxValue
        if(SValue > maxValue) maxValue = MValue; // get MaxValue
    }

    为了方便查看演示效果,我将X轴的网格线重新打开,可以看到,现在的唯一的数据点就被放在了中间位置,效果比起默认的情况就好很多了

     然后我们继续最开始的优化,想要让数据点显示货币分隔符号,其实只需要在HelpJS中对数据进行简单的处理就好

    meta.data.forEach(function(bar, index) {
        var tempdata = dataset.data[index];
        // 数据点的值有可能为null,所以先加上判断
        if(tempdata != null){
            // 考虑传入的数据不一定全是整数,当传入一个浮点数时,我们以 "." 进行分隔
            var arry = tempdata.toString().split(".");
            // split分隔出来的数组,第一个数字一定是整数部分,直接通过正则表达式对其添加货币分隔符号
            tempdata = arry[0].toString().replace(/(d)(?=(?:d{3})+$)/g, '$1,');
            // 如果是浮点数,那么 split分隔的数组就有两个值,第二个值就是小数部门,直接拼接即可
            if(arry.length > 1) tempdata = tempdata + '.' + arry[1];
        }
        // 这里添加 “$” 是为了避免 null 值时,data.length 这个报错,毕竟空指针问题
        var data = '$' + tempdata;
        
        // var data = '$' + dataset.data[index];
        var Offset = data.length * 2;
        .......
    }

    查看显示效果:

    看上去还不错,分隔符的显示很完美,那么接下来最后一个优化,让画布的大小自适应,这个其实比较简单,上一篇提到,改变画布的大小有两种方式:

    // 方式一:在Helper中直接渲染
    var el = cmp.find('lineChart').getElement();
    var ctx = el.getContext('2d');
    ctx.canvas.width = '700';
    ctx.canvas.height = '300';
    
    // 方式二:在cmp中,直接设定宽高
    <canvas aura:id="lineChart" id="lineChart" width="600" height="300"/>

    要使画布大小自适应,那么显然在Helper中直接渲染是可行的,所以我们获取画布外的div大小,然后将div的宽高赋值给画布,就能保证在画布渲染出来时,是符合div大小的,所以,代码如下:

    // 获取容器div的大小
    var divChart = document.getElementById('chartContainer');
    
    var el = cmp.find('lineChart').getElement();
    var ctx = el.getContext('2d');
    // 将容器div的大小赋值给画布
    ctx.canvas.width = divChart.offsetWidth;
    ctx.canvas.height = divChart.offsetHeight;

    将div的宽度设置为100%,高度设置为50%

    <aura:application >
        <div id="chartContainer" style="100%;height:50%;">
            <c:ChartDemo/>
        </div>
    </aura:application>

    演示效果如下:

    到这里为止,针对ChartJS数据展过程中的优化点做了更细致的整理和解决,希望对看到这里的你有所帮助

    最后,友情提示,ChartJS的版本是在不断迭代的,如果你看到有的代码你没法运行,或者运行的展示结果并不一样,那么我推荐你换一个版本过来进行尝试,比如在ChartJS中对tooltip的样式属性:displayColors,borderColor,borderWidth这些,都有提供自定义选项,但是如果版本不够新,这些属性并不能正常工作,所以在使用ChartJS时,你使用的版本也很重要

    参考内容:

    ChartJS中文文档

    ChartJS演示案例

    ChartJS官方文档

    附上完整的Helper Controller代码

    ({
        createLineGraph : function(cmp, temp) {
            var label = [];
            var firstValue = [];
            var secondValue = [];
            
            var MULTIPLE = 1.3; // 设置Y轴 在最大值的基础上延长多少
            // 注意 -1 是假设的的值,所以最大值不能取
            var maxValue = temp[0]["firstValue"] == -1 ? 0 : temp[0]["firstValue"]; 
    
            for(var a=0; a < temp.length; a++){
                //  label为ISNULL说明是我们传入的占位点,所以将Label设置为''
                var tempLabel = temp[a]["label"] == 'ISNULL' ? '' : temp[a]["label"];
                if(tempLabel == '') MULTIPLE = 2;
                label.push(tempLabel);
                
                //  value为-1时,说明是传入的占位符,所以将其设定为null
                var tempValue = temp[a]["firstValue"] == -1 ? null : temp[a]["firstValue"];
                firstValue.push(tempValue);
            
                var tempValue2 = temp[a]["secondValue"] == -1 ? null : temp[a]["secondValue"];
                secondValue.push(tempValue2);
            
                // setting the yAxes ticks 
                var FValue = temp[a]["firstValue"] == -1 ? 0 : temp[a]["firstValue"];
                var SValue = temp[a]["secondValue"] == -1 ? 0 : temp[a]["secondValue"];
                if(FValue > maxValue) maxValue = FValue; // get MaxValue
                if(SValue > maxValue) maxValue = SValue; // get MaxValue
            }
            
            var divChart = document.getElementById('chartContainer');
            
            var el = cmp.find('lineChart').getElement();
            var ctx = el.getContext('2d');
            ctx.canvas.width = divChart.offsetWidth;
            ctx.canvas.height = divChart.offsetHeight;
            
            var data = {
                labels: label,
                datasets: [{
                    label: 'USD Sent',
                    data: firstValue,
                    backgroundColor: "rgba(153,255,51,0.4)",// 填充的颜色
                    pointRadius:5,
                    pointStyle:'circle',
                    borderColor:'#bbd9b7',// 折线的颜色设置
                    pointBorderColor:'#00ff00', // 顶点圈圈的颜色
                    pointBackgroundColor:'#00ff00' // 顶点的颜色
                }, {
                    label: 'USD Recieved',
                    data: secondValue,
                    backgroundColor: "rgba(255,153,0,0.4)",// 填充的颜色
                    pointRadius:5,
                    pointStyle:'circle',
                    borderColor:'#bbd9b7',// 折线的颜色设置
                    pointBorderColor:'#ff0000', // 顶点圈圈的颜色
                    pointBackgroundColor:'#ff0000' // 顶点的颜色
                }]
            };
            
            var options = {
                elements: {
                    line: {tension: 0} // 设置折线图
                },
                legend: {display: false}, // 设置不显示图例
                responsive: false, // responsive和maintainAspectRatio设置为false才可以调整图标的宽高
                maintainAspectRatio: false,
                scales: {
                    xAxes: [{
                        gridLines: {display:false} // 隐藏网格线
                    }],
                    yAxes: [{
                        gridLines: {display:false},
                        display: false, // 隐藏Y轴
                        ticks: { // 设置Y轴最大最小坐标点
                            min: 0, 
                            max: maxValue * MULTIPLE,
                        },
                    }]
                },
                // 设置显示数据的顶点
                hover:{animationDuration:0},//防止鼠标移动到顶点时的闪烁效果
                animation:{
                    "duration":1,
                    "onComplete": function() {
                        var chartInstance = this.chart,
                            ctx = chartInstance.ctx;
                        ctx.fillStyle = 'black';
                        ctx.textAlign = 'center';
                        ctx.textBaseline = 'bottom';
                        this.data.datasets.forEach(function(dataset, i) {
                            var meta = chartInstance.controller.getDatasetMeta(i);
                            var len = meta.data.length;
                            meta.data.forEach(function(bar, index) {
                                var tempdata = dataset.data[index];
                                if(tempdata != null){
                                    var arry = tempdata.toString().split(".");
                                    tempdata = arry[0].toString().replace(/(d)(?=(?:d{3})+$)/g, '$1,');                                
                                    if(arry.length > 1) tempdata = tempdata + '.' + arry[1];
                                }
                                var data = '$' + tempdata;
                                
                                // var data = '$' + dataset.data[index];
                                var Offset = data.length * 2;
                                var Offset = data.length * 2; // 偏移量
                                if(len >= 2){
                                    if(index == 0){//first point style 
                                        ctx.fillText(data, bar._model.x + Offset, bar._model.y - 5);    
                                    }else if(index == len -1){ // last point style
                                        ctx.fillText(data, bar._model.x - Offset, bar._model.y - 5);    
                                    }else{
                                        ctx.fillText(data, bar._model.x, bar._model.y - 5);    
                                    }
                                }else{// only one point style
                                    ctx.fillText(data, bar._model.x + Offset, bar._model.y - 5);    
                                }
                            });
                        });
                    }
                }
            };
            
            new Chart(ctx, {
                type: 'line',
                data: data,
                options: options
            });
            
        }
    })
  • 相关阅读:
    010editor爆破与注册机
    [FlareOn4]notepad
    [FlareOn6]Snake(NES逆向)
    [FlareOn6]Memecat Battlestation
    [FlareOn6]FlareBear
    回车符和换行符之间的区别
    docker配置搭建elasticsearch集群
    docker部署安装harbor
    ansible的get_url模块
    ansible的lineinfile与blockinfile模块
  • 原文地址:https://www.cnblogs.com/luqinghua/p/12707502.html
Copyright © 2011-2022 走看看