Categories JavaScript

Tạo bảng biểu cột, đường phức tạp hơn – học thư viện JS ECharts (phần 2)

Trong phần một chúng ta đã làm quen với các cú pháp, câu lệnh cơ bản của ECharts, trong bài 2 này tôi sẽ thử tìm hiểu các bảng biểu dạng cột đã được đề cập trong bài đầu nhưng có nhiều tính năng hơn.

A. Biểu đồ cột giúp nổi bật thông tin khi di chuột qua (axis align with tick)

biểu đồ bổ sung thêm thông tin

Đoạn mã mẫu:

option = {
    tooltip: {
        trigger: 'axis', 
        axisPointer: { 
            type: 'shadow' 
        }
    },
    grid: {
        left: '3%',
        right: '4%',
        bottom: '3%',
        containLabel: true
    },
    xAxis: [
        {
            type: 'category',
            data: ['Thứ 2', 'Thứ 3', 'Thứ 4', 'Thứ 5', 'Thứ 6', 'Thứ 7', 'Chủ nhật'],
            axisTick: {
                alignWithLabel: true
            }
        }
    ],
    yAxis: [
        {
            type: 'value'
        }
    ],
    series: [
        {
            name: 'Số lượng phỏng vấn',
            type: 'bar',
            barWidth: '60%',
            data: [10, 52, 200, 334, 390, 330, 220]
        }
    ]
};

Phần đầu tiên:

tooltip: {
        trigger: 'axis', // kích hoạt trục tọa độ
        axisPointer: { 
            type: 'shadow' // mặc định là line, giá trị khác là shadow
        }
}

Phần thứ hai:

    grid: {          // dùng để căn lề bảng biểu
        left: '3%',
        right: '4%',
        bottom: '3%',
        containLabel: true
    }

Phần thứ ba:

xAxis: [        // xác định trục xAxis là trục cần kích hoạt
        {
            type: 'category',
            data: ['Thứ 2', 'Thứ 3', 'Thứ 4', 'Thứ 5', 'Thứ 6', 'Thứ 7', 'Chủ nhật'],
            axisTick: {
                alignWithLabel: true // sẽ hiện lên cùng tool tip
            }
        }
    ],

Phần cuối có thông tin đáng lưu ý là barWidth: '50%', giúp xác định độ rộng cột tương đối. Bạn cứ điều chỉnh con số này để chọn được chiều rộng cột phù hợp. Và cái đầu là name là tên của dãy dữ liệu.

Khi người dùng hover chuột qua các cột sẽ có ba thông tin hiện lên là category + name + thông tin số trong phần data.

B. Thêm tên bảng, điều chỉnh khoảng cách, ẩn hiện cột dữ liệu, độ rộng của bảng

biểu đồ cột đôi

Code mẫu:

option = {
    title: {
        text: 'Dân số thế giới',
        subtext: 'những nước đông dân nhất'
    },
    tooltip: {
        trigger: 'axis',
        axisPointer: {
            type: 'shadow'
        }
    },
    legend: {
        data: ['năm 2011', 'năm 2012']
    },
    grid: {
        left: '2%',
        right: '4%',
        bottom: '3%',
        containLabel: true
    },
    xAxis: {
        type: 'value',
        boundaryGap: [0, 0.01]
    },
    yAxis: {
        type: 'category',
        data: ['Braxil', 'Indosia', 'Hoa Kỳ', 'Ấn Độ', 'Trung Quốc', 'Dân số toàn thế thới']
    },
    series: [
        {
            name: 'năm 2011',
            type: 'bar',
            data: [18203, 23489, 29034, 104970, 131744, 630230]
        },
        {
            name: 'năm 2012',
            type: 'bar',
            data: [19325, 23438, 31000, 121594, 134141, 681807]
        }
    ]
};

Chúng ta sẽ đi vào từng phần một, vì đoạn mã của cái này khá phức tạp.

title: {
        text: 'Dân số thế giới',
        subtext: 'những nước đông dân nhất',
               textStyle: {
               fontSize: 20,
               fontFamily: "Arial"
               }
 }

Đầu tiên là tiêu đề cho bảng, bao gồm tiêu đề chính ở phần text, và tiêu đề phụ ở phần subtext. Nếu bị lỗi font tiếng Việt, bạn cần bổ sung thêm thông tin về font-family như ở trên, ngoài ra điều chỉnh cả kích cỡ font nếu muốn, cái này tôi có nói lần đầu tiên ở bài giới thiệu ECharts.

