Kỹ Thuật Tạo Hiệu Ứng Cột 3D Giả Lập Trong ECharts Từ Tọa Độ 2D

Phương pháp thứ nhất: Sử dụng đối tượng pictorialBar tùy chỉnh

Để mô phỏng cột 3D mà không cần thư viện WebGL phức tạp, chúng ta có thể tận dụng loại biểu đồ pictorialBar. Phương pháp này cho phép bạn định nghĩa một hình ảnh hoặc SVG làm mẫu (symbol) để hiển thị thay cho hình chữ nhật mặc định.

const container = document.getElementById('chartContainer');
// Đường dẫn tới tài nguyên hình ảnh khối 3D
const iconSource = 'image://data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAYCAYAAADkXjYcAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vd3d3LmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NEIxMjQ0RkYxNTU1MTFFMzk2QTY5MDgzNDcxMEE1NDAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NEIxMjQ1MDAxNTU1MTFFMzk2QTY5MDgzNDcxMEE1NDAiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo0QjEyMzQ5MDFGNTE1MTFFMzk2QTY5MDgzNDcxMEE0OCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo0QjEyMzQ5MTFGNTUxMUUzOTZBNjkwODM0NzExQTQ4Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+J7qKfQAABD1JREFUeNrsmE1oU1UUgH/vJU1STJu0SaOSGk1tW1uxVkQQRBBEEcWFB4oiCMKC4Eq4E7lw40JwcCF04UIQRBARQRBFUcSCVmsf1dqaNGmatGmapn3vzJ05c+fuJJPMnfl/kJl7z73vu9/dOXfnzgD8X1yH9+Ed+Cd8AN/Ce/A+/AtfwtvwHtwA78M98B78C2/APfAO/A/fwvvwHnwAb8A98B68Be/B+3APvA93wfvwPrwP78O78B68D2/B+/AuvAdvw/vwBrwPf4e/w1vwNrwPb8Lb8Da8DW/Dm/AWvAlvxRtwB7wBb8Kb8AacAKfBKXAS3IAzcBqcgFNwEhzACXAcHIWTcAwOwmE4AIThEBzCA3AYDsBhMAC7YT/sA/2wB/bBHtgNe2AH7IE9sBt2w27YA7thN+yCXbATdsEO2Ak7YAds/+///9sF22EnbIPtsA22wVbYCltgC2yGzbAZNsNm2AybYTNshv+hA/4LHoQPwofgffAmvAu3wG1wC94CN8ENeANehdvhNrgJbsLtcAvcBDfADXgd3oA34HV4DV6HV+FVeAVehpfhZXgJXoIX4UV4EZ6H5+AZOA/PwDNwFp6Ck/AknIQj8BgchsNgGAyDITAEhsAQEAJDoA+EQRAEAqEfAIHgD/pAAPj9fh/oh57/B/z/9/o9oNcPeoKgB/RAXujzgb4Q6PWDPtAbBL1+0OsHQa8fBIKAfwgEQRAEQRAEQRAEQRAEQRAEQRAEQRAEQRAEQRAEQfD/4j/9J3Y4jWw0AAAAAElFTkSuQmCC';

const timeLabels = ['Thứ Hai', 'Thứ Ba', 'Thứ Tư', 'Thứ Năm', 'Thứ Sáu', 'Thứ Bảy', 'Chủ Nhật'];
const metricValues = [
    { value: 13, symbol: iconSource },
    { value: 53, symbol: iconSource },
    { value: 23, symbol: iconSource },
    { value: 43, symbol: iconSource },
    { value: 66, symbol: iconSource },
    { value: 78, symbol: iconSource },
    { value: 68, symbol: iconSource }
];

const chartInstance = echarts.init(container);

