I'm planning to create a dashboard for my web application, I have some data to represent graphically so to show that planning to use you Activity Gauge (http://www.highcharts.com/demo/gauge-activity) and other charts.
But as per our requirement we need to create these graphs with gradient effect with gradually fill effect with two or more than two colors, see the attached dashboard mockup for better understanding our thought. I have done our best effort to make with Highchart Linear gradients and Radial gradients but logically it seems not possible.
I have done some changes as per and I am able to make design like - http://jsfiddle.net/td2v4u4z/29/ but I lost gradually effect on this.
here is my original graph - http://jsfiddle.net/td2v4u4z/26/
I want to make this exactly like this -
Also, is this possible to place label inside the circle as per the below image -
My sample graph is - http://jsfiddle.net/qambxkmo/13/
Please let us know how can we achieve these design with Highchart.
Thanks in advance!
I am not sure what do you mean by 'gradually fill effect', but I think that you may wonder about an initial animation on your chart.
You can change the function responsible for making your gradient a little bit to achieve the chart with an initial animation. I am sending you this chart below:
http://jsfiddle.net/td2v4u4z/31/
If you think that is a good idea to have a functionality of simple adding similar gradient to your gauge, I think that you can ask for this feature on Highcharts uservoice. The best ideas (with biggest number of votes) gets to be implemented in future Highcharts versions.
https://highcharts.uservoice.com/
In case of the second question you have asked, I think that you should be able to add custom function for rotating your dataLabels, so they will be inside your donut chart:
rotate = function() {
$.each(options.series, function(i, p) {
angle1 = 0;
angle2 = 0;
angle3 = 0;
allY = 0;
$.each(p.data, function(i, p) {
allY += p.y;
});
$.each(p.data, function(i, p) {
p.dataLabels = p.dataLabels || {};
angle2 = angle1 + p.y * 360 / (allY);
angle3 = angle2 - p.y * 360 / (2 * allY);
if (angle3 >= 180) {
p.dataLabels.rotation = 0 + angle3;
} else {
p.dataLabels.rotation = 0 + angle3;
}
angle1 = angle2;
});
});
};
I have made simple example how it can work: http://jsfiddle.net/j7as86gh/31/
Related
I am using d3.js v6 to create a 3D graph of the below 2D chart representation. This circle has multiple squares in it and each square has been assigned a color based on the value. The bigger the value, more darker the square.
Now I want to convert this in 3D shape where only the height of a particular square increases when the value gets high, so the result would be somehow similar to the image below. The base would be circular but the height of each value would go up based on the value
I am trying to achieve this in angular, if anyone could please help me out. Here is the Stackblitz Link
I made the one as you requested.
source code on github
here's working demo: https://stackoverflow-angular-3d-chart.surge.sh/
This involved several intricate steps.
I couldn't go any deeper from this answer because every part that I mentioned here could be hours worth tutorial. These are what I've felt interesting when I was working on it.
Used Stacks
EDIT: the stackblitz code is now outdated. I've used the most recent version for each package.
Three.js r143
D3.js v7.6.1
Angular.js v14
Getting Circle Grid
experiment note on ObservableHQ: https://observablehq.com/#rabelais/circle-inside-grids
First I've experimented on SVG with D3.js to get proper circle grid.
It seemed daunting but turned out very simple. I've slightly modified Midpoint circle algorithm to fill box grids in circular shape. It is little different from filling grids in 3d space; 2d space has top left corner as beginning of everything. In 3d space, everything starts from center.
const midPointX = gridWidth / 2;
const midPointY = gridHeight / 2;
const { midPointX, midPointY, radius } = config;
const getCollision = ({ x, y }) => {
return (midPointX - x) ** 2 + (midPointY - y) ** 2 - radius ** 2 > 0;
}
Calculating Gaps
d3's scale band supports automatic calculation of gaps and content size in responsive environment.
const scaleBandX = d3
.scaleBand()
.domain(d3.range(0, config.gridWidth))
.range([config.margin, config.svgWidth - config.margin * 2])
.paddingInner(0.2);
const scaleBandY = d3
.scaleBand()
.domain(d3.range(0, config.gridHeight))
.range([config.margin, config.svgHeight - config.margin * 2])
.paddingInner(0.2);
scaleBandX.bandwidth(); // width of box in 2d space
scaleBandY.bandwidth(); // height of box in 2d space
scaleBandX(boxIndex); // x position of box in 2d space with gap
scaleBandY(boxIndex); // y position of box in 2d space with gap
as D3 assumes vector calculation as normal, it was pretty easy to apply the very same method in 3D.
Expressing on 3D space
I've used Three.js to express everything in 3D. The app is running on Angular per request but it does not matter which frontend framework is used.
Everything about expressing 2d bar chart on 3d is very trivial. However, the dimension is different from 2d; the positions have to be swapped.
// code to make a single bar mesh
makeBar(d: typeof gridData[0]) {
// length and height is swapped. because camera is looking from 90 degree angle by default.
const geo = new T.BoxGeometry(d.w, d.l, d.h, 32, 32);
const mat = new T.MeshPhysicalMaterial({ color: 'red' });
const mesh = new T.Mesh(geo, mat);
mesh.position.x = d.x;
// z and y is also swapped. because of the same reason.
mesh.position.z = d.y;
mesh.position.y = d.z;
return mesh;
}
then each element is assigned as 3d Group, to make them centered altogether.
EDIT: color scheme was missing. it is now added.
I am working on a plugin to allow "natural looking" signatures to be drawn using mouse or touch. When confirmed by the user, the result will be a stored SVG that can then be displayed in place of the "Click to sign" button.
The attached JSFiddle http://jsfiddle.net/TrueBlueAussie/67haj4nt/3/ shows a testbed for what I am trying to do. The SVG generated image should look close to the original canvas paths.
The first div contains a canvas, in which I draw some multiple-segment lines (e.g. paths). Using quadraticCurveTo, and a midpoint for the control point, I draw the lines with smooth curves. This works just fine.
The key part of the curved line drawing is:
$.each(lines, function () {
if (this.length > 0) {
var lastPoint = this[0];
ctx.moveTo(lastPoint[0], lastPoint[1]);
for (var i = 1; i < this.length; i++) {
var point = this[i];
var midPoint = [(lastPoint[0] + point[0]) / 2, (lastPoint[1] + point[1]) / 2];
ctx.quadraticCurveTo(lastPoint[0], lastPoint[1], midPoint[0], midPoint[1]);
lastPoint = point;
}
// Draw the last line straight
ctx.lineTo(lastPoint[0], lastPoint[1]);
}
});
I have tried multiple options for SVG generation of the same output, but I am stumped on how to convert the same sets of points to equivalent curved lines. Quadratic Beziers require "proper" control points, but I would prefer to use the far simpler mid-points if possible.
Any ideas? Is this possible or will I have to convert both to use Beziers with calculated control point(s). Is there a simple way to calculate control points that will do the same job?
jQuery or raw JavaScript solutions are fine, but you need to demonstrate in the JSFiddle provided :)
It's just a bug in your code. You are not updating lastPoint in your SVG version.
http://jsfiddle.net/67haj4nt/4/
And if you update the SVG version to match the canvas version, you get identical curves.
http://jsfiddle.net/67haj4nt/5/
I'm trying to draw a grid of rectangles on top of the map tiles using the Javascript API and highlight (switch fillColor for) whichever rectangle is currently under the mouse pointer. I would expect such a small change to be effective almost immediately.
However the speed at which changes take place is unbearable for something like this, as changes seem to trigger with a delay of maybe 100ms or so. This applies even if I save a reference to one of the rectangles on a 2x2 grid and then change its color from the console. So this seems unlikely (but still possible) to be a performance issue but rather feels like the Maps simply won't refresh often enough.
Is there maybe a way for me to tell the Maps to redraw a region immediately, or should I use some other way of drawing which would be more performant? I currently have a workaround of using a floating div as the highlight, but it feels a bit wrong and comes with other issues to hack around.
rect = new nokia.maps.map.Rectangle(boundingBox, opts)
...
// slow, but not a deal breaker
map.objects.add(rect)
...
// too slow to happen on every mouseenter/mouseleave event
rect.set('fillColor', '000000')
I'm using the 2.5 version of the Javascript API and I'm targeting mostly Chrome.
You could try map.update(-1, true); to force a redraw the map.
Alternatively, one possible performance improvement (which has several caveats) would be to use an overlay for the grid and only one rectangle. This could be of use if you are trying to highlight the current square region of a map tile as served from the TMS server.
You could add a 256x256 grid (or 128x128 or 64x64 etc) using the code in the question here, and then merely move one rectangle over the map to show the current highlight:
For a given zoom and coordinate, the current tile CoordinateZoomToXY is:
var longitude = coord.longitude,
latitude = coord.latitude,
tilesPerRow = Math.pow(2, zoom),
column,
row;
longitude /= 360;
longitude += 0.5;
latitude = 0.5 - ((Math.log(Math.tan((Math.PI / 4) + (latitude * Math.PI / 360))) / Math.PI) / 2.0);
column = Math.floor(longitude * tilesPerRow);
row = Math.floor(latitude * tilesPerRow);
hence the reverse operation (XYZtoCoordinate) is:
var tilesPerRow = Math.pow(2, zoom),
longitude = column / tilesPerRow * 360.0 - 180.0,
lat_rad = Math.atan(sinh(Math.PI * (1 - 2 * row / tilesPerRow))),
latitude = lat_rad * 180.0 / Math.PI;
and the current tile is:
nokia.maps.geo.BoundingBox.coverAll([
XYZtoCoordinate(zoom, column , row),
XYZtoCoordinate(zoom, column + 1, row + 1)]));
If you added this to the listener and just moved one rectangle, it may help as you would only need to update one map object each time.
I am simulating a page turn effect in html5 canvas.
On each page I am drawing lines to simulate lined paper.
These lines are drawn as the page is turned and in order to give natural perspective I am drawing them using quadratic curves based of several factors (page turn progress, closeness to the center of the page etc.. etc...)
The effect is very natural and looks great but I am looking for ways to optimize this.
Currently I am drawing every line twice, once for the actual line and once for a tiny highlight 1px below this line. I am doing this like so:
// render lines (shadows)
self.context.lineWidth = 0.35;
var midpage = (self.PAGE_HEIGHT)/2;
self.context.strokeStyle = 'rgba(0,0,0,1)';
self.context.beginPath();
for(i=3; i < 21; i++){
var lineX = (self.PAGE_HEIGHT/22)*i;
var curveX = (midpage - lineX) / (self.PAGE_HEIGHT);
self.context.moveTo(foldX, lineX);
self.context.quadraticCurveTo(foldX, lineX + ((-verticalOutdent*4) * curveX), foldX - foldWidth - Math.abs(offset.x), lineX + ((-verticalOutdent*2) * curveX));
}
self.context.stroke();
// render lines (highlights)
self.context.strokeStyle = 'rgba(255,255,255,0.5)';
self.context.beginPath();
for(i=3; i < 21; i++){
var lineX = (self.PAGE_HEIGHT/22)*i;
var curveX = (midpage - lineX) / (self.PAGE_HEIGHT);
self.context.moveTo(foldX, lineX+2);
self.context.quadraticCurveTo(foldX, lineX + ((-verticalOutdent*4) * curveX) + 1, foldX - foldWidth - Math.abs(offset.x), lineX + ((-verticalOutdent*2) * curveX) + 1);
}
self.context.stroke();
As you can see I am opening a path, looping through each line, then drawing the path. Then I repeat the whole process for the 'highlight' lines.
Is there any way to combine both of these operations into a single loop without drawing each line individually within the loop which would actually be far more expensive?
This is a micro-optimization, I am well aware of this. However this project is a personal exercise for me in order to learn html5 canvas performance best practices/optimizations.
Thanks in advance for any suggestions/comments
Paths can be stroked as many times as you like, they're not cleared when you call .stroke(), so:
create your path (as above)
.stroke() it
translate the context
change the colours
.stroke() it again
EDIT tried this myself - it didn't work - the second copy of the path didn't notice the translation of the coordinate space :(
It apparently would work if the path was created using new Path() as documented in the (draft) specification instead of the "current default path" but that doesn't appear to be supported in Chrome yet.
I'm having a bit of trouble playing around with KineticJS.
As you can see from my fiddle, I am able to access the Shape object (box) inside of my drop event, to get the x,y coordinates, and I'm performing math on them to get the new coordinates I want to 'snap' the shape to, but I can't figure out how on earth to set the position and redraw the box.
The docs are sparse, at best:
http://www.kineticjs.com/api-docs.php (See Shape.setPosition( x, y))
Has anyone here messed with this library yet?
EDIT: My now working fiddle: http://jsfiddle.net/Programmer/m4MZk/
check out the "Animals on the Beach" lab which is an example of snapping shape objects into place based on their coordinates:
http://www.html5canvastutorials.com/labs/html5-canvas-animals-on-the-beach-game-with-kineticjs/
This worked for me and has now been extended into a full circuit diagram drawing app.
It's based on the Animals on the Beach code but a bit simpler.
http://reviseomatic.org/help/e-tools/Diagram%20Designer%20Circuits.php
wire1vImg.on('dragend', function() {
var point = wire1vImg.getPosition();
var newX = Math.round(point.x / 15) * 15;
var newY = Math.round(point.y / 15) * 15;
wire1vImg.setPosition(newX, newY);
stage.draw();
});