I'm working with Highcharts spider web chart at the moment and wanted to see if I can do the following
How do I zoom polar charts? (or is it possible?)
How do I put background-color in each of the segment?
How do I zoom polar charts? (or is it possible?)
If you want a zoom like zoomType then no. zoomType is disabled for polar charts by highcharts-more.js. From source:
// Disable certain features on angular and polar axes
chart.inverted = false;
chartOptions.chart.zoomType = null;
How do I put background-color in each of the segment?
You can use math and Chart.renderer to create and fill paths to color the background of the segments. For example you might do it like this:
var colors = [ "pink", "yellow", "blue", "red", "green", "cyan", "teal", "indigo" ];
var parts = 6;
for(var i = 0; i < parts; i++) {
centerX = chart.plotLeft + chart.yAxis[0].center[0];
centerY = chart.plotTop + chart.yAxis[0].center[1];
axisLength = chart.yAxis[0].height;
angleOffset = -Math.PI/2;
angleSegment = Math.PI/(parts/2);
firstPointX = centerX + (axisLength * Math.cos(angleOffset + (angleSegment * i)));
firstPointY = centerY + (axisLength * Math.sin(angleOffset + (angleSegment * i)));
secondPointX = centerX + (axisLength * Math.cos(angleOffset + (angleSegment * (i+1))));
secondPointY = centerY + (axisLength * Math.sin(angleOffset + (angleSegment * (i+1))));
chart.renderer.path([
'M', centerX, centerY,
'L', firstPointX, firstPointY,
'L', secondPointX, secondPointY,
'Z'
]).attr({
fill: colors[i % colors.length],
'stroke-width': 1,
'opacity': 1
}).add();
}
As seen in this JSFiddle demonstration. You just have to match number of categories with the parts variable.
Related
I'm looking to have a transform on a KonvaJS line. I have all of it working and it scales the object, however I'd like it to adjust the points of the line instead of setting the scale property when I grab a resize handle. Would also consider doing this in a path as well.
So the goal is that my objects scaleX and scaleY is always 1 and it's just the points that are scaling out.
Is this at all possible?
You just need to apply the scale to points property:
shape.on('transformend', () => {
const oldPoints = shape.points();
const newPoints = [];
for(var i = 0; i< oldPoints.length / 2; i++) {
const point = {
x: oldPoints[i * 2] * shape.scaleX(),
y: oldPoints[i * 2 + 1] * shape.scaleY(),
}
newPoints.push(point.x, point.y);
}
shape.points(newPoints);
shape.scaleX(1);
shape.scaleY(1);
layer.draw();
})
Demo: https://jsbin.com/vuhakuvoxa/1/edit?html,js,output
I need to make rounded-perpendicular lines with react-konva, is it achievable using existing APIs? If yes, how?
I used bezier API for Line class, it works great. Somehow now I need to revamp the bezier curve to be rounded-perpendicular lines.
Sth like this:
There are many ways you can implement it. You can use tension to create curved lines or use lineCap property.
But to have more control it is better to create a custom shape.
const RADIUS = 20;
const Line = ({ points }) => {
return (
<Shape
points={points}
sceneFunc={(context, shape) => {
const width = points[1].x - points[0].x;
const height = points[1].y - points[0].y;
const dir = Math.sign(height);
const radius = Math.min(RADIUS, Math.abs(height / 2), Math.abs(width / 2));
context.beginPath();
context.moveTo(points[0].x, points[0].y);
context.lineTo(points[0].x + width / 2 - RADIUS, points[0].y);
context.quadraticCurveTo(
points[0].x + width / 2,
points[0].y,
points[0].x + width / 2,
points[0].y + dir * radius
);
context.lineTo(points[0].x + width / 2, points[1].y - dir * radius);
context.quadraticCurveTo(
points[0].x + width / 2,
points[1].y,
points[0].x + width / 2 + radius,
points[1].y
);
context.lineTo(points[1].x, points[1].y);
context.fillStrokeShape(shape);
}}
stroke="black"
strokeWidth={2}
/>
);
};
Demo: https://codesandbox.io/s/757nw05p6
Still in the process of improving my competence about D3, I got stuck with a problem where I'm trying to plot a zoomable curve in a SVG element with margins (so that I need a clipPath rect to avoid that plot invades margins when zoomed) but the clipPath margins cut the display of d3.symbols off the plot.
This is the relevant code for the plot
var margin = {top: 20, right: 60, bottom: 30, left: 30},
w = 960 - margin.left - margin.right,
h = 500 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", w)
.attr("height", h);
// The curve I want to plot: y=x^2
var my_curve = d3.range(10).map(function(d) { var my_y = d * d; return { "x" : d, "y" : my_y }; });
var x_range_min = d3.min(my_curve, function(d) { return d.x; });
var x_range_max = d3.max(my_curve, function(d) { return d.x; });
var y_range_min = d3.min(my_curve, function(d) { return d.y; });
var y_range_max = d3.max(my_curve, function(d) { return d.y; });
var xScale = d3.scaleLinear().domain([x_range_min, x_range_max]).range([0, w]);
var yScale = d3.scaleLinear().domain([y_range_max, y_range_min]).range([0, h]);
var xAxis = d3.axisBottom().scale(xScale);
var yAxis = d3.axisLeft().scale(yScale);
// symbols
svg.selectAll(".my_pts").data(my_curve).enter().append("path")
.attr("class", "my_pts")
.attr("d", d3.symbol().type(d3.symbolTriangle).size(200))
.attr("transform", function(d) { return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")"; })
// with this zoomed line does not enter margin area
.attr("clip-path", "url(#clip)");
...as you can see only part of the triangle symbol is depicted, I guess because the path is drawn at 0,0 and cut by the clipPath before the translation can be performed.
I have also posted this fiddle https://jsfiddle.net/fabio_p/988c1sjv/
where you can find a more complete version of the code, with the brush & zoom function, so that you can see why the clipPath is needed (if you have never encountered the issue with margins before)
My question is the following: is there a workaround to this problem? I was hoping to find a way to directly draw the symbol in the right place without the need of a later translation (possibly with a "custom symbol type"), but it seems it goes beyond my current skills, since I was unable to produce any actual solution.
Any suggestion would be welcome. Thanks in advance.
Create a group with the clip path:
var clipping = svg.append("g")
.attr("clip-path", "url(#clip)");
And append both the line and the symbols to the group:
clipping.append("path")
.data([my_curve])
//etc...
Here is your updated fiddle: https://jsfiddle.net/cvtvrL2q/
I need some help for the handles in the navigator of highstock.
My set options do not work in my example:
http://jsfiddle.net/q1xpn6hL/
Please take a look at line 5 to 9:
borderColor: '#666',
width: 10,
height: 35,
borderRadius: 2,
borderWidth: 0.5
thanks a lot!
Those options are not supported. You would have to extend Highcharts to enable them. It is possible to override function that handles creating of handles.
Example: http://jsfiddle.net/v4vhy01j/
(function (H) {
H.wrap(H.Scroller.prototype, 'drawHandle', function (proceed, x, index) {
var scroller = this,
chart = scroller.chart,
renderer = chart.renderer,
elementsToDestroy = scroller.elementsToDestroy,
handles = scroller.handles,
handlesOptions = scroller.navigatorOptions.handles,
borderWidth = H.pick(handlesOptions.borderWidth, 1),
borderRadius = H.pick(handlesOptions.borderRadius, 0),
width = H.pick(handlesOptions.width, 9),
height = H.pick(handlesOptions.height, 16),
attr = {
fill: handlesOptions.backgroundColor,
stroke: handlesOptions.borderColor,
'stroke-width': borderWidth
},
tempElem;
// create the elements
if (!scroller.rendered) {
// the group
handles[index] = renderer.g('navigator-handle-' + ['left', 'right'][index])
.css({
cursor: 'ew-resize'
})
.attr({
zIndex: 4 - index
}) // zIndex = 3 for right handle, 4 for left
.add();
// the rectangle
tempElem = renderer.rect(-5, 9 - height/2, width, height, borderRadius)
.attr(attr)
.add(handles[index]);
elementsToDestroy.push(tempElem);
// the rifles
tempElem = renderer.path([
'M', -1.5, 13 - height/2,
'L', -1.5, 5 + height/2,
'M',
0.5, 13 - height/2,
'L',
0.5, 5 + height/2]).attr(attr)
.add(handles[index]);
elementsToDestroy.push(tempElem);
}
// Place it
handles[index][chart.isResizing ? 'animate' : 'attr']({
translateX: scroller.scrollerLeft + scroller.scrollbarHeight + parseInt(x, 10),
translateY: scroller.top + scroller.height / 2 - 8
});
});
}(Highcharts));
The Highstock navigator does not support all those options. Here's what the documentation says:
handles: Object
Options for the handles for dragging the zoomed area. Available options are backgroundColor (defaults to #ebe7e8) and borderColor (defaults to #b2b1b6).
I want to show the tooltip on the right side of the cursor.
I looked in the documentation/examples but I can't find a way to force the tooltips to stay on the right side of the cursor.
Can anyone tell me how to do it?
With tooltip positioner I only can set a default position.
Tooltip positioner is much more than just default position. The function arguments contain info about your point position & tooltip dimensions, using which it should be fairly simple to position it to the right.
Highchart/stock allows you to define your alternate positioner as follows
tooltip:{
positioner:function(boxWidth, boxHeight, point){
...
}
}
Note that you have three arguments (boxWidth, boxHeight, point) at your disposal, these seem to be sufficient for most of the use cases to calculate a desired tooltip position. boxWidth and boxHeight are the width and height that your tooltip will require, hence you can use them for edge cases to adjust your tooltip and prevent it from spilling out of the chart or even worse getting clipped.
The default tooltip positioner that comes with highstock is as follows (Source)
/**
* Place the tooltip in a chart without spilling over
* and not covering the point it self.
*/
getPosition: function (boxWidth, boxHeight, point) {
// Set up the variables
var chart = this.chart,
plotLeft = chart.plotLeft,
plotTop = chart.plotTop,
plotWidth = chart.plotWidth,
plotHeight = chart.plotHeight,
distance = pick(this.options.distance, 12), // You can use a number directly here, as you may not be able to use pick, as its an internal highchart function
pointX = point.plotX,
pointY = point.plotY,
x = pointX + plotLeft + (chart.inverted ? distance : -boxWidth - distance),
y = pointY - boxHeight + plotTop + 15, // 15 means the point is 15 pixels up from the bottom of the tooltip
alignedRight;
// It is too far to the left, adjust it
if (x < 7) {
x = plotLeft + pointX + distance;
}
// Test to see if the tooltip is too far to the right,
// if it is, move it back to be inside and then up to not cover the point.
if ((x + boxWidth) > (plotLeft + plotWidth)) {
x -= (x + boxWidth) - (plotLeft + plotWidth);
y = pointY - boxHeight + plotTop - distance;
alignedRight = true;
}
// If it is now above the plot area, align it to the top of the plot area
if (y < plotTop + 5) {
y = plotTop + 5;
// If the tooltip is still covering the point, move it below instead
if (alignedRight && pointY >= y && pointY <= (y + boxHeight)) {
y = pointY + plotTop + distance; // below
}
}
// Now if the tooltip is below the chart, move it up. It's better to cover the
// point than to disappear outside the chart. #834.
if (y + boxHeight > plotTop + plotHeight) {
y = mathMax(plotTop, plotTop + plotHeight - boxHeight - distance); // below
}
return {x: x, y: y};
}
With all the above information, I think you have sufficient tools to implement your requirement by simply modifying the function to make float to right instead of the default left.
I will go ahead and give you the simplest implementation of positioning tooltip to right, you should be able to implement the edge cases based on the aftermentioned default tooltip positioner's code
tooltip: {
positioner: function(boxWidth, boxHeight, point) {
return {x:point.plotX + 20,y:point.plotY};
}
}
Read more # Customizing Highcharts - Tooltip positioning
The better solution to get your tooltip always on the right side of the cursor is the following:
function (labelWidth, labelHeight, point) {
return {
x: point.plotX + labelWidth / 2 + 20,
y: point.plotY + labelHeight / 2
};
}