Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gradients not working #43

Closed
Blazsoul opened this issue Feb 13, 2020 · 7 comments
Closed

Gradients not working #43

Blazsoul opened this issue Feb 13, 2020 · 7 comments

Comments

@Blazsoul
Copy link

Blazsoul commented Feb 13, 2020

Hi, first of all many thanks for this amazing library

I have noticed that i cant pass gradient color in any way. Currently i am using POST method and stringify the chart parameter so it will be transformed to code on the server side.
And for some reason the gradient color transcoded to undefined after i use stringify

I am wondering if it is even possible to pass gradients

This is my code:

const { stringify } = require('javascript-stringify');
const {createCanvas, loadImage} = require('canvas')
const canvas = createCanvas(200, 200)
const ctx = canvas.getContext('2d')

const getGradientFill = (...colors) => {
    const gradientFill = ctx.createLinearGradient(500, 0, 100, 0);
    colors.forEach((color,i) => gradientFill.addColorStop(i, color));
    return gradientFill;
};

const gradientGreen = getGradientFill("#6ba141", "#84d66a");
const gradientRed = getGradientFill("#a0191e", "#c32d38");

const values = [42.5, -75.3, 100, -25.1];

const barChart = {
        backgroundColor: "white",
        width: 1080,
        height: 1080,
        format: "png",
        chart: stringify({
            type: 'bar',
            data: {
                labels: ["Week", "Month", "Quarter", "YTD"],
                "yLabels": [
                    100,
                    -100
                ],
                datasets: [
                    {
                        data: values,
                        // stack: "stack0",
                        backgroundColor: values.map(value => value < 0 ? gradientRed : "#84d66a"),
                        datalabels: {
                            // display labels for this specific dataset
                            display: true,
                            anchor: (context) => {
                                const index = context.dataIndex;
                                const value = context.dataset.data[index];
                                return value > 0 ? "start" : "end"
                                // return "end"
                            },
                            align: "start",
                            formatter:  (value) => value + "%",
                            color: "white",
                            font: {
                                size: 34,
                                weight: "bold"
                            }
                        },
                        borderColor: "black",
                        borderWidth: 2,
                    },
                ]
            },
            options: {
                barRoundness: 10,
                legend: {display: false},
                title: {
                    display: true,
                },
                layout: {
                    padding: {
                        left: 143,
                        right: 143,
                        top: 446,
                        bottom: 83
                    }
                },
                scales: {
                    yAxes: [
                        {
                            display: false,
                            gridLines: false,
                            suggestedMin: -100,
                            suggestedMax: 100,
                            stacked: true
                        }
                    ],
                    xAxes: [
                        {
                            // display: false,
                            stacked: true,
                            gridLines: false
                        }
                    ]
                }
            }
        })
    };
@typpo
Copy link
Owner

typpo commented Feb 13, 2020

Thanks for the report. It looks like there is a gap in capability because the CanvasGradient type cannot be serialized by javascript-stringify or in any other way.

I think the solution is for QuickChart to provide a predefined function getGradientFill that is available in your chart definition. I've added this experimentally to the master branch. This functionality is live, experimentally, on quickchart.io. Can you let me know how it works for you?

Here's an example:
image

link

{
  type: 'bar',
  data: {
    labels: [2012, 2013, 2014, 2015, 2016],
    datasets: [{
      label: 'Raisins',
      data: [12, 6, 5, 18, 12],
      backgroundColor: getGradientFill("blue", "pink"),
    }, {
      label: 'Bananas',
      data: [4, 8, 16, 5, 5],
      backgroundColor: getGradientFill("#ff0000", "#ffff00"),
    }]
  }
}

@Blazsoul
Copy link
Author

Blazsoul commented Feb 13, 2020

Thanks for the really fast response and deploy, it worked!
I think its a bit hard to implement, but the possibility to do it is really important.
I had to manually insert the gradientFill function to the string after javascript-stringify using replace

backgroundColor: values.map(value => value < 0 ? "__gradientRed__" : "__gradientGreen__")

data.chart.replace(/'__gradientRed__'/g,getGradientFill( "#a0191e", "#c32d38"))
data.chart.replace(/'__gradientGreen__'/g,getGradientFill( "#6ba141", "#84d66a"))

Because my function was just for testing purposes, its really not generic.
So meanwhile i have built a more generic one with color offset and linear direction control, i think that it could be some solution for the meantime. but i will think about something more easy to implement.

The new function(the default values are just for readability like typescript):

const getGradientFill = (colorsOptions = [{offset:0, color: "white"}], linearGradient = [500, 0 , 100, 0]) => { const gradientFill = ctx.createLinearGradient(...linearGradient); colorsOptions.forEach((options) => gradientFill.addColorStop(options.offset, options.color)); return gradientFill; };

Example:
const gradientRed = getGradientFill([{ offset: 0, color : "#a0191e"},{ offset: 0.5, color :"#c32d38"},{ offset: 1, color : "#a0191e"}], [100, 0 , 30, 0]);

@typpo
Copy link
Owner

typpo commented Feb 13, 2020

Thanks, I'll add the generalized function later today and update this thread. I agree the use of the function is very clumsy when using javascript-stringify.. I'll see if I can think of a better way.

typpo added a commit that referenced this issue Feb 13, 2020
typpo added a commit that referenced this issue Feb 13, 2020
@typpo
Copy link
Owner

typpo commented Feb 13, 2020

Hi @Blazsoul, I've updated the API with a generalized getGradientFill (the old example will no longer work):

{
  type: 'bar',
  data: {
    labels: [2012, 2013, 2014, 2015, 2016],
    datasets: [{
      label: 'Gradient example',
      data: [12, 6, 5, 18, 12],
      backgroundColor: getGradientFill([{ offset: 0, color : "red"},{ offset: 0.5, color :"#b19cd9"},{ offset: 1, color : "#dafdd3"}], [100, 0 , 300, 0]),
    }]
  }
}

image

I've also added a helper function getGradientFillHelper that takes a direction and a list of colors and creates an evenly spaced gradient, according to the dimensions of the chart:

{
  type: 'bar',
  data: {
    labels: [2012, 2013, 2014, 2015, 2016],
    datasets: [{
      label: 'Gradient example',
      data: [12, 6, 5, 18, 12],
      backgroundColor: getGradientFillHelper('horizontal', ["red", "#b19cd9", "#dafdd3"]),
    }]
  }
}

image

You can try it out, and if this all looks good to you I'll add it to the documentation.

@Blazsoul
Copy link
Author

Blazsoul commented Feb 14, 2020

Works great!
Another suggestion i have is the ability to override height and width, send them to the third parameter of getGradientFillHelper, cuz is some cases the bar gradient get stretched to full width or height, but the bar is smaller than full form(if u add padding as well)

Example(look at week and quarter, its the same gradient):
getGradientFillHelper('vertical', ["red", "blue", "yellow"])

image

My suggestion:
getGradientFillHelper('vertical', ["red", "blue", "yellow"], {height: 270})

@typpo
Copy link
Owner

typpo commented Feb 22, 2020

Hey @Blazsoul, sorry for the delay. I've added an optional dimensions parameter that lets the user specify width and height:

{
  type: 'bar',
  data: {
    labels: [2012, 2013, 2014, 2015, 2016],
    datasets: [{
      label: 'Gradient example',
      data: [12, 6, 5, 18, 12],
      backgroundColor: getGradientFillHelper('horizontal', ["red", "#b19cd9", "#dafdd3"], {width:200}),
    }]
  }
}

image

@Blazsoul
Copy link
Author

Works great, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants