I'm having problems getting edges coordinates ( x, y).
Is there any solution?
cy.on('tap', function(e) {
if (e.cyTarget === cy) {
...
}
});
You could use midpoint method to get model position coordinates of edges:
cy.on('tap', function(e){
// Checking if the target is not the background and if it is an edge
if(typeof e.cyTarget.isEdge !== 'undefined' && e.cyTarget.isEdge()) {
// Logging its position
console.log(e.cyTarget.midpoint());
}
});
More information on midpoint method can be found here.
Related
I try to make a rectangle draggable. When I add a ViewBox to the svg the drag position is not correct anymore, without it works perfect. I think i need a scale factor. Have someone a solution?
Here is my example
https://codesandbox.io/embed/vue-interactjs-7t0nr
My drag logic
interact(o).draggable({
onmove: event => {
//let mouse = this.$parent.createSVGPoint();
//console.log(mouse);
//mouse.x = event.dx;
//console.log(mouse.x);
//mouse.y = event.dy;
//mouse.matrixTransform(this.$parent.getScreenCTM());
let index = event.target.attributes.index.value;
this.points[index].x += event.dx;
this.points[index].y += event.dy;
}
});
Update
Found a solution with a scale factor; i have add this to my example
onmove: event => {
let index = event.target.attributes.index.value;
this.points[index].x += event.dx * this.scale;
this.points[index].y += event.dy * this.scale;
}
mounted() {
this.calculateScale();
window.addEventListener("resize", this.calculateScale);
},
beforeDestroy() {
window.removeEventListener("resize", this.calculateScale);
},
methods: {
calculateScale() {
let rootRect = this.$refs.mainSvg.getBoundingClientRect();
this.scale = this.svgWidth / rootRect.width;
console.log(this.scale);
}
}
I am trying make a multi colored polyline. I have been able to do it successfully before with Vue.js but now we are adding it to react native app and its not working as i expected in React js.
I am making multiple polylines, each line (segment) has multiple points. I have a structure like this:
groups: [ { key: 'BLUE', cordinates: [] }, key: 'GREEN', cordinates: [] ];
Now each key represent a color and cordinates is an array of cordinates. Now when I loop it like this:
{
this.state.groups.map((group, index) => {
return (
<Polyline
key={index}
coordinates={group.Points}
strokeColor={
group.Key === "GREEN" ? "#0F0" : "#000"
// "#000"
// group.Key === "G"
} // fallback for when `strokeColors` is not supported by the map-provider
strokeColors={[
'#7F0000',
'#00000000', // no color, creates a "long" gradient between the previous and next coordinate
'#B24112',
'#E5845C',
'#238C23',
'#7F0000'
]}
strokeWidth={6}
/>
);
})
}
The problem is it works! perfectly but it doesnt draw the last polyline which is being updated. So for example there are 10 segments in this polyline. Now after 3 are drawn and loop is on 4th segment, its pushing each cordinate in the last group with a delay of 30 ms. I added delay to show it animated. Now it won't draw on map untill all cordinates of the 4th segments are pushed. When its done, and 5th segment is started, 4th segment shows perfectly but now 5th segment stops working.
I know that points are being perfectly added because I have added a Camera as well and I change its center to be last point that was pushed in groups/segments.
Group/Segments loop:
addPoint(group, point) {
var data = this.state.data;
if (group <= (data.length - 1)) {
var g = data[group];
// console.log('g', g);
if (point <= (g.Item2.length - 1)) {
var p = g.Item2[point];
var {groups} = this.state;
// console.log('groups,', groups);
groups[group].Points = groups[group].Points.concat({
longitude: p.longitude,
latitude: p.latitude,
});
this.MapView.animateCamera({
center: {
latitude: p.latitude,
longitude: p.longitude,
},
duration: 100,
zoom: 15,
});
point++;
setTimeout(() => {
this.addPoint(group, point);
}, 300);
} else {
point = 0;
group++;
if (group < this.state.data.length - 1) {
var key = this.state.data[group].Item1;
console.log('key', key);
var groups = this.state.groups.concat({
Key: key,
Points: [],
});
this.setState({
groups: groups,
})
}
setTimeout(() => {
this.addPoint(group, point);
}, 300);
}
} else {
console.log('last group reached');
}
}
Is there any solution for this?
I figured it out. The problem was whenever I updated the coordinates array of any polyline, it had to re-render whole thing which was performance wise very poor decision.
I solved it by making a custom polyline component which maintains its own coordinates array. Implemented an inner timeout function which pushes coordinates incrementally. This solved the problem and its now super easy to use.
You can read more about this here: multi colored gradient polyline using google maps on react native
How would I get the coordinates of my onTouchEnd event. So I touch or move figure anywhere within the display and I can retrieve the X, Y positioning of where it happened. onResponderRelease is not triggered in a onTouchEnd on Android,
I've included example implementations for all of the gesture response event handlers, but have commented out most of them on my View to just provide the basic functionality: subscribing to all touch and move events
pseudo code would look like this :
<View
style={this.props.style}
ref={this._saveRef}
onStartShouldSetResponder={(event) => {
return this.handleDoubleTap({nativeEvent:
event.nativeEvent});
}}
onResponderGrant={this._onTouchStart}
onResponderMove={this._onTouchMove}
onResponderRelease={this._onTouchEnd}
onResponderTerminate={this._onTouchEnd} // When
onResponderRelease can't call by some reason
>
{this.props.children}
</View>
Responder Event Handler Methods :
if (this.isDoubleTap) {
return false;
}
this.context.setScroll && this.context.setScroll(false);
const currentTouchTimeStamp = Date.now();
this._prevTouchInfo = {
prevTouchX: nativeEvent.pageX,
prevTouchY: nativeEvent.pageY,
prevTouchTimeStamp: currentTouchTimeStamp
};
this.props.onStart(nativeEvent);
};
_onTouchMove = ({nativeEvent}) => {
if (nativeEvent.touches.length <= 1) {
if (this.isDoubleTap) {
return false;
}
const self = this;
const gesture = {
x0: this._prevTouchInfo.prevTouchX,
x1: nativeEvent.pageX,
y0: this._prevTouchInfo.prevTouchY,
y1: nativeEvent.pageY,
dx: nativeEvent.pageX - this._prevTouchInfo.prevTouchX,
dy: nativeEvent.pageY - this._prevTouchInfo.prevTouchY
};
InteractionManager.runAfterInteractions(function () {
self.props.onMove(nativeEvent, gesture);
});
}
};
_onTouchEnd = ({nativeEvent}) => {
nativeEvent.touches.length === 0 && this.context.setScroll && this.context.setScroll(true);
this.props.onEnd(nativeEvent);
};
}
I am Developing an Application in React native.Actually when i am touch on Particular Position On view, getting the Corresponding x and y co-ordinates. App UI would look like this:
If this is still not enough functionality for android (eg. if you need multi-touch info), refer to PanResponde
The API documentation (http://js.cytoscape.org/#extensions/api) says that cytoscape( type, name, extension ) whould register an extension.
It worked in v2.4 but doesn't work anymore in v2.6.
What is the right way to do it now ?
EDIT:
Here is what I do
;(function($$){ 'use strict';
var defaults = {
fit: true, // whether to fit the viewport to the graph
padding: 30, // the padding on fit
startAngle: 3/2 * Math.PI, // the position of the first node
counterclockwise: false, // whether the layout should go counterclockwise/anticlockwise (true) or clockwise (false)
minNodeSpacing: 10, // min spacing between outside of nodes (used for radius adjustment)
boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
avoidOverlap: true, // prevents node overlap, may overflow boundingBox if not enough space
height: undefined, // height of layout area (overrides container height)
width: undefined, // width of layout area (overrides container width)
concentric: function(node){ // returns numeric value for each node, placing higher nodes in levels towards the centre
return node.degree();
},
levelWidth: function(nodes){ // the variation of concentric values in each level
return nodes.maxDegree() / 4;
},
animate: false, // whether to transition the node positions
animationDuration: 500, // duration of animation in ms if enabled
ready: undefined, // callback on layoutready
stop: undefined // callback on layoutstop
};
function ConcentricLayout( options ){
console.log('INIT');
this.options = $$.util.extend({}, defaults, options);
}
ConcentricLayout.prototype.run = function(){
console.log('RUNNING');
// Run code
};
$$('layout', 'customconcentric', ConcentricLayout);
})( cytoscape );
And how I use it
var cy = cytoscape({
container: document.getElementById('cy'),
boxSelectionEnabled: false,
autounselectify: true,
elements: p,
layout: {
name: 'null',
stop: function() {
cy.elements('node.group').forEach(function( ele ){
var eles = ele.children();
eles.layout({
name: 'customgrid',
fit: false,
avoidOverlapPadding: 0,
columns: 2
});
});
cy.elements('node.machine').forEach(function( ele ){
var elesleft = ele.children('node.mod');
var elesright = ele.children('node.group');
var descendants = elesright.descendants();
if (elesleft.length > 0) {
var relpos = getRelativePositions(descendants);
elesright.forEach(function( ele ){
ele.relativePosition('x', 200);
});
setRelativePositions(relpos, cy);
elesleft.layout({
name: 'customgrid',
fit: false,
avoidOverlapPadding: 0,
columns: 1
});
}
});
cy.elements('node.machine, node.env').layout({
name: 'customconcentric',
fit: true
});
}
}
});
With 2.4.9 I see this in my browser's javascript console.
cytoscape.layout.custom.js:41 INIT
cytoscape.layout.custom.js:46 RUNNING
With 2.6.2, nothing happens.
The registration API is fine. You're relying on a private object that is not accessible:
this.options = $$.util.extend({}, defaults, options);
Only reference public APIs in the docs if you want your code to be compatible with newer versions: http://js.cytoscape.org
Use a proper debugger, like in Chrome. Your debugger should show you an error message when you reference non-existent objects or otherwise cause exceptions.
BOUNTY UPDATE: I'm putting a bounty on this, so I wanted to give a little bit more information in case someone comes up with a different solution than modifying my code. The object is to animate the actual category position in HighCharts for bar and column charts. Animating the actual "bar/columns" seems to be built in to HighCharts, however, the label positions is what I'm having trouble with. See below for JSFiddle. Also, I know this question is dealing with SVG, but I need the animation support in IE8 as well.
I'm currently on a mission to animate the reorganization of categories in HighCharts for bar and column charts.
I have bar charts working fine, with the ability to reorganize categories and labels, with labels animating with the following code:
$(this).animate({'svgY': c.labelPositions[myX]}, {'duration': animDuration, 'queue': false});
Now I'm working on the columns, and I'm having significant trouble getting the labels to animate. The code is relatively the same:
$(this).animate({'svgX': c.labelPositions[myX]}, {'duration': animDuration, 'queue': false});
I'm using jQuery SVG to allow the animation of SVG elements, you can find it here.
You can view the jsFiddle I'm working on here. Just click the "Go" buttons under each chart to see them in action.
The actual "hack" to allow category animating is the Highcharts.Series.prototype.update = function(changes, callback){ function.
Just playing around trying to get something to work, I found that I could animate the svgY of the Column labels, but svgX just seems to not function at all.
Actual HighCharts.js hacks are welcome.
I took a look into your code and improved it a little bit. I did the following:
Unified code for column/bar using single variable that stores the attribute we're going to update
Removed jQuerySVG dependency, instead my code uses built-in Highcharts animate method
Fixed some minor bugs
I tested this with IE7+/Chrome/Firefox, it works fine in all of them.
Here you can find my version of Highcharts.Series.prototype.update:
Highcharts.Series.prototype.update = function (changes, callback) {
var series = this,
chart = this.chart,
options = chart.options,
axis = chart.xAxis[0],
ticks = axis.ticks,
type = chart.options.chart.type,
animDuration = 400,
attr = type === 'bar' ? 'y' : 'x',
animOpt = {},
text;
if (options.chart.animation && options.chart.animation.duration) {
animDuration = options.chart.animation.duration;
}
if (type == "bar" || type == "column") {
if (typeof chart.labelPositions === "undefined") {
chart.labelPositions = [];
$.each(ticks, function () {
chart.labelPositions.push(this.label[attr]);
});
}
for (var category in changes) {
for (var i = 0; i < series.points.length; i++) {
if (typeof series.points[i].originalCategory === "undefined") {
series.points[i].originalCategory = series.points[i].category;
}
if (series.points[i].originalCategory == category) {
$.each(ticks, function () {
text = this.label.text || this.label.element.innerHTML; // use innerHTML for oldIE
if (text == category) {
var myX = (typeof changes[category].x !== "undefined") ? changes[category].x : series.points[i].x;
series.points[i].update(changes[category], false, {
duration: animDuration
});
animOpt[attr] = parseInt(chart.labelPositions[myX]);
// This is the line that animates the bar chart labels.
this.label.animate(animOpt, {
'duration': animDuration,
'queue': false
});
return false;
}
});
}
}
}
chart.redraw();
if (typeof callback !== "undefined") {
setTimeout(function () { callback(); }, animDuration);
}
}
}
Check out the demo: http://jsfiddle.net/evq5s/17