skip to Main Content

Im working on displaying radar chart. I need to apply 2 color in each point labels. A image attached below. For example if first label ‘Eating 65 / 28’ then 65 is in red abd 28 in blue.
enter image description here
I have applied html style on it something like Eating <span style="color:red;font-weight:bold;">65</span> <span style="color:blue;font-weight:bold;">28</span> but style didn’t rendered. Is there anyway to do this. below is the code.

const data = {
  labels: [
    'Eating 65 / 28',
    'Drinking 59 / 48',
    'Sleeping 90 / 40',
    'Designing 81 / 19',
    'Coding 56 / 96',
    'Cycling 55 / 27',
    'Running 48 / 100'
  ],
  datasets: [{
    label: 'My First Dataset',
    data: [65, 59, 90, 81, 56, 55, 40],
    fill: true,
    backgroundColor: 'rgba(255, 99, 132, 0.2)',
    borderColor: 'rgb(255, 99, 132)',
    pointBackgroundColor: 'rgb(255, 99, 132)',
    pointBorderColor: '#fff',
    pointHoverBackgroundColor: '#fff',
    pointHoverBorderColor: 'rgb(255, 99, 132)'
  }, {
    label: 'My Second Dataset',
    data: [28, 48, 40, 19, 96, 27, 100],
    fill: true,
    backgroundColor: 'rgba(54, 162, 235, 0.2)',
    borderColor: 'rgb(54, 162, 235)',
    pointBackgroundColor: 'rgb(54, 162, 235)',
    pointBorderColor: '#fff',
    pointHoverBackgroundColor: '#fff',
    pointHoverBorderColor: 'rgb(54, 162, 235)'
  }]
};

const config = {
  type: 'radar',
  data: data,
  options: {
    elements: {
      line: {
        borderWidth: 3
      }
    }
  },
};

2

