How to add custom background to Google Charts - javascript

Google have introduced a stock chart to their results which highlights "After hours":
I've had a hunt around the documentation and I can't see anything obvious that would suggest how to achieve a similar effect (even the line markers change colour). The closest I have found is this this jsfiddle (asgallant/apH2B/) which shows horizontal zoning -but doesn't alter the colour of the line.
Does anyone have any suggestions on how to achieve this effect?

I managed to achieve this effect by generating a div ontop of the chart (with an absolute position). Also made up some javascript to redraw the chart and add a Grey line in the selected interval.
Input function:
function updateShader() {
// gets the entered values from the fields
var start = Math.round(parseInt(document.getElementById('startPos').value))
var width = Math.round(parseInt(document.getElementById('interval').value))
// draws the chart again with the entered filters
drawBasic(start, width);
//converts the entered values to ISH pixels
start = Math.round(start * 5.125)
width = Math.round(width * 5.125)
// special case when start = end point, so it will be grey on a little on both sides
if (start == width) {
start = start + 69
} else {
start = start + 73
}
// changes the "cover-box" to fit the entered values
document.getElementById('cover').style.marginLeft = start + "px";
document.getElementById('cover').style.width = width - start + 73 + "px";
}
And the CSS for the cover box:
#cover {
position:absolute;
width:0px;
height:124px;
margin-top:38px;
margin-left:73px;
pointer-events:none;
background: rgba(0, 0, 0, 0.5);
z-index:2;
}
There are some flaws, like it allows for the "cover" box to be drawn outside the chart but overall this solution would work. Maybe throw in some more user-input checks.
I also changed the tooltip to be HTML, so I could change the z-index of it and via that generate it ontop of the cover box.
Finally, complete fiddle.

Related

Zoom image given x and y

I have an image which as a "ruler" (made of basic divs positioned absolute on top of the image) that are use to measure the ends of the image. Now the idea is that if you long press one of the ruler ends (the dots at the end of the line which are draggable), the image in the background would zoom in that point, and follow the dot if the user moves it. I am able to detect the long press but I cannot get the image to zoom and follow the dot once detected. The code below is where I have done the detection and now I should apply the styling to move the image. I thought of using the transition property but couldn't get it to zoom on the dot. Any help is appreciated...
Here's a codesandbox with how the ruler works: Link
Meaningful code:
const x = get('x', varToUse); //This just gives the x coordinate of the ruler end
const y = get('y', varToUse); //This just gives the y coordinate of the ruler end
const image = ruler.current.parentElement.parentElement.childNodes[1].childNodes[1];
if (zoom) {
image.style.transform = `translate(${x * 2}px, ${y * 2}px) scale(2.0)`;
} else {
image.style.transform = `scale(1.0)`;
}
This is what the ruler looks like just to get an understanding:
You can make the image a div with background-image.
.image {
background-image: url({image_url});
}
so this way you can update the image size and position easily with this properties
.image {
background-size: x y;
background-position x y;
}
I think this way is easier to do the image resizing and zoom abilities.
another way is to use a canvas library that can help you a lot they have lots of built in functions.
I think trying it without library is better for now but as it grows try to move to a canvas library
The first reason is that in the code you provided, the DOM element that is being manipulated is a div id='root'. The image should be selected.

Issue with AMCharts Series not appearing when The data has less than 3 values

I am using AMCharts4 in a WordPress Project, everything works fine, but I am having a weird issue when I filter the data, let me show what's happening with images:
With All the data (Works 100% fine)
-Filtering the data (More than 2 results works fine 100%)
Filtering the data (2 or Less results not showing the Scores, here is the bug)
See how it's not showing the respective scores as the 2 first images.
The question here is how do I make those scores appear? I have tried commented lines of code in my funcions but nothing seems to work.
You could reproduce the issue in this link selecting the Australia or Brazil location for example
And see my function here
labelBullet = series.bullets.push(new am4charts.LabelBullet())
labelBullet.label.horizontalCenter = "left";
labelBullet.label.dx = 10;
labelBullet.label.text = "{values.valueX.workingValue.formatNumber('#.')}";
labelBullet.locationX = 1;
The issue is in your usage of labelBullet.locationX = 1;. If you take a look at am4 Bullets documentation:
There's one caveat, though. For bullets, locationY property means the
relative vertical position in the whole height of the column. That
means that if our scale would not start at zero, it would be not in
the direct center of the currently visible portion of the column.
As you are using inverted chart, same applies to locationX in your case. Meaning, label position is relative to the scale.
To fix the issue you can either set min for value axis to 0, as in this jsfiddle using:
valueAxis.min = 0;
or as mentioned in the document, push labels directly into column series template, as in this jsfiddle.
label = series.columns.template.createChild(am4core.Label);
label.text = "{values.valueX.workingValue.formatNumber('#.')}";
label.align = "left";
label.valign = "middle";
label.zIndex = 2;

