move the horizontal line up and down - javascript

I have a thermometer of svg type. I want to create some animation using Snap. I could animate the text with the given value but could not animate the dashed line that you can see in the screenshot to move as per the given value. How should i do it?
Here is my code
const svg = Snap(this.svg);
const { color } = this.state;
svg.line(55, 366, 90, 366).attr({
id: 'marker-line',
stroke: color,
strokeDasharray: '2 4',
strokeWidth: '1'
});
const animateMarker = (value, svg, marker, lastValue) => {
// Snap.animate(value);
const markerLine = svg.select('#marker-line');
if (markerLine) {
Snap.animate(
lastValue || 0,
value,
val => {
// markerLine.attr({ y1: 366-val });
marker.textContent = roundOffDecimalDigit(val, 2); // eslint-disable-line no-param-reassign
},
400
);
}
};

Without the rest of the code, it's difficult to tell. Your code looks ok though, just it only sets one of the y co-ordinates, and I assume you want to set both. Eg
Snap.animate(lastValue,value, function( val ) { markerLine.attr({ y1: val, y2: val }); }, 400 )
If it's still not working, try putting it on a jsfiddle and it will be easier to help.

Related

LightningChartJs - add breaks to line chart for x axis

I would like to display breaks in the x axis of the line chart and display them with a fixed width, like at the image below:
Found this nice article that describes the functionality:
https://www.arction.com/news/using-scale-breaks-data-visualization/
If I understand correctly, there is the possibility in the SDK to display these with scaleAreas or even better with ClipAreas.
But I could not find this possibility in the LightningChart JS framework.
As starting point I was able to generate breaks in data with a NaN value and added a Textbox.
So my questions are:
1. How to add breaks and fit to predefined width in xaxis
2. How to rotate the Textbox 90 degrees?
Help would be greatly appreciated.
Thanks in advance. :o)
// Extract required parts from LightningChartJS.
const {
lightningChart,
AxisTickStrategies,
DataPatterns,
emptyFill,
ColorHEX,
emptyLine,
UIElementBuilders,
UIBackgrounds,
UIOrigins,
UIDraggingModes,
SolidLine,
SolidFill
} = lcjs
// Create a XY Chart.
const dateOrigin = new Date(2020, 0, 1)
const chart = lightningChart().ChartXY({
defaultAxisXTickStrategy: AxisTickStrategies.DateTime(
dateOrigin
)
})
.setTitle('Demo')
chart.setPadding({ right: '1' })
// Add a progressive line series.
// Using the DataPatterns object to select the horizontalProgressive pattern for the line series.
const lineSeries = chart.addLineSeries({ dataPattern: DataPatterns.horizontalProgressive })
.setName('Demo')
// Generate some points using for each month
const dataFrequency = 10;
// Setup view nicely.
chart.getDefaultAxisY()
.setScrollStrategy(undefined)
.setInterval(-20, 120)
.setTitle('Demo y')
// Data for the plotting
const data = [];
for (let i = 0; i < 1500; i++) {
let index = i;
if (i === 500) {
index = NaN;
}
if (i > 500) {
index = i + 1000;
}
data.push({
x: index,
y: Math.floor(Math.random() * 100)
});
}
chart.addUIElement(
UIElementBuilders.TextBox
.setBackground(UIBackgrounds.Rectangle),
chart.uiScale
)
.setText('Break')
.setPosition({ x: 45, y: 50 })
.setOrigin(UIOrigins.Center)
.setDraggingMode(UIDraggingModes.notDraggable)
.setFont((font) => font
.setSize(40)
)
.setBackground(style => style
.setFillStyle(new SolidFill({ color: ColorHEX('#f00') }))
)
.setTextFillStyle((style) => style
.setColor(ColorHEX('#0f0'))
)
// Adding points to the series
lineSeries.add(data.map((point) => ({ x: point.x * dataFrequency, y: point.y })))
<script src="https://unpkg.com/#arction/lcjs#1.3.1/dist/lcjs.iife.js"></script>
LightningChart JS doesn't have support for scale breaks yet. It's something that will most likely be developed at some point but there is no timeline for it yet.
TextBox rotation is not yet possible for UITextBox, it's coming in a future release but not the next release.

Highcharts Annotation SVG doesn't move with other shapes