const configOption = {
    grid: {
        left: '16%', bottom: '26%', right: '10%', top: '16%'
    },
    tooltip: {
        show: true,
        confine: true,
        trigger: 'axis',
        position: function(point, params) {
            // Điều chỉnh vị trí tooltip theo chỉ mục dữ liệu
            return params[0].dataIndex <= 7 
                ? [point[0] + 20, '30%'] 
                : {top: '30%', left: point[0] - size.contentSize[0] - 25};
        },
        padding: 6,
        backgroundColor: 'transparent',
        axisPointer: {
            type: 'shadow',
            shadowStyle: { color: 'rgba(0,222,255,0.3)' }
        }
    },
    xAxis: {
        data: timeLabels,
        axisLine: { lineStyle: { color: '#397cbc' } },
        axisLabel: {
            fontSize: 16,
            color: '#30eee9',
            interval: 0,
            rotate: -45
        }
    },
    yAxis: {
        type: 'value',
        minInterval: 1,
        axisLabel: { color: '#2ad1d2' },
        splitLine: { lineStyle: { color: '#125770' } },
        axisLine: { show: false },
        axisTick: { show: false }
    },
    series: [{
        type: 'pictorialBar',
        symbol: iconSource,
        barGap: 0,
        barWidth: 20,
        symbolSize: ['100%', '100%'],
        data: metricValues
    }]
};

chartInstance.setOption(configOption);

Phương pháp thứ hai: Ghép nối nhiều lớp dữ liệu

Một cách tiếp cận khác là sử dụng ba chuỗi (series) chồng lên nhau để tạo cảm giác chiều sâu: một cho mặt trước, một cho bên cạnh (tối hơn) và một cho bề mặt trên cùng.

// Dữ liệu mẫu
const values = [23, 30, 33, 5, 60, 7, 88];
const categories = ['Thứ Hai', 'Thứ Ba', 'Thứ Tư', 'Thứ Năm', 'Thứ Sáu', 'Thứ Bảy', 'Chủ Nhật'];
const domElement = document.getElementById("timeRange");
const myInstance = echarts.init(domElement);

const graphOptions = {
    grid: {
        left: 0, bottom: 0, top: 10, right: 0,
        height: 'auto', width: 'auto', containLabel: true
    },
    tooltip: {
        show: true,
        confine: true,
        trigger: 'axis',
        position: function(p, params) {
            return params[0].dataIndex <= 17 
                ? [p[0] + 20, '30%'] 
                : {top: '30%', left: p[0] - size.contentSize[0] - 25};
        },
        axisPointer: { type: 'shadow', shadowStyle: { color: 'rgba(0,222,255,0.3)' } }
    },
    xAxis: {
        data: categories,
        axisLine: { lineStyle: { color: '#397cbc' } },
        axisLabel: { 
            interval: 0, 
            rotate: -45, 
            color(value, index) {
                return index < 10 ? '#FCC059' : '#30EEE9';
            }
        }
    },
    yAxis: {
        type: 'value',
        minInterval: 1,
        axisLabel: { formatter: '{value}', color: '#2ad1d2' },
        axisLine: { show: false },
        axisTick: { show: false },
        splitLine: { lineStyle: { color: '#125770' } }
    },
    series: [
        // Lớp 1: Mặt chính
        {
            type: 'bar',
            barWidth: 20,
            data: values,
            itemStyle: { color: '#4A90E2' },
            barGap: 0
        },
        // Lớp 2: Mặt nghiêng bên phải (tạo bóng)
        {
            type: 'bar',
            barWidth: 20,
            itemStyle: { color: '#3773BA' },
            data: values,
            barGap: 0
        },
        // Lớp 3: Nắp trên cùng
        {
            name: 'topLayer',
            tooltip: { show: false },
            type: 'pictorialBar',
            symbol: 'rect',
            symbolRotate: 45,
            symbolSize: [30, 30],
            symbolOffset: ['0', '-17'],
            symbolPosition: 'end',
            data: values,
            z: 3,
            itemStyle: {
                normal: {
                    color: function(params) {
                        const palette = ["#FC9F9F","#F7B177","#BBF64A","#4DF4EE","#947FFA","#749f83","#ca8622","#bda29a","#6e7074","#546570"];
                        return palette[params.dataIndex % palette.length];
                    },
                    borderWidth: 0.01
                },
                label: {
                    show: true,
                    position: 'top',
                    textStyle: { color: 'black', fontSize: 16, fontFamily: 'Arial' }
                }
            }
        }
    ]
};

myInstance.setOption(graphOptions);

Thẻ: echarts JavaScript data-visualization frontend-development web-charting

Đăng vào ngày 23 tháng 7 lúc 13:15