Answers


  1. You want each point label on your radar chart to appear in two different colors. You’ve tried to accomplish this using HTML styling, but you’ve noted that the style isn’t applied. The Chart.js library does not support direct HTML tags, so you cannot apply styles using tags like span. Instead, you will need to develop a custom solution using the options and callback functions provided by Chart.js.

    Login or Signup to reply.
  2. It can be done, but it is far from trivial. Also, we may ask ourselves when having some fancy of styling a chart if it’s really useful, does it provide more information, or might it tire or scare away the user who is typically happy to see the same type of data depicted in the same kind style.

    In any case, the problem is technically interesting. The implementation is based on a custom axis, RadialLinearScaleCustomPointLabels, that extends RadialLinearScale, to provide a (scriptable) option pointStyle.draw.callback that allows drawing a label in sequences that can be styled differently. That is done through the context of the call, that provides the function this.drawText(text) to draw a part of the label, preceded by call to either this.setStyle({color, bold}), or this.setDefaultStyle to set its style. Better seen actually used, as in the following example.

    // global variables obtained from the umd script, no modules
    const RadialLinearScale = Chart.RadialLinearScale;
    const {renderText, toFont, color: makeColor} = Chart.helpers;
    
    // // global variables obtained via import, in a module environment
    // import {Chart, RadialLinearScale, registerables} from "./chart.js";
    // Chart.register(...registerables);
    // import {renderText, toFont, color as makeColor} from './helpers.js';
    
    class RadialLinearScaleCustomPointLabels extends RadialLinearScale{
        static id = 'radial_custom_point_labels';
    
        static defaults = {
            ...RadialLinearScale.defaults,
            pointLabels: {
                ...RadialLinearScale.defaults.pointLabels,
                draw: {
                    callback(label){ this.drawText(label); }
                }
            }
        }
    
        drawGrid(){
            const display = this.options.pointLabels.display;
            if(display){
                this.options.pointLabels.display = false;
            }
            super.drawGrid();
            if(display){
                const thisScale = this;
                const {options: {pointLabels: pointLabelsOpts}} = this;
                this._pointLabels.forEach(
                    (pointLabel, i) => {
                        const optsAtIndex = pointLabelsOpts.setContext(thisScale.getPointLabelContext(i));
                        thisScale.customDrawLabel(pointLabel, thisScale._pointLabelItems[i], optsAtIndex);
                    }
                )
            }
            this.options.pointLabels.display = display;
        }
    
        _newRenderContext(ctx, fullText, plFont, x, y, defaultColor, textAlign){
            let _restore;
            const hackCtx = () => {
                _restore = ctx.restore;
                ctx.restore = () => null;
            }
            const restoreCtx = () => {
                ctx.restore = _restore.bind(ctx);
                ctx.restore();
            }
    
            if(textAlign !== 'left'){
                // change horizontal alignment to left, so we can measure the text after it was rendered.
                hackCtx();
                renderText(ctx, '', this._x, y, plFont, {
                    color: 'rgba(0,0,0,0)',
                    textAlign,
                    textBaseline: 'middle'
                });
                const m = ctx.measureText(fullText);
                restoreCtx();
                if(textAlign === 'center'){
                    x -= m.width/2;
                }
                else if(textAlign === 'right'){
                    x -= m.width;
                }
    
                textAlign = 'left';
            }
    
            return {
                _color: defaultColor,
                _userBold: false,
    
                _x: x,
                setDefaultStyle(){
                    this._color = defaultColor;
                    this._userBold = false;
                },
                setStyle({color, bold}){
                    this._color = color;
                    this._userBold = bold;
                },
                drawText(txt){
                    hackCtx();
                    renderText(ctx, txt, this._x, y, plFont, {
                        color: this._color,
                        textAlign,
                        textBaseline: 'middle',
                        ... this._userBold ? {
                            strokeColor: makeColor(this._color).alpha(0.3).rgbString(),
                            strokeWidth: 2
                        } : {}
                    });
                    this._x += ctx.measureText(txt).width;
                    restoreCtx();
                }
            }
        }
    
        customDrawLabel(label, pos, opts){
            if(!pos.visible){
                return;
            }
            const plFont = toFont(opts.font);
            const color = opts.color ?? 'rgb(0,0,0)';
            const { x , y , textAlign  } = pos;
            opts.draw.callback?.call?.(
                 this._newRenderContext(this.ctx, label, plFont, x, y + plFont.lineHeight / 2, color, textAlign), label);
        }
    }
    
    
    Chart.register(RadialLinearScaleCustomPointLabels);
    
    
    const data = {
        labels: [
            'Eating 65 / 28',
            'Drinking 59 / 48',
            'Sleeping 90 / 40',
            'Designing 81 / 19',
            'Coding 56 / 96',
            'Cycling 55 / 27',
            'Running 48 / 100'
        ],
        datasets: [{
            label: 'My First Dataset',
            data: [65, 59, 90, 81, 56, 55, 40],
            fill: true,
            backgroundColor: 'rgba(255, 99, 132, 0.2)',
            borderColor: 'rgb(255, 99, 132)',
            pointBackgroundColor: 'rgb(255, 99, 132)',
            pointBorderColor: '#fff',
            pointHoverBackgroundColor: '#fff',
            pointHoverBorderColor: 'rgb(255, 99, 132)'
        }, {
            label: 'My Second Dataset',
            data: [28, 48, 40, 19, 96, 27, 100],
            fill: true,
            backgroundColor: 'rgba(54, 162, 235, 0.2)',
            borderColor: 'rgb(54, 162, 235)',
            pointBackgroundColor: 'rgb(54, 162, 235)',
            pointBorderColor: '#fff',
            pointHoverBackgroundColor: '#fff',
            pointHoverBorderColor: 'rgb(54, 162, 235)'
        }]
    };
    
    const config = {
        type: 'radar',
        data: data,
        options: {
            elements: {
                line: {
                    borderWidth: 3
                }
            },
            scales:{
                r: {
                    type: 'radial_custom_point_labels',
                    pointLabels: {
                        draw: {
                            callback: function(labelText){
                                const [_, text, number1, slash, number2] = labelText.match(/^(D+)(d+)(s*/s*)(d+)$/);
                                this.setDefaultStyle();
                                this.drawText(text);
                                this.setStyle({color: 'rgb(255, 99, 132)', bold: true});
                                this.drawText(number1);
                                this.setDefaultStyle();
                                this.drawText(slash);
                                this.setStyle({color: 'rgb(54, 162, 235)', bold: true});
                                this.drawText(number2);
                            }
                        }
                    }
                }
            }
        },
    };
    new Chart(document.querySelector('#chart1'), config);
    <div style="min-height: 60vh">
        <canvas id="chart1">
        </canvas>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search