Tool tip là cái chúng ta vừa nói ở phần A để hiển thị thông tin khi hover chuột qua.

Còn legend dùng để ẩn và hiện dữ liệu, với 2 nhãn dán nằm trong phần data. Và nút của nó sẽ hiện ở trên cùng.

Grid để căn lề.

Cái boundaryGap trong xAxis rất đáng chú ý, điều chỉnh cái này sẽ thay đổi giá trị lớn nhất và nhỏ nhất trên trục hoành qua đó ảnh hưởng đến độ rộng của cột dữ liệu. Trong đa số trường hợp, để như mã mẫu là hợp lý.

Ở phần series đáng chú ý là chúng ta đưa cả 2 cột dữ liệu vào bảng với các data name lần lượt là năm 2011 và năm 2012. Chú ý là cái name ở đây cần phải giống với cái ở legend thì việc điều chỉnh mới được (ẩn / hiện dữ liệu).

C. Chú thích cho bảng có nhiều cột dữ liệu chênh lệch

đánh dấu dữ liệu

Với bảng có độ chênh dữ liệu lớn việc chú thích thêm cho bảng rất quan trọng, vì những bảng có giá trị nhỏ khó biết được giá trị là bao nhiêu hoặc/và trong trường hợp chúng ta muốn chỉ rõ giá trị của một cột dữ liệu nào đấy.

Mã mẫu:

option = {
    title: {
        text: 'Bảng dữ liệu mua theo tháng',
        subtext: "hoàn toàn hư cấu",
               textStyle: {
               fontSize: 20,
               fontFamily: "Arial"
               }        
    },
    tooltip: {
        trigger: 'axis'
    },
    legend: {
        data: ['bốc hơi', 'lượng mưa']
    },
    toolbox: {
        show: true,
        feature: {
            dataView: {show: true, readOnly: false},
            magicType: {show: true, type: ['line', 'bar']},
            restore: {show: true},
            saveAsImage: {show: true}
        }
    },
    calculable: true,
    xAxis: [
        {
            type: 'category',
            data: ['tháng 1', 'tháng 2', 'tháng 3', 'tháng 4', 'tháng 5', 'tháng 6', 'tháng 7', 'tháng 8', 'tháng 9', 'tháng 10', 'tháng 11', 'tháng 12']
        }
    ],
    yAxis: [
        {
            type: 'value'
        }
    ],
    series: [
        {
            name: 'bốc hơi',
            type: 'bar',
            data: [2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3],
            markPoint: {
                data: [
                    {type: 'max', name: 'giá trị lớn nhất'},
                    {type: 'min', name: 'giá trị nhỏ nhất'}
                ]
            },
            markLine: {
                data: [
                    {type: 'average', name: 'giá trị trung bình'}
                ]
            }
        },
        {
            name: 'lượng mưa',
            type: 'bar',
            data: [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3],
            markPoint: {
                data: [
                    {name: 'lớn nhất trong năm', value: 182.2, xAxis: 7, yAxis: 183},
                    {name: 'thấp nhất trong năm', value: 2.3, xAxis: 11, yAxis: 3}
                ]
            },
            markLine: {
                data: [
                    {type: 'average', name: 'giá trị trung bình'}
                ]
            }
        }
    ]
};

Chúng ta sẽ chú ý đến phần markPoint. Đây là đoạn mã dùng để đánh dấu dữ liệu nổi bật hình quả bóng bay.

Đối với dữ liệu có nhãn "bốc hơi" chúng ta thấy nó sử dụng cú pháp tìm giá trị lớn nhất và nhỏ nhất qua type maxmin.

markPoint: {
      data: [
              {type: 'max', name: 'giá trị lớn nhất'},
              {type: 'min', name: 'giá trị nhỏ nhất'}
      ]
}

Đối với dữ liệu có nhãn "lượng mưa" chúng ta thấy nó sử dụng có pháp lựa chọn một cột cụ thể, ở đây không dùng type min max mà nó chỉ định giá trị nào và cột nào muốn hiển thị:

