Is there a way to dynamically build an SVG using sprites in amcharts 4?
Example: screenhot
There are 20 different types which are represented by colors.
Each pin can contain a multitude of types.
So an example can be that a pin has 3 types and will consist out of 3 colors.
I have an SVG path which is a circle.
With regular JS and SVG i can create a path for each type and change the stroke color, strokedasharray and strokedashoffset.
This results in the nice circle with 3 colors.
However this seems to be impossible to do with amcharts 4.
For starters, strokedashoffset is not even a supported property for a sprite. Why would you bother supporting strokedasharray and then ignore strokedashoffet?!
The second problem is finding out how to pass data to the sprite.
This is an example of a data object I pass to the mapImageSeries class.
[{
amount: 3,
client: undefined,
colorsArr: {0: "#FFB783", 1: "#FD9797", 2: "#77A538"},
dashArray: "500,1000",
dashOffset: 1500,
divided: 500,
global: true,
groupId: "minZoom-1",
hcenter: "middle",
id: "250",
latitude: 50.53398,
legendNr: 8,
longitude: 9.68581,
name: "Fulda",
offsetsArr: {0: 0, 1: 500, 2: 1000},
scale: 0.5,
title: "Fulda",
typeIds: (3) ["4", "18", "21"],
typeMarker: " type-21 type-18 type-4",
vcenter: "bottom",
zoomLevel: 5
}]
It seems impossible to pass the colors down to the sprite.
var svgPath = 'M291,530C159,530,52,423,52,291S159,52,291,52s239,107,239,239c0,131.5-106.3,238.3-237.7,239'
var mainPin1 = single.createChild(am4core.Sprite)
mainPin1.strokeWidth = 100
mainPin1.fill = am4core.color('#fff')
mainPin1.stroke = am4core.color('#ff0000')
mainPin1.propertyFields.strokeDasharray = 'dashArray'
mainPin1.propertyFields.strokeDashoffset = 'dashOffset'
mainPin1.path = svgPath
mainPin1.scale = 0.04
mainPin1.propertyFields.horizontalCenter = 'hcenter'
mainPin1.propertyFields.verticalCenter = 'vbottom'
With what you've provided, simulating your custom SVGs is beyond the scope of what can be answered, so I'll try tackling:
applying stroke-dashoffset despite lack of innate library support. (I see you've added a feature request on GitHub for it, so why the library doesn't include it, when/if it will, can be left for discussion there.)
passing data/colors to the Sprite
For both we're going to have to wait until the instances of Sprites are ready along with their data. Presuming your single variable is a reference to a MapImageSeries.mapImages.template, we can set up an "inited" event like so:
single.events.once("inited", function(event){
// ...
});
Our data and data placeholders don't really support nested arrays/objects in general, since your colors are nested within a field, we can find them via:
event.target.dataItem.dataContext.colorsArr
You can then set the fill and stroke on the Sprite or event.target.children.getIndex(0) manually from there (in my demo below, the index will be 1 because mainPin1 is not the first/only child created on the MapImage template).
As for stroke-dashoffset, you can access the actual rendered SVGElement via sprite.group.node and just use setAttribute.
I forked our map image demo from our map image data guide and added all the above to it here:
https://codepen.io/team/amcharts/pen/6a3d87ff3bdee7b85000fe775af9e583
Related
I would have a question to ask, hoping for your advice. In my project I'm working on the different entities that will be present, one of them is the "TreeEntity". It is visibly composed of four parts. "Leaves, Shadow, Stump, Trunk". All four parts have only one id and are randomly chosen when a new "TreeEntity" is called. In addition, leaves and shadow each have four different states, based on the age of the tree. The shadow will have to rotate around the trunk based on the time. Now, since the engine of my project would like to create it as universal as possible, so that it can also be used in other projects or other people, I am facing a dead end.
I have this result at the moment (premise, all the images used are for testing purposes only):
As can be seen, the shadow is very detached from the trunk. This, theoretically, would be possible to fix it, modifying the image used:
Or write a huge dictionary with all the precise anchors written for each image that make up leaves, shadow, stump and trunk.
But doing so would be detrimental to my goal. So, the advice I would like to ask you, is whether in pyglet or otherwise, regardless of the shadow used (which in this case is random) is it possible to choose an anchor point that places it in the right place? Or do you have to modify the image or create a dictionary?
My question arises from the fact that I have seen several times images for example of shadows or something else that is like the one I inserted here, but in that case the shadows were in their correct place. The same game from here I took the pictures to test and I'm inspired by one or two mechanics has the shadows placed in the right place. So I don't know which way to go sincerely.
At the moment, all four parts have a universal anchor dictated by a small dictionary:
__statdict = {
"leaves": {
"cell": (3, 1),
"anchor_x": "center",
"anchor_y": "bottom"
},
"shadow": {
"cell": (4, 1),
"anchor_x": "left",
"anchor_y": "bottom"
},
"stump": {
"cell": (1, 1),
"anchor_x": "center",
"anchor_y": "bottom"
},
"trunk": {
"cell": (1, 1),
"anchor_x": "center",
"anchor_y": "bottom"
}
}
And when loading resources, anchor is assigned:
if self.__statdict[name]["anchor_x"] == "center":
ancx = texture.width // 2
elif self.__statdict[name]["anchor_x"] == "right":
ancx = texture.width
else:
ancx = 0
if self.__statdict[name]["anchor_y"] == "center":
ancy = texture.height // 2
elif self.__statdict[name]["anchor_y"] == "top":
ancy = texture.height
else:
ancy = 0
texture.anchor_x = ancx
texture.anchor_y = ancy
I have tried to be as complete as possible. Thanks for your help. Maybe maybe I'm having problems for nothing and the solution is simpler than I thought, so I apologize if this were the case.
Update (1):
Is it possible, via pyglet or a module to accompany it, given the direction of a sprite (in this case on the right), to know what is the position of the first pixel that is not an alpha? Something like this: "The direction of the SpriteShadow is to the right. The first pixel that has an rbg value with alpha equal to 255 is ay = 20 (relative to the width of the sprite). Then I move the SpriteShadow to the left with respect to the general anchor of 20 pixels "
Using Fabric.js, I have an image object that acts like a background where other group objects (consisting of rectangle and text) can be placed over it.
I would like to be able to programmatically rotate the image (90 degrees clockwise or counter-clockwise) when user clicks the corresponding button, and then rotate all other group objects relative to the image.
I was able to get this to work with the Fabric.js group feature, but the performance progressively degraded over a few rotations even with just one group object.
Looking for alternatives, I changed to use the approach described in the tranformation example here: http://fabricjs.com/using-transformations. However, the group object is not placed in the expected position after rotation.
Would anyone have any pointers on what the problem might be?
Here's the relevant code that handles just one group object for simplicity:
private rotateObjects(rotationAngle: number): void
{
let groups = this.Canvas.getObjects().filter(x => x !== this.ActiveImage);
let imageTransform = this.ActiveImage.calcTransformMatrix();
let invertedImageTransform = fabric.util.invertTransform(imageTransform);
let annotationRelationship = fabric.util.multiplyTransformMatrices(invertedImageTransform, (groups[0]).calcTransformMatrix());
this.ActiveImage.rotate(rotationAngle);
let newTransform = fabric.util.multiplyTransformMatrices(this.ActiveImage.calcTransformMatrix(), annotationRelationship);
let options = fabric.util.qrDecompose(newTransform);
groups[0].set({
flipX: false,
flipY: false,
});
groups[0].setPositionByOrigin(new fabric.Point(options.translateX, options.translateY),
"center",
"center"
);
groups[0].set(options);
groups[0].setCoords();
this.Canvas.renderAll();
}
Here are screenshots of before and after rotation. Notice that the yellow group rectangle is not placed exactly over the words "Manny Bello".
Before rotation: https://imgur.com/RcS4ekZ
After rotation: https://imgur.com/nk8gStf
Is there a way I can address legend items individually?
I'd like to style them, make some appear smaller, some disabled, stuff like that.
If I were to define a module/plugin, how would I manipulate the array of items?
legend: {
item: {
rules: []
}
}
doesn't work.
Workaround based on SVG editing
This is a hacky solution that goes into the SVG markup and sets the attributes we want: 0.25 opacity and smaller marker radius.
⚠ It will break unless there's SVG rendering and round legend markers.
It also FAILS to work on legend_item_click event (it works too early and gets overridden afterwards).
let disableLegendItems = (arr) => {
let markers = [...document.getElementById('chartDiv-graph-id0-legend-c').children],
items = [...document.getElementsByClassName('zc-legend-item')];
arr.map((i) => {
markers[i].setAttribute('fill-opacity', '0.25');
markers[i].setAttribute('r', '4');
items[i].setAttribute('fill-opacity', '0.25');
});
console.warn(`Legend items at indexes ${arr} styled as disabled`);
};
zingchart.bind('demo-chart', 'load', function() {
disableLegendItems([3, 7, 8, 9, 11, 13]);
});
You can customize the legend by setting the legendMarker and legendItem objects inside the series objects (where values are placed). This way you can customize the legend marker and item for each individual series. We do not have to use rules and should avoid rules if possible for efficiency.
Here is a demo to show you how to set this up:
https://demos.zingchart.com/view/064CFFCD
being new to Flot i am struggling a bit. My goal is the present a bar with different data elements in it that must have a different color per element. I want to provide the color per data element.
Any hints on how this can be done?
Example:
[0,100][0,200][0,100][0,200]
All elements with value 100 should be blue and all elements with 200 should be green.
A nice one would be,
[0,100,blue][0,200,green][0,100,blue][0,200,green]
But this off course does not work, it is just an explanation what i want to achieve!
Doing this with multiple data series seems does not work in my case.
Any hints on how this can be done?
You can do it with multiple series, as this is how I have done something like this :)
So in your plot method you would have something like this:
$.plot($('#placeholder'), []); // The array would hold your data
You can also further extend this to provide some options to flot to tell it what to make your chart look like. To do that you pass an object of options after the array.
However, im not quite sure what you need in terms of the Graph, your example gave some numbers but the x coordinate was all 0, im not sure if you just want a bar graph?
Anyway, heres the code of how you would get it to display a bar graph, with one green line and one blue line:
var flotOptions = {
series: {
lines: { show: true, fill: false, lineWidth: 15 }
}
},
var data = [{
color: '#001EFF',
data: [[0, 0], [0, 100]]
}, {
color: '#00FF0E',
data: [[5, 0], [5, 200]]
}];
$.plot($('#placeholder'), data, flotOptions);
I would recommend making it so that the data is automatically generated on a server side, then you can check in javascript, and add the color depending on the y value in the data array of each series.
I have also created a jsFiddle of this for you, so you can go take a look and play around with it. As I have said im not quite sure what you want but this is a good start for you. Good luck!!
I'm looking for a way to access each point's on the graph actual svg object in order to change some of its properties on-the fly.
Say I have 100 objects, of those around 50% need to be drawn in a different color (though they belong to the same data series, no way round). Thus, by using:
chart.series[0].points
I'm able to modify object properties (like fill) but no way to redraw the chart afterwards. Using chart.redraw() gives nothing, well actually it changes the points color but only after I mouse over them, the chart itself is not redrawn properly.
Any help appreciated.
Regards!
There are at least two ways that you can use here.
On Load
You can define a color for each of the points explicitly while adding them to the data array. The following is what the documentation states
A list of object with named values. In this case the objects are point configuration objects as seen below.
Range series values are given by low and high.
Example:
data: [{
name: 'Point 1',
color: '#00FF00',
y: 0
}, {
name: 'Point 2',
color: '#FF00FF',
y: 5
}]
Reference: http://api.highcharts.com/highcharts#series.data
jsFiddle # http://jsfiddle.net/jugal/mXPPH/
Dynamically
You can add the color/markers to individual points using the point.update() method as follows,
var p = chart.series[0].points[l - 1];
p.update({
marker: {
symbol: 'square',
fillColor: "#A0F",
lineColor: "A0F0",
radius: 5
}
});
Reference : http://api.highcharts.com/highcharts#Point.update%28%29
You may also want to look a similar answer I had provided for this question # Dynamically draw marker on last point in highcharts and its jsFiddle # http://jsfiddle.net/jugal/zJZSx/