swimlane / ngx-charts

:bar_chart: Declarative Charting Framework for Angular
https://swimlane.github.io/ngx-charts/
MIT License
4.29k stars 1.15k forks source link

Feature Request: A way to add shadow to charts #333

Open richavyas opened 7 years ago

richavyas commented 7 years ago

I'm submitting a ... (check one with "x")

[ ] bug report => search github for a similar issue or PR before submitting
[x] feature request
[ ] support request => Please do not submit support request here

Current behavior Currently, there's no way to add shadow to charts.

Expected behavior A component that creates shadow to charts and add shadows attribute in the usage chart template.

Please let me know if this looks fine to you. I'll create a PR for this.

Please tell us about your environment:

richavyas commented 7 years ago

e.g. in case of bar-chart with light theme & gradient, it's gonna look like this

bars-with-shadow
marjan-georgiev commented 7 years ago

@richavyas interesting. Which charts would you add that to?

Hypercubed commented 7 years ago

Hi @richavyas , this can be done using a SVG filter definition and css. I'm not sure about the browser compatibility. Here is an example in plnkr:

https://plnkr.co/edit/isOUub?p=preview

richavyas commented 7 years ago

@marjan-georgiev I have created a new component, ngx-charts-svg-shadow which has SVG filter definition, as suggested by @Hypercubed.

Then charts like all the bar charts, line chart, area chart, etc. will have attribute shadow, and if user passes shadow=true, then the chart will have shadows. It's on the similar lines of adding gradient to any chart.

marjan-georgiev commented 7 years ago

@richavyas as @Hypercubed showed, this can be done without having to modify the chart components, simply by assigning that class to the chart.

richavyas commented 7 years ago

We would still have to add SVG filter definition not only CSS in every chart if we want to add shadow to it as shown in plunker:

<ngx-charts-bar-horizontal
      ...
</ngx-charts-bar-horizontal>

<svg>
      <defs>
        <filter id="myShadow" 
         ...
        </filter>
      </defs>
</svg>
Hypercubed commented 7 years ago

Not necessarily every chart, but, yes, would need to add an SVG filter definition somewhere.

richavyas commented 7 years ago

That's right @Hypercubed. that's why I thought of creating a component with SVG filter definition.

@Component({
  selector: 'g[ngx-charts-svg-shadow]',
  template: `
    <svg:filter
      [id]="name"
      ...
    </svg:filter>

and main component of any chart, e.g. bar.component.ts would have this:

<svg:defs *ngIf="hasShadow">
      <svg:g ngx-charts-svg-shadow
       ...
</svg:defs>
Hypercubed commented 7 years ago

@richavyas The filter definition doesn't need to be in the same SVG. One way we could do it is to add a ngx-charts-svg-shadow component, like you have suggested, then use classes for each chart to apply shadow.

For the user it could look like this:

<ngx-charts-svg-shadow
</ngx-charts-svg-shadow>
<!-- defines the shadow globally for the page, only needed once -->

<ngx-charts-bar-horizontal
  class="svg-shadow-filter" ... >
</ngx-charts-bar-horizontal>

The ngx-charts-svg-shadow component and CSS class will have to be defined in ngx-charts. I foresee some issue with IDs, since they need to be unique. If you add any attributes to ngx-charts-svg-shadow-filter how do you ensure the CSS references to the correct url(#id)?

An alternative, and perhaps the most flexible, allowing users to define their own filters. This could look something like:

<svg>
  <defs>
    <filter id="someUniqueId" #myShadowFilter ... />
       ...
    </filter>
  </defs>
</svg>

<ngx-charts-bar-vertical
  [barFilter]="myShadowFilter" ... > <!-- references the filter by template reference or id -->
</ngx-charts-bar-vertical>

Might be interesting to explore creating declarative components for SVG filter definitions in ngx-charts to hide some of the complexity and ensure unique IDs. Perhaps looking like this:

<ngx-charts-svg-shadow
  #myShadowFilter
  [color]="color"
  [dx]="dx"
  [dy]="dy" ...>
</ngx-charts-svg-shadow>

<ngx-charts-bar-vertical
  [barFilterRef]="myShadowFilter" ... >
</ngx-charts-bar-vertical>

This is all suggestions. Any thoughts?

Hypercubed commented 7 years ago

I wonder if we could use projection here:

<ngx-charts-bar-vertical
  [barFilterRef]="myShadowFilter" ... >
  <defs>
    <ngx-charts-svg-shadow
      #myShadowFilter
      [color]="color"
      [dx]="dx"
      [dy]="dy" ...>
    </ngx-charts-svg-shadow>
  </defs>
</ngx-charts-bar-vertical>
richavyas commented 7 years ago

Hi @Hypercubed, i am leaning the most towards the following approach as you suggested:

<ngx-charts-svg-shadow
  #myShadowFilter
  [color]="color"
  [dx]="dx"
  [dy]="dy" ...>
</ngx-charts-svg-shadow>

<ngx-charts-bar-vertical
  [barFilterRef]="myShadowFilter" ... >
</ngx-charts-bar-vertical>

Will open a PR for this.