mirror of
https://github.com/chartjs/Chart.js.git
synced 2026-03-12 11:16:51 +01:00
Configurable hide/show animations (#7055)
Configurable hide/show animations
This commit is contained in:
@@ -20,9 +20,10 @@ The following animation options are available. The global options for are define
|
||||
| `active` | `object` | `{ duration: 400 }` | Option overrides for `active` animations (hover)
|
||||
| `resize` | `object` | `{ duration: 0 }` | Option overrides for `resize` animations.
|
||||
| [property] | `object` | `undefined` | Option overrides for [property].
|
||||
| [collection] | `object` | `undefined` | Option overrides for multiple properties, identified by `properties` array.
|
||||
| [collection] | `object` | [defaults...](#default-collections) | Option overrides for multiple properties, identified by `properties` array.
|
||||
| [mode] | `object` | [defaults...](#default-modes) | Option overrides for update mode. Core modes: `'active'`, `'hide'`, `'reset'`, `'resize'`, `'show'`. A custom mode can be used by passing a custom `mode` to [update](../developers/api.md#updatemode)
|
||||
|
||||
Default collections:
|
||||
### Default collections
|
||||
|
||||
| Name | Option | Value
|
||||
| ---- | ------ | -----
|
||||
@@ -35,6 +36,17 @@ Direct property configuration overrides configuration of same property in a coll
|
||||
|
||||
These defaults can be overridden in `options.animation` and `dataset.animation`.
|
||||
|
||||
### Default modes
|
||||
|
||||
| Mode | Option | Value
|
||||
| -----| ------ | -----
|
||||
| active | duration | 400
|
||||
| resize | duration | 0
|
||||
| show | colors | `{ type: 'color', properties: ['borderColor', 'backgroundColor'], from: 'transparent' }`
|
||||
| | visible | `{ type: 'boolean', duration: 0 }`
|
||||
| hide | colors | `{ type: 'color', properties: ['borderColor', 'backgroundColor'], to: 'transparent' }`
|
||||
| | visible | `{ type: 'boolean', easing: 'easeInExpo' }`
|
||||
|
||||
## Easing
|
||||
|
||||
Available options are:
|
||||
|
||||
@@ -28,7 +28,7 @@ myLineChart.update(); // Calling update now animates the position of March from
|
||||
|
||||
> **Note:** replacing the data reference (e.g. `myLineChart.data = {datasets: [...]}` only works starting **version 2.6**. Prior that, replacing the entire data object could be achieved with the following workaround: `myLineChart.config.data = {datasets: [...]}`.
|
||||
|
||||
A `mode` string can be provided to indicate what should be updated and what animation configuration should be used. Core calls this method using any of `undefined`, `'reset'`, `'resize'` or `'active'`. `'none'` is also a supported mode for skipping animations for single update.
|
||||
A `mode` string can be provided to indicate what should be updated and what animation configuration should be used. Core calls this method using any of `'active'`, `'hide'`, `'reset'`, `'resize'`, `'show'` or `undefined`. `'none'` is also a supported mode for skipping animations for single update. Please see [animations](../configuration/animations.md) docs for more details.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -164,4 +164,20 @@ Like [setDatasetVisibility](#setdatasetvisibility) except that it hides only a s
|
||||
```javascript
|
||||
chart.setDataVisibility(0, 2, false); // hides the item in dataset 0, at index 2
|
||||
chart.update(); // chart now renders with item hidden
|
||||
```
|
||||
```
|
||||
|
||||
## hide(datasetIndex)
|
||||
|
||||
Sets the visibility for the given dataset to false. Updates the chart and animates the dataset with `'hide'` mode. This animation can be configured under the `hide` key in animation options. Please see [animations](../configuration/animations.md) docs for more details.
|
||||
|
||||
```javascript
|
||||
chart.hide(1); // hides dataset at index 1 and does 'hide' animation.
|
||||
```
|
||||
|
||||
## show(datasetIndex)
|
||||
|
||||
Sets the visibility for the given dataset to true. Updates the chart and animates the dataset with `'show'` mode. This animation can be configured under the `show` key in animation options. Please see [animations](../configuration/animations.md) docs for more details.
|
||||
|
||||
```javascript
|
||||
chart.show(1); // shows dataset at index 1 and does 'show' animation.
|
||||
```
|
||||
|
||||
@@ -263,9 +263,9 @@ class BarController extends DatasetController {
|
||||
|
||||
update(mode) {
|
||||
const me = this;
|
||||
const rects = me._cachedMeta.data;
|
||||
const meta = me._cachedMeta;
|
||||
|
||||
me.updateElements(rects, 0, mode);
|
||||
me.updateElements(meta.data, 0, mode);
|
||||
}
|
||||
|
||||
updateElements(rectangles, start, mode) {
|
||||
|
||||
@@ -52,9 +52,7 @@ class LineController extends DatasetController {
|
||||
}
|
||||
|
||||
// Update Points
|
||||
if (meta.visible) {
|
||||
me.updateElements(points, 0, mode);
|
||||
}
|
||||
me.updateElements(points, 0, mode);
|
||||
}
|
||||
|
||||
updateElements(points, start, mode) {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
import helpers from '../helpers';
|
||||
import {effects} from '../helpers/helpers.easing';
|
||||
import {resolve} from '../helpers/helpers.options';
|
||||
|
||||
const transparent = 'transparent';
|
||||
const interpolators = {
|
||||
number: function(from, to, factor) {
|
||||
return from + (to - from) * factor;
|
||||
boolean: function(from, to, factor) {
|
||||
return factor > 0.5 ? to : from;
|
||||
},
|
||||
color: function(from, to, factor) {
|
||||
var c0 = helpers.color(from || transparent);
|
||||
@@ -13,30 +15,23 @@ const interpolators = {
|
||||
return c1 && c1.valid
|
||||
? c1.mix(c0, factor).rgbaString()
|
||||
: to;
|
||||
},
|
||||
number: function(from, to, factor) {
|
||||
return from + (to - from) * factor;
|
||||
}
|
||||
};
|
||||
|
||||
class Animation {
|
||||
constructor(cfg, target, prop, to) {
|
||||
const me = this;
|
||||
let from = cfg.from;
|
||||
const currentValue = target[prop];
|
||||
|
||||
if (from === undefined) {
|
||||
from = target[prop];
|
||||
}
|
||||
if (to === undefined) {
|
||||
to = target[prop];
|
||||
}
|
||||
|
||||
if (from === undefined) {
|
||||
from = to;
|
||||
} else if (to === undefined) {
|
||||
to = from;
|
||||
}
|
||||
to = resolve([cfg.to, to, currentValue, cfg.from]);
|
||||
let from = resolve([cfg.from, currentValue, to]);
|
||||
|
||||
me._active = true;
|
||||
me._fn = cfg.fn || interpolators[cfg.type || typeof from];
|
||||
me._easing = helpers.easing.effects[cfg.easing || 'linear'];
|
||||
me._easing = effects[cfg.easing || 'linear'];
|
||||
me._start = Math.floor(Date.now() + (cfg.delay || 0));
|
||||
me._duration = Math.floor(cfg.duration);
|
||||
me._loop = !!cfg.loop;
|
||||
|
||||
@@ -5,25 +5,55 @@ import Animation from './core.animation';
|
||||
import defaults from '../core/core.defaults';
|
||||
import {noop, extend, isObject} from '../helpers/helpers.core';
|
||||
|
||||
const numbers = ['x', 'y', 'borderWidth', 'radius', 'tension'];
|
||||
const colors = ['borderColor', 'backgroundColor'];
|
||||
|
||||
defaults._set('animation', {
|
||||
// Plain properties can be overridden in each object
|
||||
duration: 1000,
|
||||
easing: 'easeOutQuart',
|
||||
onProgress: noop,
|
||||
onComplete: noop,
|
||||
|
||||
// Property sets
|
||||
colors: {
|
||||
type: 'color',
|
||||
properties: colors
|
||||
},
|
||||
numbers: {
|
||||
type: 'number',
|
||||
properties: numbers
|
||||
},
|
||||
|
||||
// Update modes. These are overrides / additions to the above animations.
|
||||
active: {
|
||||
duration: 400
|
||||
},
|
||||
resize: {
|
||||
duration: 0
|
||||
},
|
||||
numbers: {
|
||||
type: 'number',
|
||||
properties: ['x', 'y', 'borderWidth', 'radius', 'tension']
|
||||
show: {
|
||||
colors: {
|
||||
type: 'color',
|
||||
properties: colors,
|
||||
from: 'transparent'
|
||||
},
|
||||
visible: {
|
||||
type: 'boolean',
|
||||
duration: 0 // show immediately
|
||||
},
|
||||
},
|
||||
colors: {
|
||||
type: 'color',
|
||||
properties: ['borderColor', 'backgroundColor']
|
||||
},
|
||||
onProgress: noop,
|
||||
onComplete: noop
|
||||
hide: {
|
||||
colors: {
|
||||
type: 'color',
|
||||
properties: colors,
|
||||
to: 'transparent'
|
||||
},
|
||||
visible: {
|
||||
type: 'boolean',
|
||||
easing: 'easeInExpo' // for keeping the dataset visible almost all the way through the animation
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
function copyOptions(target, values) {
|
||||
@@ -132,6 +162,10 @@ export default class Animations {
|
||||
continue;
|
||||
}
|
||||
let value = values[prop];
|
||||
let animation = running[prop];
|
||||
if (animation) {
|
||||
animation.cancel();
|
||||
}
|
||||
|
||||
const cfg = animatedProps.get(prop);
|
||||
if (!cfg || !cfg.duration) {
|
||||
@@ -140,10 +174,6 @@ export default class Animations {
|
||||
continue;
|
||||
}
|
||||
|
||||
let animation = running[prop];
|
||||
if (animation) {
|
||||
animation.cancel();
|
||||
}
|
||||
running[prop] = animation = new Animation(cfg, target, prop, value);
|
||||
animations.push(animation);
|
||||
}
|
||||
|
||||
@@ -91,9 +91,10 @@ class Animator {
|
||||
|
||||
if (draw) {
|
||||
chart.draw();
|
||||
if (chart.options.animation.debug) {
|
||||
drawFPS(chart, items.length, date, me._lastDate);
|
||||
}
|
||||
}
|
||||
|
||||
if (chart.options.animation.debug) {
|
||||
drawFPS(chart, items.length, date, me._lastDate);
|
||||
}
|
||||
|
||||
me._notify(chart, anims, date, 'progress');
|
||||
|
||||
@@ -594,13 +594,14 @@ class Chart {
|
||||
*/
|
||||
updateDatasets(mode) {
|
||||
const me = this;
|
||||
const isFunction = typeof mode === 'function';
|
||||
|
||||
if (plugins.notify(me, 'beforeDatasetsUpdate') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
|
||||
me.updateDataset(i, mode);
|
||||
me.updateDataset(i, isFunction ? mode({datesetIndex: i}) : mode);
|
||||
}
|
||||
|
||||
plugins.notify(me, 'afterDatasetsUpdate');
|
||||
@@ -838,6 +839,30 @@ class Chart {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_updateDatasetVisibility(datasetIndex, visible) {
|
||||
const me = this;
|
||||
const mode = visible ? 'show' : 'hide';
|
||||
const meta = me.getDatasetMeta(datasetIndex);
|
||||
const anims = meta.controller._resolveAnimations(undefined, mode);
|
||||
me.setDatasetVisibility(datasetIndex, visible);
|
||||
|
||||
// Animate visible state, so hide animation can be seen. This could be handled better if update / updateDataset returned a Promise.
|
||||
anims.update(meta, {visible});
|
||||
|
||||
me.update((ctx) => ctx.datasetIndex === datasetIndex ? mode : undefined);
|
||||
}
|
||||
|
||||
hide(datasetIndex) {
|
||||
this._updateDatasetVisibility(datasetIndex, false);
|
||||
}
|
||||
|
||||
show(datasetIndex) {
|
||||
this._updateDatasetVisibility(datasetIndex, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
||||
@@ -922,11 +922,8 @@ class DatasetController {
|
||||
const chartAnim = resolve([chart.options.animation], context, index, info);
|
||||
let config = helpers.mergeIf({}, [datasetAnim, chartAnim]);
|
||||
|
||||
if (active && config.active) {
|
||||
config = helpers.extend({}, config, config.active);
|
||||
}
|
||||
if (mode === 'resize' && config.resize) {
|
||||
config = helpers.extend({}, config, config.resize);
|
||||
if (config[mode]) {
|
||||
config = helpers.extend({}, config, config[mode]);
|
||||
}
|
||||
|
||||
const animations = new Animations(chart, config);
|
||||
@@ -957,6 +954,9 @@ class DatasetController {
|
||||
* @private
|
||||
*/
|
||||
_includeOptions(mode, sharedOptions) {
|
||||
if (mode === 'hide' || mode === 'show') {
|
||||
return true;
|
||||
}
|
||||
return mode !== 'resize' && !sharedOptions;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,13 +20,13 @@ defaults._set('legend', {
|
||||
onClick: function(e, legendItem) {
|
||||
var index = legendItem.datasetIndex;
|
||||
var ci = this.chart;
|
||||
var meta = ci.getDatasetMeta(index);
|
||||
|
||||
// See controller.isDatasetVisible comment
|
||||
meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null;
|
||||
|
||||
// We hid a dataset ... rerender the chart
|
||||
ci.update();
|
||||
if (ci.isDatasetVisible(index)) {
|
||||
ci.hide(index);
|
||||
legendItem.hidden = true;
|
||||
} else {
|
||||
ci.show(index);
|
||||
legendItem.hidden = false;
|
||||
}
|
||||
},
|
||||
|
||||
onHover: null,
|
||||
|
||||
Reference in New Issue
Block a user