Highcharts - Positioning the legend (left and right/x and y)

My Highcharts solutions give the user the option to control which series are showed at any one point. Because of the amount of series available, I am extending the functionality to checkboxes rather than just add them all on initiation and hide the majority initially as this would make the legend huge.
I would like to overlay a button on the chart to make it look integrated. This gives no problems in itself as I can give a negative value to legend.x to move it to make room for the button. However, this then poses a problem when more series are programmatically added, as the legend maintains its original width and I lose some options off to the side when there are too many.
This is a stripped down fiddle of my problem: https://jsfiddle.net/paLoxcy3/. This is a relevant snippet:
legend: {
align: "right",
x: -100
}
It's worth adding here the graph needs to maintain a responsive width, hence I cannot just add a width. As an aside, were I do to this (and indeed when the series names do eventually drop a line in my current fiddle), they then become left aligned which is not desired.
I've had a good play around with the options of legend but at this point assume the only solution to effectively add padding-right to the legend holder is to use chart.events.load and chart.events.redraw to somehow do it manually? It's a bit annoying as the options to add marginTop and
marginBottom exist but not marginLeft and marginRight.
Any help much appreciated! Shortcut to docs is here to save some time :)
I think that this problem is connected with small issue in Highcharts legend.renderItem function. As a workaround you can change if statement responsible for moving item to another line. Here you can see how this statement looks in code:
// if the item exceeds the width, start a new line
if (horizontal && legend.itemX - initialItemX + itemWidth >
(widthOption || (chart.chartWidth - 2 * padding - initialItemX - options.x))) {
legend.itemX = initialItemX;
legend.itemY += itemMarginTop + legend.lastLineHeight + itemMarginBottom;
legend.lastLineHeight = 0; // reset for next line (#915, #3976)
}
And Here you can see how I have changed this statement:
// if the item exceeds the width, start a new line
if (horizontal && legend.itemX - initialItemX + itemWidth >
(widthOption || (chart.chartWidth - 2 * padding - initialItemX - ((options.align === 'right') ? (-options.x) : options.x)))) {
legend.itemX = initialItemX;
legend.itemY += itemMarginTop + legend.lastLineHeight + itemMarginBottom;
legend.lastLineHeight = 0; // reset for next line (#915, #3976)
}
Here you can find Github topic connected with this issue:
https://github.com/highcharts/highcharts/issues/5443
And here you can see chart with my workaround:
https://jsfiddle.net/paLoxcy3/2/
Best regards.

Jcrop circle selection on reduced large images shows the full size image in the preview selection zone

I'm using the Jcrop jQuery library to allow users to choose a round/circle crop area on images they upload. Their demo page is: http://jcrop.org/demos/circle
When the image is too big to fit on screen, Jcrop can be given the JS options:
boxWidth: 500, //Maximum width to display bigger images
boxHeight: 500, //Maximum height to display bigger images
This successfully resizes the image down to fit the screen for the whole image, however the draggable selection zone instead displays the image at its original full size. And therefore the selection zone does not match everything surrounding it.
Please see the problem at: https://jsfiddle.net/LiebeMachen/mjw88acr/15/
Try dragging around the circle over various places in the image, and you'll see that what is in the circle is actually the full size version of the image, when it should fit in with the surrounding area based on how much the image was sized down for display.
As far as I know, this issue doesn't occur when Jcrop using a regular square/rectangle selection. It seems to be specific to doing a circle selection. There are some question similar question on here about this, but those questions were not using circle selection, and their fixes don't seem to help here with a circle.
My actual in-page Javascript just starts from line 2875 in the JavaScript pane on jsfiddle. All the JS above that is just the jcrop.js library itself.
How can I keeping using the boxWidth/boxHeight settings to scale large images down, while getting the selection zone to also do the same?
The problem you experience comes from the fact that the sample on jcrop website (which you probably used for the circular crop) does not do proper computation of the css for the circular selection.
By adjusting the background-size in the CircularSel besides background-position, you will get what you expected. See snippet below, and don't forget to replace "your_image_id_goes_here" with your actual image id.
// Create a new Selection object extended from Selection
var CircleSel = function () { };
// Set the custom selection's prototype object to be an instance
// of the built-in Selection object
CircleSel.prototype = new $.Jcrop.component.Selection();
// Then we can continue extending it
$.extend(CircleSel.prototype, {
zoomscale: 1,
attach: function () {
this.frame.css({
background: 'url(' + scope.imageUrl() + ')'
});
},
positionBg: function (b) {
var midx = (b.x + b.x2) / 2;
var midy = (b.y + b.y2) / 2;
var ox = (-midx * this.zoomscale) + (b.w / 2);
var oy = (-midy * this.zoomscale) + (b.h / 2);
//this.frame.css({ backgroundPosition: ox+'px '+oy+'px' });
this.frame.css({
backgroundPosition: -(b.x + 1) + 'px ' + (-b.y - 1) + 'px',
// ############## BEGIN FIX ##############
//The following line of code will float your boat
backgroundSize: $('#your_image_id_goes_here').width() + 'px ' + $('#your_image_id_goes_here').height() + 'px'
// ############## END FIX ##############
});
},
redraw: function (b) {
// Call original update() method first, with arguments
$.Jcrop.component.Selection.prototype.redraw.call(this, b);
this.positionBg(this.last);
return this;
},
prototype: $.Jcrop.component.Selection.prototype
});