in my Highcharts project, I successfully make all the shapes in a single annotation draggable, but when I add the SVG (the type of this shape is "path"), the SVG doesn't move along with other shapes in the annotation.
I need to have some customised shapes in the annotation. Can anyone point out what the issue is for this SVG? Is it possible to put the SVG within annotation and still be draggable? Or is it some mistake I made that causes the issue?
My example here. https://jsfiddle.net/roy_jaide/754xuhtk/ As you can see, the label, circle and the line are all draggable, but the SVG shape just doesn't move at all.
Thanks for reading my question, and much appreciated if any solution provided.
Highcharts.Annotation.prototype.onDrag = function (e) {
if (this.chart.isInsidePlot(e.chartX - this.chart.plotLeft, e.chartY - this.chart.plotTop)) {
var translation = this.mouseMoveToTranslation(e),
xAxis = this.chart.xAxis[0],
yAxis = this.chart.yAxis[0],
xStep = this.options.stepX,
yStep = this.options.stepY,
pxStep = xAxis.toPixels(xStep) - xAxis.toPixels(0),
pyStep = yAxis.toPixels(yStep) - yAxis.toPixels(0);
if (this.options.draggable === 'x') { //for now, it's exclusive for age handle
this.potentialTranslationX += translation.x;
if (Math.abs(this.potentialTranslationX) >= Math.abs(pxStep)) {
translation.x = (this.potentialTranslationX > 0) ? pxStep : -pxStep;
translation.y = 0;
this.currentXValue += (this.potentialTranslationX > 0) ? xStep : -xStep;
this.potentialTranslationX -= (this.potentialTranslationX > 0) ? pxStep : -pxStep; //minus the step and continue to cumulate
//this.potentialTranslation = 0; //not correct, the mouse will go faster than the handle
if (this.points.length) {
this.translate(translation.x, 0);
} else {
this.shapes.forEach(function (shape) {
shape.translate(translation.x, 0);
});
this.labels.forEach(function (label) {
label.translate(translation.x, 0);
label.text = label.annotation.options.preText + label.annotation.currentXValue;
});
}
}
}
this.redraw(false);
}
}
Update 1: After trying Sebastian's answer on my chart, it turns out to be difficult to calculate the correct coordinates. At last I use type "image" to put display the shape. The shape is a Font-Awesome icon so before using "image" I did try to add a label with "useHTML" : true, but it seems the icon is moved a little after the first redraw(false), not sure why.
The image of the shape. I achieved this by adding "image" shape.
d: ["M", 440, 72, "L", 410, 45, 470, 45, "Z"],
I wouldn't recommend this way to create a draggable custom shape. This option creates a shape as expected but also sets a fixed position. Probably it is possible to implement move functionality, but I think that it will require a lot of changes into draggable core code.
What I can suggest is to make it this way:
annotations: [{
draggable: 'x',
shapes: [{
points: [{
x: 440,
y: 72
}, {
x: 410,
y: 45
}, {
x: 470,
y: 45
}, {
x: 440,
y: 72
}],
fill: 'red',
type: 'path',
stroke: "blue",
strokeWidth: 3,
markerStart: 'circle',
}]
}]
Where markerStart is a defined shape.
See a Demo

Highcharts: Can I animate changing the order of bars on bar chart?

I want to create a chart like the below.
https://www.reddit.com/r/interestingasfuck/comments/9togwf/the_major_world_economies_over_time/
I created this chart by Highcharts. But, I can't animate changing the order of bars.
function rotate(array, times) {
while (times--) {
var temp = array.shift();
array.push(temp)
}
}
window.data = {
categories: ['Africa', 'America', 'Asia', 'Europe', 'Oceania'],
y_values: [100, 200, 300, 400, 500],
colors: ['#DC4D3A', '#E93D3F', '#83C6C7', '#46D388', '#D1D785']
};
document.getElementById("button").addEventListener("click", function () {
for (var i = 0; i < data['y_values'].length; i++) {
chart.series[0].data[i].update({y: data['y_values'][i]});
chart.series[0].data[i].update({color: data['colors'][i]});
}
chart.xAxis[0].update({categories: data['categories']});
rotate(data['y_values'], 1);
rotate(data['categories'], 1);
rotate(data['colors'], 1);
}, false);
All code I wrote are in JSFiddle.
https://jsfiddle.net/Shinohara/35e8gbyz/
Please anyone can help me?
Highcharts provides animate method for SVG elements, which you can use to achieve the wanted result. You need to animate columns, axis labels and data labels:
document.getElementById("button").addEventListener("click", function() {
var points = chart.series[0].points,
ticks = chart.xAxis[0].ticks;
points[0].graphic.animate({
x: points[1].shapeArgs.x
});
points[1].graphic.animate({
x: points[0].shapeArgs.x
});
points[0].dataLabel.animate({
y: points[1].dataLabel.translateY
});
points[1].dataLabel.animate({
y: points[0].dataLabel.translateY
});
ticks[0].label.animate({
y: ticks[1].label.xy.y
});
ticks[1].label.animate({
y: ticks[0].label.xy.y
});
}, false);
Live demo: https://jsfiddle.net/BlackLabel/ux59vcd6/
API Reference: https://api.highcharts.com/class-reference/Highcharts.SVGElement#animate
It seems there is no native implementation.
The only idea I have it to not display Y axis labels at all. Using SVGRenderer
draw the text and save it as a variable (for further updating)
chart.customText = chart.renderer.text('label name', 100, 100) // label name, X, Y
.attr({
useHTML: true
})
.css({
fontSize: '16px' // and other CSS if necessary
})
.add();
Then you can update x, y or text
chart.customText.attr({
str: 'new text. You can use HTML',
y: 150 // new value
})
Your next step is to do something with Y position. Or even you can try to draw only 1 text with HTML that contains all labels. Then using JS move them as you need.

