Vue-chartjs: Example to create gradient background for chart from child component canvas context

Created on 14 Nov 2018  ยท  6Comments  ยท  Source: apertureless/vue-chartjs

Expected Behavior

This question has been asked before at https://stackoverflow.com/questions/51022003/vue-chartjs-create-gradient-background-for-chart-from-child-component-canvas. However I think a lot of people would benefit from an example in the documentation somewhere.

The author in this article like me was 'confused' as to what was meant by "..pass in all data as props" . I still don't know what I need to do to get this working. My scenario is exactly the same as the author.

The actual reusable chart code is:

```import { Bar, mixins } from 'vue-chartjs';

export default {
extends: Bar,
gradient: null,
mixins: [mixins.reactiveProp],
props: ['chartData', 'options'],
mounted() {

    var gradientStroke = this.$refs.canvas.getContext('2d').createLinearGradient(0, 230, 0, 50);

    gradientStroke.addColorStop(1, 'rgba(253,93,147,0.8)');
    gradientStroke .addColorStop(0, 'rgba(253,93,147,0)'); //blue colors

//I don't know what to do with it here. Because if the chart isn't reusable and I add my chartdata here it works fine. But I want it to be reusable

    this.renderChart(this.chartData, this.options);

}

};

### Actual Behavior

Then in my parent code:

```this.myChartData = {
                labels: labels,
                datasets: [{
                    label: 'Name',
                    fill: true,
                    backgroundColor: '#ff5991',
                    hoverBackgroundColor: '#ff5991',
                    borderColor: '#ff5991',
                    borderWidth: 2,
                    borderDash: [],
                    borderDashOffset: 0.0,
                    data: chartdata
                }]
            };

Please can we add an example for this as I know this is for issues only but there isn't anyone or Gittur to talk to.

Many thanks

Environment

  • vue.js version: 2.5.17
  • vue-chart.js version: 3.4.0
  • npm version: -/-
โ“ question

Most helpful comment

Well, if you want a reuseable chart, it depends on mulitple factors, because there are multiple ways of doing it.

The more generic the solution should be, the more complex it will get.

The questions are, what should be reuseable and configurable?

The easiest solution is if you just have a line chart for example with only one dataset. And the colors/gradients are fixed.

import {Line} from 'vue-chartjs'

export default {
  extends: Line,
  props: {
    labels: {
      type: Array,
      default: () => ['A', 'B', 'C']
    },
    datalabel: {
       type: String,
       default: 'Downloads per Week'
    },
    chartdata: {
      type: Array,
      default: () => [100, 40, 106]
    }
  },
  data () {
    return {
      gradient: null
    }
  },
  mounted () {
    this.gradient = this.$refs.canvas.getContext('2d').createLinearGradient(0, 0, 0, 450)


    this.gradient.addColorStop(0, 'rgba(255, 0,0, 0.5)')
    this.gradient.addColorStop(0.5, 'rgba(255, 0, 0, 0.25)');
    this.gradient.addColorStop(1, 'rgba(255, 0, 0, 0)');



    this.renderChart({
      labels: this.labels,
      datasets: [
        {
          label: this.datalabel,
          borderColor: '#FC2525',
          pointBackgroundColor: 'white',
          borderWidth: 1,
          pointBorderColor: 'white',
          backgroundColor: this.gradient,
          data: this.chartdata
        }
      ]
    }, {responsive: true, maintainAspectRatio: false})

  }
}

This way, you can pass in your data, labels etc. and have a reuseable chart.

You could also make it a bit simpler if you use the rest/spread operator.

Chart with spread

import {Line} from 'vue-chartjs'

export default {
  extends: Line,
  props: {
    labels: {
      type: Array,
      default: () => ['A', 'B', 'C']
    },
    chartconfig: {
       type: Object,
       default: () => {
          label: 'My Data',
          borderColor: '#FC2525',
          pointBackgroundColor: 'white',
          borderWidth: 1,
          pointBorderColor: 'white',
          data: [10, 11, 12]
       }
    }
  },
  data () {
    return {
      gradient: null
    }
  },
  mounted () {
    this.gradient = this.$refs.canvas.getContext('2d').createLinearGradient(0, 0, 0, 450)


    this.gradient.addColorStop(0, 'rgba(255, 0,0, 0.5)')
    this.gradient.addColorStop(0.5, 'rgba(255, 0, 0, 0.25)');
    this.gradient.addColorStop(1, 'rgba(255, 0, 0, 0)');



    this.renderChart({
      labels: this.labels,
      datasets: [
        {
          ...this.chartconfig
          backgroundColor: this.gradient,
        }
      ]
    }, {responsive: true, maintainAspectRatio: false})

  }
}

This way you are setting the backgroundColor gradient and the rest of the data and config comes over your vue props.


It becomes a bit more complicated if you want configurable gradients.
If you just want different colors you can pass in the gradient colors as props and then in your mounted() hook just reference them

this.gradient.addColorStop(0, '`${this.gradientColor1}`')

If you want more configuration you have to write a small generateGradient helper, which takes his data from props or a gradient Object which is a prop.

For example

// Example object with your configuration for your gradient.
const settings = {
 gradientColorStops: 3,
 colorStopValues: [0, 0.5, 1],
 colorStopColors: ['rgba(...)', 'rgba(...)', 'rgba(...)']
}
...

