I'm trying hard to make dashboard with Blazor. But I'm new to js and css so feel some hard.
Currently I'm implementing chart components and I'd like to add vertical line which is moved automatically like below image.
I searched a lot of pages, however I didn't even know what to do.
I'm not asking you to write code, but it would be nice to tell me about sites, methods or libraries that I can refer to.
Situation
I'm using Blazorise Chart .net core 5.0
My tries
Create a data set to be expressed like a vertical line, set a timer, and move the vertical line data once per second.(The StateHasChange() function should be continues to be called.
Draw vertical line using css(But I couldn't know how to move it)
Thank you for reading.
Since Blazorise uses ChartJs internally to draw the charts, you might be able to use the ChartJs annotation plugin. You might need to leverage a JavaScript Interop method to inject the plugin options.
Alternatively you can simply draw the line yourself, as the Chart renders in a <canvas /> element:
Heres a piece of code i use (Personally I use ChartJs.Blazor, and my chart is a Bar chart, so you will need to adapt the code to fit your own needs and library)
annotationConfig: function (canvasID, indexToDrawAt) {
var originalLineDraw = Chart.controllers.bar.prototype.draw;
Chart.helpers.extend(Chart.controllers.bar.prototype, {
draw: function () {
originalLineDraw.apply(this, arguments);
var chart = this.chart;
var ctx = chart.chart.ctx;
var index = chart.config.options.lineAtIndex;
if (index) {
var xaxis = chart.scales['x-axis-0'];
var yaxis = chart.scales['y-axis-0'];
var x1 = xaxis.left;
var y1 = yaxis.getPixelForValue(index);
var x2 = xaxis.right;
var y2 = yaxis.getPixelForValue(index);
ctx.save();
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.strokeStyle = '#3C3E4F';
ctx.lineTo(x2, y2);
ctx.stroke();
ctx.restore();
}
}
});
let chart = window.ChartJsInterop.BlazorCharts.get(canvasID);
chart.options.lineAtIndex = indexToDrawAt;
chart.update();
}
This code adds a option to the chart called lineAtIndex, which when set, draws a line across the canvas at the provided index.
I use the method as such:
if (ShowAvgLine)
{
if (currentAverage != 0.0)
{
jSRuntime.InvokeVoidAsync("generalInterop.annotationConfig", _config.CanvasId, currentAverage);
}
}
If you get the above to work with your library, you might try your previous solution of making timed calls to the interop method to move it.
I don´t know Blazorise Chart, maybe there is also a built in solution.
If you want to solve it in a pretty hacky way, you could just create a <div> which has the same height as your chart. Then you can use the top and left CSS properties to set the div at the leftmost position over the chart (you may have to give the div a z-index and a position: absolute so that it appears over the chart). The positions should be saved in a C# variable. Now you can implement a function which gets called every second which increments the left value and calls the StateHasChanged() method.
This is a really hacky solution but it should work as expected. But you have to beware, that on different devices the chart can be displayed in a other way, so maybe the div won´t align anymore.
Related
I encapsulated d3 charts into function as suggested best practice from creator in blog Towards Reusable Charts. Is it possible to create optional functionalities on top of this chart, so calling specific function would trigger it, otherwise it would be omitted.
Working JSFiddle example (base working example from Rob Moore's blog)
In JS line 56 I added a function which I'd like to create and then conditionally call in line 67.
My current way of doing it, is creating a boolean and setting it to false and then calling function with argument true. Problem of doing it this way is that the code gets too many conditionals and edge cases.
P.S. this question is not meant to be a discussion how to correctly apply axis to the chart. This is just an example.
I think it's better to add additional functionalities after the chart is drawn
var runningChart = barChart().barPadding(2).fillColor('coral');
d3.select('#runningHistory')
.datum(milesRun)
.call(runningChart);
runningChart.x_axis(); // additional functionality
So that the original chart container can be saved in a variable and it can be used to append other functionalities. For example
function barChart() {
var charContainer;
function chart(selection){
charContainer = d3.select(this).append('svg')
.attr('height', height)
.attr('width', width);
}
chart.x_axis = function() {
// Add scales to axis
var x_axis = d3.axisBottom()
.scale(widthScale);
charContainer.append('g').call(x_axis);
return chart;
}
}
If there is any need to add additional functionality before the chart is drawn, then all the functionalities can be saved in a Javascript object and drawn like in this example. https://jsfiddle.net/10f7hdae/
Ive been trying to make persistent tooltip by drawing a layer over the graph and track the tooltip based on mouse position. Unfortunately I kept getting an error on line xScaleFunc.invert(d3.mouse(this)[0]). Then i came across this custom invert code for scaleband scales. From here : https://bl.ocks.org/shimizu/808e0f5cadb6a63f28bb00082dc8fe3f .
The question is i'm not understanding whats going on here.I'm kinda new to D3 , any help would be much appreciated.
xScaleFunc.invert = (function(){
var domain = xScaleFunc.domain()
var range = xScaleFunc.range()
var scale = d3.scaleQuantize().domain(range).range(domain)
return function(x){
return scale(x)
}
})()
I have two separate datasets in Chart.JS, one represents a selection of historical data, the other the predictions based on that data. Here is what the chart currently looks like. Historical data is black, prediction is blue:
I'd like to maintain the two as separate datasets, but connect them so the chart displays as a single line. In a previous version I accomplished this by adding a datapoint to the prediction dataset that is an exact duplicate of the final datapoint in the historical set, but this creates an inaccurate redundancy I want to avoid.
Unless there's some chart.js setting I've been unable to find I'm worried I might have to register a plugin to do this, which seems like it would become needlessly clunk. Thanks.
Managed to solve the problem, posting to help anyone with a similar problem.
I ended up creating a chart.js plugin that draws a line finds the X and Y coordinates of the last historical point and then draws a line between that and the X and Y coordinate of the first prediction point. After beating my head against it for a while I can't believe it was so simple.
I'm posting the code down below - this is a react application so it may not fit cleanly into every use case. The JSFiddle isn't setup to work, but you can see how to grab the point data which was the hardest part for me!
//Grab the chart meta data and identify the x and y coordinates of the
appropriate points
var meta = chartInstance.getDatasetMeta(0);
var predictionMeta = chartInstance.getDatasetMeta(1);
var chartFirstPointX = meta.data[this.props.actualData.length - 1]._model.x;
var lastActualY = meta.data[this.props.actualData.length - 1]._model.y;
var predictionFirstPointX = predictionMeta.data[0]._model.x;
var predictionFirstPointY = predictionMeta.data[0]._model.y;
Chart.pluginService.register({
id: 'forecastLine',
beforeDraw: function(chartInstance) {
var ctx = chartInstance.chart.ctx,
yTop = chartInstance.chartArea.top,
yBottom = chartInstance.chartArea.bottom,
firstPredictionX = predictionFirstPointX,
firstPredictionY = chartInstance.config.data.datasets[1].data[0].y;
ctx.fillStyle = '#FAFAFA';
ctx.fillRect(0, 0, chartInstance.chart.width, chartInstance.chart.height);
//draw a vertical line separating the two datasets, then draw a line
connecting them
ctx.save();
ctx.beginPath();
ctx.moveTo(chartFirstPointX, yBottom);
ctx.lineTo(chartFirstPointX, yTop);
ctx.moveTo(chartFirstPointX, lastActualY);
ctx.lineTo(predictionFirstPointX, predictionFirstPointY);
ctx.lineWidth = 2;
ctx.strokeStyle = '#68D1FE';
ctx.stroke();
ctx.restore();
I'm working on a modification to vis.js's Graph3d to do a filled line graph, like this:
The hard part - unsurprisingly - is working out the rendering order for the polygons. I think I can do this by checking whether a ray from the viewer to a given line B crosses line A:
In this example, since line A is "in the way" of line B, we should draw line A first. I'll use a snippet of code from How do you detect where two line segments intersect? to check whether the lines cross.
However, I haven't figured how to find the position of the user's view. I kind of assumed this would be the camera object, so wrote a little bit of debug code to draw the camera on the graph:
var camera = this._convert3Dto2D(this.camera.getCameraLocation());
ctx.strokeStyle = Math.random()>0.5 ? 'ff0000' : '00ff00';
ctx.beginPath();
ctx.moveTo(camera.x, camera.y);
ctx.lineTo(camera.x, camera.y+5);
ctx.stroke();
In fact, the camera co-ordinates as measured by this are always at 0,0,0 on the graph (which would be the far top right on the above screengrab). What I need, I think, is effectively the bottom of the screen.
How can I find this? Or is there a better way to achieve what I'm trying to do?
I don't know if this is still an active issue, but FWIW, Graph3D has internal handling of the sort ordering.
All graph points are sorted with respect to the viewpoint, using a representative coordinate called point.bottom. The rendering is then done using this ordering, with the most distant elements drawn first. This works fine as long as none of the elements intersect; in that case, you can expect artefacts.
Basically, all you need to do, is define point.bottom per graph polygon, and Graph3D will then pick it up from there.
If you are still interested in working on this:
This happens in Graph3d.js, method Graph3d.prototype._calcTranslations(). For an example, have a look at how the Grid and Surface graph elements are initialized in Graph3d.prototype._getDataPoints(). The relevant code is:
obj = {};
obj.point = point3d;
obj.trans = undefined;
obj.screen = undefined;
obj.bottom = new Point3d(x, y, this.zRange.min);
I'm using highcharts for my project. I've used multiple axes for drawing the chart. What I want is there should be a straight line on fixed x-axis even though with different values.
I want to have something like plotLines which will be straight and fixed on the x-axis but still shows different values I put it.
I would create separate series to achieve that. I think it's simpler solution than playing around with renderer. Demo: http://jsfiddle.net/6wo5mzo4/
(function(H) {
var seriesTypes = H.seriesTypes;
seriesTypes.fakeLine = Highcharts.extendClass(seriesTypes.line);
seriesTypes.fakeLine.prototype.translate = function() {
var s = this;
seriesTypes.line.prototype.translate.call(s);
H.each(s.points, function(point) {
point.plotY = s.chart.plotWidth / 2; // display in the middle - note: plotWidth, not plotHeight, because chart is inverted
});
}
})(Highcharts);
However, I still have doubts how useful such chart will be. I would also suggest to review the requirements.