jtblin / angular-chart.js

Reactive, responsive, beautiful charts for AngularJS using Chart.js: http://jtblin.github.io/angular-chart.js
Other
2.67k stars 761 forks source link

Gradient color for (line)charts #510

Open Doomic opened 7 years ago

Doomic commented 7 years ago

This issue will be an extention to #240 but i also find some results that may help.

ChartJs ChartJs supports that that linecolors and backgroundcolors (and perhaps more color object) are a gradient. This can be done by getting the ctx and call createLinearGradient function, push the result in the color object you want and there is a gradient line/background.

gradient = ctx.createLinearGradient(0, 0, 250, 0);
gradient.addColorStop(0, 'rgba(243, 103, 101,0.5)');
gradient.addColorStop(1, 'rgba(0, 89, 179,0.5)');
dataset.backgroundColor: gradient;

AngularChart Angular-chart can't give you the ctx (correct me if i am wrong). However just find the canvas with javascript and get the context(2d) will give the ctx. (just like you are used to in ChartJs) Then a made the gradient and put it in the color scope and/or the datasetoverride. However, this will not result in a gradient line or background. an other color will there (i believe it is the color for the latest draw object to the canvas).

Example http://jsbin.com/duforuyumu/1/edit?html,js,output

Solution After searching i found the problem. It is inside the angular.merge function. (this function is used inside the getDataSets function and the getDataSet function) this merge function (deep copy) loops trough all attributes and if the attribute is an object; this object will be merged again. This looks like a good way of dealing with merging objects. jQuery does also include a extend function with deep copy possibility. After implimenting this function insteed of the angular.merge the issues is fixed.

//angular.merge(dataset, datasetOverride[i]);
$.extend(true, dataset, datasetOverride[i]);

Search deeper This means jquery merge the object a little bit different. I got the source code of both functions and find out that jquery first determent if the object is a "normal" object.

var normalObject = {};
var gradient = ctx.createLinearGradient(0, 0, 250, 0);

typeof normalObject -> "object"
typeof gradient -> "object"

toString.call(normalObject) -> "[object Object]"
toString.call(gradient) -> "[object CanvasGradient]"

as you can see... both are objects. however not both are a "normal" object. Angular will try to loop trough the gradient attributes (but there are none). And jquery will copy (link) the gradient to the result (like a string or number just will be linked).

Final I understand that this merge function is from angular. So the team there should fix this problem... I will make an issue there as well. But for now, angular-chart can be fixed with a jquery extend.

Doomic commented 7 years ago

issue on angular github page https://github.com/angular/angular.js/issues/15180

jtblin commented 7 years ago

Great investigation @Doomic. Hopefully they can fix that in angular, I'd rather not have to roll my own merge function.

Doomic commented 7 years ago

If you don't mind. I leave the "fight" where to fix up to you. (personally i agree with you and this should be fixed in angular.)

if they doen't want to fix there code, here is the code they use, but with the improvement. you could use it for the merge situations

/**
* @ngdoc function
* @name angular.merge
* @module ng
* @kind function
*
* @description
* Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
* by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
*
* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
* objects, performing a deep copy.
*
* @param {Object} dst Destination object.
* @param {...Object} src Source object(s).
* @returns {Object} Reference to `dst`.
*/
function merge(dst) {
  return baseExtend(dst, slice.call(arguments, 1), true);
}

function baseExtend(dst, objs, deep) {
  var h = dst.$$hashKey;

  for (var i = 0, ii = objs.length; i < ii; ++i) {
    var obj = objs[i];
    if (!isObject(obj) && !isFunction(obj)) continue;
    var keys = Object.keys(obj);
    for (var j = 0, jj = keys.length; j < jj; j++) {
      var key = keys[j];
      var src = obj[key];

      if (deep && isObject(src)) {
        if (isDate(src)) {
          dst[key] = new Date(src.valueOf());
        } else if (isRegExp(src)) {
          dst[key] = new RegExp(src);
        } else if (src.nodeName) {
          dst[key] = src.cloneNode(true);
        } else if (isElement(src)) {
          dst[key] = src.clone();
        } else if (toString.call(src) != "[object Object]") { 
          dst[key] = src;
        } else {
          if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
          baseExtend(dst[key], [src], true);
        }
      } else {
        dst[key] = src;
      }
    }
  }

  setHashKey(dst, h);
  return dst;
}
rootux commented 7 years ago

Hey. I want to use gradient color and come to this thread. Did anyone was able to solve this?

Narretz commented 7 years ago

We've decided that this will not be fixed in AngularJS. Please use https://lodash.com/docs/4.17.4#merge or a similar function instead.