props: {
 gradient: {
  type: Boolean,
  default: true
 }
 gradientSettings: {
   type: Object,
   default: () => {}
 }
}
....

mounted () {
  if (this.gradient) {
    this.generateGradient()
   // in generateGradient you can iterate over the values you got passed in your gradientSettings for (let i = 0; i < this.gradientSettings.gradientColorStops; i++)

  }
},

All 6 comments

Well, if you want a reuseable chart, it depends on mulitple factors, because there are multiple ways of doing it.

The more generic the solution should be, the more complex it will get.

The questions are, what should be reuseable and configurable?

The easiest solution is if you just have a line chart for example with only one dataset. And the colors/gradients are fixed.

import {Line} from 'vue-chartjs'

export default {
  extends: Line,
  props: {
    labels: {
      type: Array,
      default: () => ['A', 'B', 'C']
    },
    datalabel: {
       type: String,
       default: 'Downloads per Week'
    },
    chartdata: {
      type: Array,
      default: () => [100, 40, 106]
    }
  },
  data () {
    return {
      gradient: null
    }
  },
  mounted () {
    this.gradient = this.$refs.canvas.getContext('2d').createLinearGradient(0, 0, 0, 450)


    this.gradient.addColorStop(0, 'rgba(255, 0,0, 0.5)')
    this.gradient.addColorStop(0.5, 'rgba(255, 0, 0, 0.25)');
    this.gradient.addColorStop(1, 'rgba(255, 0, 0, 0)');



    this.renderChart({
      labels: this.labels,
      datasets: [
        {
          label: this.datalabel,
          borderColor: '#FC2525',
          pointBackgroundColor: 'white',
          borderWidth: 1,
          pointBorderColor: 'white',
          backgroundColor: this.gradient,
          data: this.chartdata
        }
      ]
    }, {responsive: true, maintainAspectRatio: false})

  }
}

This way, you can pass in your data, labels etc. and have a reuseable chart.

You could also make it a bit simpler if you use the rest/spread operator.

Chart with spread

import {Line} from 'vue-chartjs'

export default {
  extends: Line,
  props: {
    labels: {
      type: Array,
      default: () => ['A', 'B', 'C']
    },
    chartconfig: {
       type: Object,
       default: () => {
          label: 'My Data',
          borderColor: '#FC2525',
          pointBackgroundColor: 'white',
          borderWidth: 1,
          pointBorderColor: 'white',
          data: [10, 11, 12]
       }
    }
  },
  data () {
    return {
      gradient: null
    }
  },
  mounted () {
    this.gradient = this.$refs.canvas.getContext('2d').createLinearGradient(0, 0, 0, 450)


    this.gradient.addColorStop(0, 'rgba(255, 0,0, 0.5)')
    this.gradient.addColorStop(0.5, 'rgba(255, 0, 0, 0.25)');
    this.gradient.addColorStop(1, 'rgba(255, 0, 0, 0)');



    this.renderChart({
      labels: this.labels,
      datasets: [
        {
          ...this.chartconfig
          backgroundColor: this.gradient,
        }
      ]
    }, {responsive: true, maintainAspectRatio: false})

  }
}

This way you are setting the backgroundColor gradient and the rest of the data and config comes over your vue props.


It becomes a bit more complicated if you want configurable gradients.
If you just want different colors you can pass in the gradient colors as props and then in your mounted() hook just reference them

this.gradient.addColorStop(0, '`${this.gradientColor1}`')

If you want more configuration you have to write a small generateGradient helper, which takes his data from props or a gradient Object which is a prop.

For example

// Example object with your configuration for your gradient.
const settings = {
 gradientColorStops: 3,
 colorStopValues: [0, 0.5, 1],
 colorStopColors: ['rgba(...)', 'rgba(...)', 'rgba(...)']
}
...

props: {
 gradient: {
  type: Boolean,
  default: true
 }
 gradientSettings: {
   type: Object,
   default: () => {}
 }
}
....

mounted () {
  if (this.gradient) {
    this.generateGradient()
   // in generateGradient you can iterate over the values you got passed in your gradientSettings for (let i = 0; i < this.gradientSettings.gradientColorStops; i++)

  }
},

@apertureless this code looks great but to make i reactive do I need to add a watcher? BEcauseat the moment it doesn't update data when my api is finished making the call. If I hard code the values in the props on the chart component it works?

Well, you never mentioned any api calls ;)

You could just add a v-if="loaded" to your chart. So it only renders if your api call is finished

https://vue-chartjs.org/guide/#chart-with-api-data

However, please use stackoverflow for this kind of questions. The issues here are more for feature requests / bugs and not for "how do I do xxx"

OK I get it, I do apologise but there isn't much help there sometimes. Anyway this would still be beneficial to have in the docs though

I mean your code samples you gave me a few minutes ago not the v-if thing

Thanks anyway you have been most helpful and I appreciate the library a lot :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rzb picture rzb  ยท  4Comments

Scalpel78 picture Scalpel78  ยท  4Comments

DavidSotoA picture DavidSotoA  ยท  3Comments

kurbar picture kurbar  ยท  4Comments

gkatsanos picture gkatsanos  ยท  3Comments