markPoint: {
     data: [
          {name: 'lớn nhất trong năm', value: 182.2, xAxis: 7, yAxis: 183},
          {name: 'thấp nhất trong năm', value: 2.3, xAxis: 11, yAxis: 3}

182.2 là giá trị hiển thị.

xAxis: 7 nghĩa là cột thứ 7; cột đếm từ 0 bạn nhé (giống kiểu mảng/array nếu bạn nào học về PHP rồi).

yAxis: 183 giá trị này nên để lớn hơn giá trị max một chút để chỉ định vị trí của nó không quá cao so với cột.

Còn markLine tức là đánh dấu bằng đường ngang thì cả 2 nhãn dùng chung cú pháp:

markLine: {
          data: [
                {type: 'average', name: 'giá trị trung bình'}
                ]
 }

type được dùng là average nghĩa là trung bình, việc tính toán giá trị được thực hiện tự động.

D. Biểu đồ đường xếp chồng

biểu đồ đường xếp chồng

Biểu đồ đường xếp chồng có thể rất cần thiết khi mà chúng ta muốn so sánh mức biến động của 2 dữ liệu khác nhau, chẳng hạn doanh số của 2 mặt hàng A và B qua từng tháng.

Mã mẫu:

option = {
    title: {
        text: 'Biểu đồ đường xếp chồng',
               textStyle: {
               fontSize: 20,
               fontFamily: "Arial"
               }    
    },
    tooltip: { // hiển thị thông tin khi hover chuột
        trigger: 'axis'
    },
    legend: { // để tạo nhãn bật tắt dữ liệu
        data: ['AAA','BBB']
    },
    grid: { // căn lề cho biểu đồ
        left: '3%',
        right: '4%',
        bottom: '3%',
        containLabel: true
    },
    toolbox: {
        feature: {
            saveAsImage: {}
        }
    },
    xAxis: {
        type: 'category',
        boundaryGap: false,
        data: ['tháng 2', 'tháng 3', 'tháng 4', 'tháng 5', 'tháng 6', 'tháng 7', 'tháng 8']
    },
    yAxis: {
        type: 'value'
    },
    series: [
        {
            name: 'AAA',
            type: 'line',
            data: [150, 200, 225, 154, 190, 330, 410]
        },
        {
            name: 'BBB',
            type: 'line',
            data: [320, 232, 201, 134, 100, 105, 97]
        }
    ]
};

Ở phần mã cho xAxis bạn chú ý đến cái boundaryGap: false, bạn dùng hay không dùng nó sẽ ảnh hưởng đến mốc bắt đầu, nếu dùng nó sẽ đánh dấu tháng theo điểm, còn không nó sẽ đánh dấu theo khoảng. Cách nào cũng được, miễn là bạn chủ động.

Lưu ý thứ hai là ở phần series, bạn muốn ở chỗ hover hiện lên thông tin đường nào trước thì ở chỗ series cũng phải xếp nó ở trước. Như mã trên thì AAA sẽ hiển thị trên BBB khi người dùng hover qua các điểm dữ liệu. Điều này đáng lưu ý trong trường hợp các điểm dữ liệu của đường AAA thường cao hơn đường BBB.

E. Thêm các bong bóng chú thích giá trị cho biểu đồ đường

chú thích giá trị cho biểu đồ đường

Giúp bạn giải thích dữ liệu rõ ràng hơn.

Mã mẫu:

option = {
    title: {
        text: 'Những thay đổi về nhiệt độ trong tuần tới',
        subtext: 'hoàn toàn hư cấu',
               textStyle: {
               fontSize: 20,
               fontFamily: "Arial"
               }          
    },
    tooltip: {
        trigger: 'axis'
    },
    legend: {
        data: ['Nhiệt độ tối đa', 'nhiệt độ tối thiểu']
    },
    toolbox: {
        show: true,
        feature: {
            dataZoom: {
                yAxisIndex: 'none'
            },
            dataView: {readOnly: false},
            magicType: {type: ['line', 'bar']},
            restore: {},
            saveAsImage: {}
        }
    },
    xAxis: {
        type: 'category',
        boundaryGap: false,
        data: ['thứ hai', 'thứ ba', 'thứ tư', 'thứ năm', 'thứ sáu', 'thứ bảy', 'chủ nhật']
    },
    yAxis: {
        type: 'value',
        axisLabel: {
            formatter: '{value} °C'
        }
    },
    series: [
        {
            name: 'nhiệt độ tối đa',
            type: 'line',
            data: [10, 11, 13, 11, 12, 12, 9],
            markPoint: {
                data: [
                    {type: 'max', name: 'nhiệt độ tối đa'},
                    {type: 'min', name: 'nhiệt độ tối thiểu'}
                ]
            },
            markLine: {
                data: [
                    {type: 'average', name: 'giá trị trung bình'}
                ]
            }
        },
        {
            name: 'nhiệt độ tối thiểu',
            type: 'line',
            data: [1, -2, 2, 5, 3, 2, 0],
            markPoint: {
                data: [
                    {name: 'thấp nhất hàng tuần', value: -2, xAxis: 1, yAxis: -1.5}
                ]
            },
            markLine: {
                data: [
                    {type: 'average', name: 'giá trị trung bình'},
                    [{
                        symbol: 'none',
                        x: '70%',
                        yAxis: 'max'
                    }, {
                        symbol: 'circle',
                        label: {
                            position: 'start',
                            formatter: 'tối đa'
                        },
                        type: 'max',
                        name: 'giá trị cao'
                    }]
                ]
            }
        }
    ]
};

Ở đây có một số thông tin mới mà chúng ta cần lưu ý.

Ở phần trục tung y chúng ta thấy kiểu dữ liệu được gán định dạng là nhiệt độ.

axisLabel: {
     formatter: '{value} °C'
}

Ở phần series chúng ta thấy lại cấu trúc name dành để dán nhãn dữ liệu, cũng như markPoint, markLine để đánh dấu dữ liệu dạng bong bóng và đường. Còn min, max, average là nhỏ nhất, lớn nhất và trung bình. Tất cả được tạo tự động.

Phần cuối của chỗ markLine dành cho nhãn dữ liệu nhiệt độ tối thiểu, chúng ta thấy nhiều câu lệnh mới. Phần này tôi cũng chưa hiểu hết nên sẽ không giải thích gì thêm.

G. Thêm dữ liệu khác vào cột

cột có 2 chiều dữ liệu

Thông thường dữ liệu cột là một chiều, chẳng hạn bạn đồ thị hóa số lượng bán ra của từng sản phẩm trong công ty.

Tuy nhiên đôi khi bạn cần bổ sung thêm thông tin, ví dụ, người dùng đánh giá thế nào về sản phẩm. Và thông tin này và thông tin doanh số là độc lập, chúng không thể suy ra từ nhau. Một sản phẩm được người dùng đánh giá có chất lượng tốt chưa chắc đã bán được nhiều bằng sản phẩm có chất lượng khá…

Giờ chúng ta sẽ đi vào mã mẫu, lời giải thích kèm ngay trong mã:

option = {
    dataset: {
        source: [
            ['score', 'amount', 'product'], // thông tin dữ liệu đầu vào
            [89.3, 58212, 'Matcha Latte'], // bao gồm hai cột số
            [57.1, 78254, 'Milk Tea'], // score được thể hiện dưới dạng màu sắc
            [74.4, 41032, 'Cheese Cocoa'], // amounnt được chuyển thành chiều dài cột
            [50.1, 12755, 'Cheese Brownie'],
            [89.7, 20145, 'Matcha Cocoa'],
            [68.1, 79146, 'Tea'],
            [19.6, 91852, 'Orange Juice'],
            [10.6, 101852, 'Lemon Juice'],
            [32.7, 20112, 'Walnut Brownie']
        ]
    },
    grid: {containLabel: true},
    xAxis: {name: 'amount'}, // trục hoành x được gán tên amount
    yAxis: {type: 'category'}, // trục tung y được gán kiểu / type là nhóm
    visualMap: {
        orient: 'horizontal', // cột màu sắc nằm ngang
        left: 'center', // đặt ở vị trí chính giữa
        min: 10, // giá trị nhỏ nhất là 10, chọn giá trị này nhỏ hơn một chút so với giá trị thấp nhất trong score
        max: 100, // giá trị lớn nhất là 100, khoảng giá trị chọn cần phù hợp, đây là giá trị hợp lý cho score (giá trị cao nhất là gần 90)
        text: ['Điểm cao', 'Điểm thấp'], // văn bản ở 2 bên cột màu giải thích màu nào điểm cao hơn
        // Kết nối cột score với dải màu sắc
        dimension: 0, // giá trị này chỉ đến cột score, nếu bạn để là 1, nó sẽ chỉ đến cột amount
        inRange: {
            color: ['#65B581', '#FFCE34', '#FD665F'] // dải màu sắc mà chúng ta chọn
        }
    },
    series: [
        {
            type: 'bar', // kiểu thanh
            encode: {
                 // gán dữ liệu ở cột amount cho trục hoành x
                x: 'amount',
                
                // gán thông tin ở cột product cho trục tung
                y: 'product'
            }
        }
    ]
};

OK. Như vậy là tạm ổn với cột rồi, có thể bài sau chúng ta sẽ tìm hiểu biểu đồ tròn, dạng ít dùng hơn cột nhưng cũng khá phổ biến.

Back to Top