Raphael svg invert y-axis coordinates

I am using the Raphael library from http://raphaeljs.com/ and work on a chart library. For this library it is useful when the Y-axis are inverted. Now 0,0 is at the top left but I want it to be at the bottom left.
There is a possibility to apply a scale matrix to an element but I want the coordinates to be inverted for whatever I draw. Any clues?
The only way I could figure out to do this was to apply a negative scaling to the svg element using CSS (see this fiddle). (I used jQuery to add the styles).
This is not without problems, though. For example, text is going to be mirrored, unless you do something to un-mirror it (like applying the invert() method I added to elements using Raphael.el):
Raphael.el.invert = function() {
this.transform('s1,-1');
};
Also, if you are going to be interacting with the elements using your mouse, you will have to tweak things. Note that the black circle uses a pretty standard mouseMove function, but it doesn't work - it moves in the wrong direction in y. So you have to do something like I did with the other circles:
function cMove(dx, dy, x,y) {
this.attr('cx', x);
this.attr('cy', paperHeight - y);
};
In short, this is not at all elegant, and no other things I tried were really any better. I know this isn't what you want to hear, but I would recommend getting used to the coordinate system as it is, unless you just plan on displaying static charts.
One small issue with Mike C's resolution is that you have to know if the text is going to be inverted in the end when you create the text. If you want to ensure right-side-up text at the end (after applying other transformations) I found it works well to alter the text element's .transform() to flip the scale of text to right side up at the end.
function InvertText(ObjSet){
// This function resets the inversion of text such that it is always right side up
// ObjSet is a raphael paper.set() object
for (var i=0; i<ObjSet.items.length; i++){
var ThisObj = ObjSet.items[i];
if (ThisObj.type == 'text'){
var tArr = ThisObj.transform();
// Find the scaling factor
for (var j=0; j<tArr.length; j++){
if (tArr[j][0] == 's'){
tArr[0][2] = 1;
break;
}
}
ThisObj.transform(tArr);
}
}
}
You can use like this:
var ObjSet = paper.set().push(
paper.text(0,10,'FirstText'),
paper.path('M,0,0,v,100,h,20,v,-100,h,-20'),
paper.circle(0,0,5)
);
//Flip everything on the y-axis
ObjSet.transform('s,1,-1, T,100,100');
// Make the text right-side-up
InvertText(ObjSet);
Here's how to do it just with RaphaelJS transforms, no CSS transforms.
var SCALE = 2;
var paper = Raphael(0, 0, 400, 700);
// box notched at bottom-center and right-center
var p = paper.path("M0,0 L100,0 L100,40 L90,50 L100,60 L100,100 L60,100 L50,90 L40,100 L0,100 Z");
var bounds = p.getBBox();
p.attr({
stroke: 'none',
fill: [90, '#578A6E', '#34573E'].join("-")
})
.transform("t"+ (-bounds.width/2) +","+ (-bounds.height/2) +
"s"+ SCALE +","+ (-SCALE) +
"t"+ (bounds.width/2) +","+ (-bounds.height/2));
Raphael applies scale transforms from the center of the element's bounding box, rather than its origin. To invert the y-axis, offset before scaling, then offset again after.

Categories

Resources