c3 detect hover over bar

I wish to have tooltips AND do some custom stuff when a user hovers over a .c3-bar. This is both not supported by the c3 api and made difficult because the way c3 manages pointer events (by using css to turn them off). If I turn .c3-bar to pointer-events:auto !important tooltips stop working, but otherwise I can't listen to events on .c3-bar. Does anyone know how to address this?
I got it to work, but its ugly ugly
const c3Hover = 'c3-hover'
const barSelector = '.c3-bar'
const $wrapper = $('#' + this.chart.id)
const getBarRects = () =>
_.map($wrapper.find(barSelector),
(bar) => bar.getBoundingClientRect())
$wrapper.on('mousemove', ({clientX,clientY}) =>
_.reduce(getBarRects(), (acc, {left,right,top,bottom}) =>
acc || left <= clientX && clientX <= right
&& top <= clientY && clientY <= bottom
, false) ? $wrapper.addClass(c3Hover)
: $wrapper.removeClass(c3Hover))
There's a built-in way that reports an event when it hovers over data points, but it seems to fire once per series over a particular x value - i.e. on a bar chart with 2 series it will fire twice. Don't know if that's good enough for your situation?
var chart = c3.generate({
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 130, 100, 140, 200, 150, 50]
],
type: 'bar',
onmouseover: function (d) {
console.log ("yo", d);
},
},
bar: {
width: {
ratio: 0.5 // this makes bar width 50% of length between ticks
}
}
});
outputs:
yo Object {x: 0, value: 30, id: "data1", index: 0, name: "data1"}
yo Object {x: 0, value: 130, id: "data2", index: 0, name: "data2"}
when moved over the first pair of bars (regardless of the particular one it's actually over)
http://c3js.org/reference.html#data-onmouseover
Edit: Aah, I see it also fires when you're not over the actual bar but also in the general vicinity. I guess this doesn't meet your requirements then.

Creating multiple repeating shapes

I have the following code which creates me rectangle that contains some text. I need to create multiple addressable instances of this rectangle so that I can individually animate them. Each rectangle needs to contain a different text label.
var s = Snap(800, 600);
var block = s.rect(50, 50, 100, 100, 5, 5);
block.attr({
fill: "rgb(236, 240, 241)",
stroke: "#1f2c39",
strokeWidth: 3
});
var text = s.text(70, 105, "Hello World");
text.attr({
'font-size':20
});
block.attr({
width: (text.node.clientWidth + 50)
});
Rather than repeating my code I would like to create a function that accepts the text and the coordinates for placing the rectangle. What is the best way to achieve this ? Is this capability already included within snap.svg ?
UPDATE
I created another plugin, this time to import and scale SVG images. Is this the best approach to take for this ? Is the only way to scale the image using the `transform attribute ?
Import SVG plugin example.
Snap.plugin( function( Snap, Element, Paper, global ) {
Paper.prototype.importImage = function( x, y, scale ) {
var ig1 = s.group();
var image = Snap.load("../package.svg", function ( loadedFragment ) {
ig1.attr({transform: 'translate(' + x + ',' + y + ') scale('+ scale +')'});
ig1.append( loadedFragment);
} );
return ig1;
}
});
You could create a plugin to give you a new element option that does it for you, for example...
Snap.plugin( function( Snap, Element, Paper, global ) {
Paper.prototype.textRect = function( text, x, y ) {
var block = s.rect(x, y, 100, 100, 5, 5)
.attr({
fill: "rgb(236, 240, 241)",
stroke: "#1f2c39",
strokeWidth: 3,
});
var text = s.text(x + 20, y + 50, text).attr({ 'font-size': 20 });
block.attr({ width: (text.node.clientWidth + 50) });
}
});
var s = Snap(800,800)
s.textRect('Hi', 100, 100 );
s.textRect('There', 100, 200 );
example fiddle
You may want to put them both in a 'g' group element if you will move them around or drag them or something, so you can perform operations on the group.

Categories