ExtJs dynamically set axes maximum value - extjs

I would like to dynamically change the axis maximum limit of an ExtJS 4.x chart.
I tried using a listener beforerefresh but this has no effect:
var chart = Ext.create('Ext.chart.Chart', {
...
listeners: {
beforerefresh(me, eOpts)
{
// set y-axes to max(raw_value)
console.log('before refresh');
// set maximum axis value of Y axis to 0.1
me.axes.getAt(1).maximum = 0.1;
}
},
The code is reached, but using me.axes does not seem to have any effect at all.
What is the correct way to do it?
Thanks

Related

Why does 'offset' exist in React Native Panresponder?

TL;DR: I am using the Panresponder code from the React Native docs, and need help understanding why the 'offset' value is used, as opposed to just using the animated value.
Full Question:
The Scenario:
I am using a Panresponder in React Native to drag and drop objects around the screen. I am using standard code from the RN docs.
Basically, the draggable object has an animated position value. When you click the object, the offset on that animated value is set to the animated value, and the animated value is set to zero. As you drag, the animated value is incrementally set to the magnitude of how far it has been dragged in that gesture. When you release the object, the offset is added to the animated value, and the offset is then set to zero.
Example:
For example, if the object starts from position 0, then initially both the animated value and the offset are set to 0. If you drag the object by 100px, the animated value gradually increases from 0 to 100 as you drag. When you release, the zero offset is added to the animated value (so nothing happens). If you click the object again, the offset is set to 100, and the animated value is re-set to 0. If you drag the object another 50px, the animated value increases from 0 to 50. When you release the object, the 100 offset is added to the animated value, which becomes 150, and the offset is re-set to zero.
In this way, the animated value always holds the distance dragged in the current gesture, with the offset saving the position that the object was at before the current drag gesture started, and when you release the object, that saved offset value is tacked onto the animated value, so that when the object is at rest, the animated value contains the total distance that the object has been dragged by all gestures combined.
Code:
Here's the code I'm using to do this:
this.animatedValue.addListener((value) => this._value = value); // Make this._value hold the value of this.animatedValue (essentially extract the x and y values from the more complex animatedValue)
this.panResponder = PanResponder.create({
onPanResponderGrant: () => { // When user clicks to initiate drag
this.animatedValue.setOffset({ // Save 'distance dragged so far' in offset
x: this._value.x,
y: this._value.y,
})
this.animatedValue.setValue({ x: 0, y: 0}) // Set this.animatedValue to (0, 0) so that it will hold only 'distance so far in current gesture'
},
onPanResponderMove: Animated.event([ // As object is dragged, continually update animatedValue
null, { dx: this.animatedValue.x, dy: this.animatedValue.y}
]),
onPanResponderRelease: (e, gestureState) => { // On release, add offset to animatedValue and re-set offset to zero.
this.animatedValue.flattenOffset();
}
}
My Question:
This code works perfectly well. When I don't understand though, is why do we need the offset? Why do we need to re-set the animated value to zero on every new gesture, save its value in offset, and re-add that to the animated value after it's finished being dragged? When the object is released, it ends up just holding the total distance dragged, so why not just use the animated value and not use the offset? With my example above, why not just increment animated value to 100 when you drag it 100px, then when you click and drag it again, keep updating the animated value?
Possible Solution:
The only advantage I can think of to using the offset is that animatedValue will now allow you to keep track of the 'distance so far in current gesture', as opposed to just 'total distance so far over all gestures combined'. There might be a scenario in which you need the 'distance so far in current gesture' value, so I'm wondering if this is the only reason to ever use the offset, or is there a more fundamental reason I'm missing why we should use it all the time?
Any insight would be great.
Thanks!
Actually the logic isn't right in the example you used because it's just a partial example using flattenOffset that isn't meant to be used for standard drag/drop behaviour (see the bottom paragraph: https://animationbook.codedaily.io/flatten-offset/):
Because we reset our offset and our animated value in the onPanResponderGrant, the call to flattenOffset is unnecessary, here. However, in the case where we want to trigger an animation from the released location to another location, flattenOffset is required.
The whole point of the offset is that you don't need to keep track of the absolute position value in a separate variable. So you were right to doubt the need for the offset given that you where storing the absolute position in this._value.
At the beginning of a drag, the x/y values of the Animated.Value start from [0, 0], so the drag is relative to the starting position:
offset + [0, 0] = absolute position at the beginning of a drag
offset + [x, y] = absolute position at the end of the drag
For the next drag to start at the right position, you just need to add [x, y] to the offset, which is done by extractOffset():
this.panResponder = PanResponder.create({
// Allow dragging
onStartShouldSetPanResponder: (e, gesture) => true,
// Update position on move
onPanResponderMove: (e, gestureState)=> {
Animated.event([
null,
{dx: this.animatedValue.x, dy: this.animatedValue.y},
])(e, gestureState)
},
// Update offset once we're done moving
onPanResponderRelease: (e, gestureState)=> {
this.animatedValue.extractOffset();
}
});
Thanks to the offset, you don't need this._value anymore to get the proper drag behaviour.
Because it's better to have the entire animated value's state be self-contained, so you can pass its value to a transform. Of course maybe you don't want the "total distance travelled" in which case, well, don't use offsets, but if you do, using AnimatedValue's offset is the best solution.
Let me show you why by coding up an example of tracking the total distance travelled between touches without using the built-in offset:
this.offsetValue = {x: 0, y:0};
this.panResponder = PanResponder.create({
onPanResponderGrant: () => { // When user clicks to initiate drag
this.animatedValue.setValue({ x: 0, y: 0}) // Set this.animatedValue to (0, 0) so that it will hold only 'distance so far in current gesture'
},
onPanResponderMove: Animated.event([ // As object is dragged, continually update animatedValue
null, { dx: this.animatedValue.x, dy: this.animatedValue.y}
]),
onPanResponderRelease: (e, gestureState) => {
// Set the offset to the current position
this.offsetValue = {x: gestureState.dx, y: gestureState.dy}
// Reset our animatedvalue since the offset is now all good
this.animatedValue.setValue({ x: 0, y: 0})
}
}
This works, and it's less code you now have the raw value for the current touch in Animated.Value and if you want the total distance moved you can use this.offsetValue. Except... how do you apply it to get the total distance exactly? You might think you can do this:
<Animated.View
style={{
transform: [
{ translateX: this.offset.x + this.animatedValue.x },
{ translateY: this.offset.y + this.animatedValue.y },
],
}}
{...this.panResponder.panHandlers}
/>
But this will be an error because animatedValue.x isn't a number obviously. You could use ._value directly but then what's the point of using Animated? The entire idea is that you can pass a single Animated object to a transform property. So that's why you simply use the object's internal offset.

Highcharts conditionally disable marker on hover

New to highcharts - I've got a chart that I have markers disabled on series
plotOptions: {
series: {
marker: {
enabled: false
}
}
},
which is great for the drawing of the lines, but, when I hover over the chart the markers are there. this is good, however, I do not want the marker to be present if the y value on the xAxis is 0 (empty) but I do want them when the y value on the xAxis is greater than one.
I was able to do this before by just setting the y value to null, disabling hover for that series, but the nulls present on this series - drawn as a stacked areaspline graph causes the spline to get drawn improperly (no spline, jagged edges when using the connectNulls: true option in series.
So how do I, and/or can I, conditionally disable a marker on hover based on the y value on an xAxis?
I have been looking at wrapping highcharts prototypes, which I am already doing to control some crosshair behavior drawCrosshair(): https://www.highcharts.com/docs/extending-highcharts/extending-highcharts but I can't seem to find anything that controls the drawing of the markers at that level
A very static approach to this is simply addressing each point with Y-value of 0. Then you could disable the hover-marker for each of these. One such point would look like this (JSFiddle demo):
{
y:0,
marker:{
states:{
hover:{
enabled:false
}
}
}
}
And used in a series:
series: [{
marker: {
enabled: false
},
data: [3, {y:0, marker:{states:{hover:{enabled:false}}}}, 3, 5, 1, 2, 12]
}]
As you can see, it's not pretty, but might help those who need an ad-hoc solution.
A dynamic approach to this is intercepting the setState-function of the Point.
For example, wrapping it and preventing the handling if the y-value is 0:
(function (H) {
H.wrap(H.Point.prototype, 'setState', function (proceed, state, move) {
if(this.y === 0) return;
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
});
}(Highcharts));
See this JSFiddle demonstration of it in action.

angular-nvd3 change in multibar chart column width

I use angular-nvd3 library in my angular project.
I need to change column width in "multiBarChart". I tried to change on event "on-ready" but it doesn't work
onChartReady(scope, element) {
const api = scope.api;
const chart = scope.chart;
const svg = scope.svg;
var bars = svg.selectAll("rect.nv-bar");
bars.attr("width", "200");
}
You can use the below code snippet to change the width of the bar in multi bar chart.
groupSpacing property accepts a value between 0 and 1
A value of 0.1 will make the bars very wide. A value of 0.9 will make them narrow.
chart.groupSpacing(0.8); // Value must be between 0 & 1

angular-nvd3 content percentage in tooltip

I am looking into ways I can display the percent of a slice of pie compared to the current graph in the tooltip.
First I tried looking for something similar to chart.labelType: 'percent' but it looks like there is no such option.
What I am trying to do now is calculate the percentage inside chart.tooltip.contentGenerator according to documentation the function should be passed 5 arguments function (key, x, y, e, series) -> String however I am only receiving the first argument.
I am using angular 1.5.0, d3 3.5.16, nvd3 1.8.2, and angular-nvd3 1.0.5.
What is the best practice for displaying the percentage in the tooltip?
EDIT: You brought up a great point that I didn't account for, that the total (and thus the percentage portion of each pie segment) will change when you remove segments from the pie. Looked into how to account for this, and I discovered you can monitor the chart for a stateChange, and configure what happens when this event is dispatched.
So, what I've done is to update the total when that event is fired by filtering out whatever values are disabled:
chart: {
...,
dispatch: {
stateChange: function(e) {
total = _.reduce(_.filter($scope.data, function(value) {
return !value.disabled;
}), function(result, value, key) {
return result += parseFloat(value.y);
}, 0);
console.log(total);
}
},...
};
I've updated the example pie chart plunker with the new code, but everything else remained the same, except that I added in each key and the updated total into the tooltip so you can verify that it's working. But you'll still initialize the total value when the chart first loads:
var total = _.reduce($scope.data, function(result, value, key) {
return result += parseFloat(value.y);
}, 0);
...And use the chart.tooltip.contentGenerator method to customize the tooltip message, which you can set up to return something like this:
tooltip: {
contentGenerator: function (key, x, y, e, series) {
return '<h4>' + ((key.data.y / total) * 100).toFixed(2) + '% of total</h4>';
}
}
Hope that helps!

ext js 4 column chart bug? series remain visible when I hide them

Feeling I had not enough control over the chart if I had used a grouped column chart, I made my own version by just adding different series to the chart. After all the store, the number of series, their colors and such all need to be set dynamically and not hard coded. So basically this is what I have:
chart = Ext.create("Ext.chart.Chart", {
store: dataStore,
axes: dynamicAxes,
series: series
});
I leave out the not interesting stuff such as width, height of the chart etc.
now I have a method whichs returns a series object. This is added to the series array mentioned in the code above. The function has a "item" object parameter and also an idx param which is the index of the item object from the array it comes from, and a max param which is the size of the item array
the function returns something like this:
var w = (typeof (max) !== "undefined" && max !== null) ? this._getWidthByMax(max) : 30;
return {
type: "column",
axis = "left",
xField = "timestamp",
yField = item.id, // store field name equals the id of the item object
style = { stroke: colorCode, "stroke-width": (item.isDefault) ? 2 : 1, fill: colorCode },
width = w,
renderer = function (sprite, rec, attr, bix) {
var nx = idx * w;
return Ext.apply(attr, { translation: { x: nx} });
}
}
now this works fine for the number of columns I want to have. That can be one, two, three... up to seven currently.
However, if I want to hide a series, the following call doesn't work:
chart.series.getAt(idx).hideAll();
while it does work if I render my chart as a line chart.
is this a bug in Ext-js 4 or is it because of how I rendered the series for my column chart?
since nobody has replied to my question and I have found a solution in the meantime, I might as well answer my own question...
the problem occurred in Ext Js 4.0.7.
With version 4.1 RC 2 the hideAll behaved correctly.
So the solution, for anyone who would have the same problem, is to upgrade to 4.1 RC 2 or newer.

Resources