I can initialize the object just fine:
ngOnInit() {
this.cy = cytoscapeService.cytoscape( Object.assign( {
container : this.el.nativeElement,
}, DATA_FLOW_CYTOSCAPE_CONFIG ) );
this.cy.panzoom( Object.assign(
{ minZoom : 0, maxZoom : 10 }, // arbitrary numbers
relateConfig.panzoomDefaults,
) );
However, no matter what numbers I use, or as my graph changes views, I can't seem to update the max/min appearance of the zoom slider. What I mean is, if the view can only be zoomed from, let's say, 1-2, the entire slider should slide between the min (1) and the max (2).
private runLayout() {
this.cy.minZoom( 1e-50 );
const layout = this.cy.layout( DATA_FLOW_LAYOUT_CONFIG );
layout.on( 'layoutstop', () => {
this.cy.minZoom( this.cy.zoom() );
this.cy.panzoom( 'destroy' );
this.cy.panzoom( Object.assign(
{ minZoom : this.cy.zoom() },
relateConfig.panzoomDefaults,
) );
} );
This adjusts the zoom limits, but also just restricts the amount of movement the slider has, it doesn't recalculate the min and max slider positions.
Does this make sense? Any thoughts?
Ex:
Initial slider movement range -
[|----------[]----------|]
View changed, bounds limited -
[--|--[]---|-------------]
The problem here is with Object.assign.
this.cy.panzoom( Object.assign(
{ minZoom : this.cy.zoom() },
relateConfig.panzoomDefaults,
) );
If you keep the defaults for panZoom stored somewhere (relateConfig.panzoomDefaults in my case), they won't get overwritten with Object.assign. Removing the values I wanted to be able to change immediately allowed me to change them as needed using Object.assign.
Related
So in my react native, I have a spinner which I am using to enter numbers. It has two buttons which increases or decreases a value. But the problem is that I have to set the value to a state and I have multiple elements. So if I change the value of one element, everything else changes too.
Here is the Package
And here is a sample code I am working with:
this.state = {
qty: null,
}
<InputSpinner
max={50}
min={1}
step={1}
width={100}
height={50}
colorMax={"#2a292d"}
colorMin={"#2a292d"}
buttonFontSize={13}
value={this.state.qty}
onChange={(num) => {
this.setState({qty: num});
}}/>
So on change I am settings the qty state. But I have multiple spinners and changing one changes everything because each uses the same state. What would be the better solution for this? Should I use an array to store each item qty?
For me the better solution is assign at each Spinner an ID and then create an object with key = spinnerID and value = num
this.state = {
qty: {},
}
<InputSpinner
max={50}
min={1}
step={1}
width={100}
height={50}
colorMax={"#2a292d"}
colorMin={"#2a292d"}
buttonFontSize={13}
value={this.state.qty['1'] || 1}
onChange={(num) => {
let qty = Object.assign({}, this.state.qty);
qty['1'] = num;
this.setState({qty});
}}/>
yes, obviously you need to maintain multiple states for each spinner, never use one state for that. I would recommend to use an array like
`
this.state = {
spinnerValues:[]
}
`
and onChange of that input spinner you can do somewhat like
`
onChange={(num) => {
let currentState = this.state.spinnerValues;
currentState[i] = num; // here i is the index which you will provide for the spinner num
this.setState({spinnerValues: currentState});
`
and for value of each spinner
value = {this.state.spinnerValues[i]}
Is it possible to configure this to display a vertical tree instead of a horizontal tree? What I mean by vertical is something similar to Windows Explorer.
We have some users that would prefer to work with it that way.
The dagre layout extension has a github page with some useful default values for the layout:
var defaults = {
nodeSep: undefined, // the separation between adjacent nodes in the same rank
edgeSep: undefined, // the separation between adjacent edges in the same rank
rankSep: undefined, // the separation between adjacent nodes in the same rank
rankDir: undefined // 'TB' for top to bottom flow, 'LR' for left to right,
ranker: undefined, // Type of algorithm to assign a rank to each node in the input graph. Possible values: 'network-simplex', 'tight-tree' or 'longest-path'
minLen: function( edge ){ return 1; }, // number of ranks to keep between the source and target of the edge
edgeWeight: function( edge ){ return 1; }, // higher weight edges are generally made shorter and straighter than lower weight edges
// general layout options
fit: true, // whether to fit to viewport
padding: 30, // fit padding
spacingFactor: undefined, // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
nodeDimensionsIncludeLabels: false, // whether labels should be included in determining the space used by a node
animate: false, // whether to transition the node positions
animateFilter: function( node, i ){ return true; }, // whether to animate specific nodes when animation is on; non-animated nodes immediately go to their final positions
animationDuration: 500, // duration of animation in ms if enabled
animationEasing: undefined, // easing of animation if enabled
boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
transform: function( node, pos ){ return pos; }, // a function that applies a transform to the final node position
ready: function(){}, // on layoutready
stop: function(){} // on layoutstop
}
The important part here is the rankDir, you can define LR there :)
I am using React Native for a game (perhaps not the best framework but not the point). I need user to be able to slide on a grid. On the pic, user is selecting MKEC without removing its finger from screen.
I am from the web, so I thought I would use onHover event. But it dossn't exist on mobile. So I created a parent component and a grid component. Parent component catch event with PanResponder, and I calculate where user finger is on the grid, and change state of the grid component.
However, if user is going too fast from on letter to another, it sometimes 'skip' a letter. If you go from M to G (first column) very fast, it seems that not event is triggered on H. Am I clear ? User feels latency.
componentWillMount() {
const onRelease = (evt, gestureState) => {
this.props.validateWord();
};
const onMove = (evt, gestureState: GestureState) => {
const obj = Object.assign({}, {
pageX: evt.nativeEvent.pageX,
pageY: evt.nativeEvent.pageY
});
const pos = {
x: evt.nativeEvent.pageX - this.position.pageX,
y: evt.nativeEvent.pageY - this.position.pageY
}
const whatSquare = whatSquareIsTouched(
pos,
defaultLetter.width, // size of letter
defaultLetter.width * 0.22 // padding for diagonale
);
const letter = this.props.gridReducer[whatSquare.y]
&& this.props.gridReducer[whatSquare.y][whatSquare.x] ?
this.props.gridReducer[whatSquare.y][whatSquare.x] : null;
// if letter => if touch is on a letter and not outside of grid comp
if (letter) {
this.props.letterTouched(letter, whatSquare.insidePadding);
}
}
this._panResponder = createResponder(onMove, onRelease);
}
How could I improve my code ? Or perhaps go another way to tackle this problem?
Update after comment :
I have not solved the problem yet. But i tried the following. I assumed that when user change direction, a new event is trigger. So I could calculate the finger path. If user starts at M and finish in G without changing direction, then H has been hovered.
I want to limit map extent to the initial extent of the map and limit user from panning more than certain extent.
I tried following but nothing has changed:
map = new Map( "map" , {
basemap: "gray",
center: [-85.416, 49.000],
zoom : 6,
logo: false,
sliderStyle: "small"
});
dojo.connect(map, "onExtentChange", function (){
var initExtent = map.extent;
var extent = map.extent.getCenter();
if(initExtent.contains(extent)){}
else{map.setExtent(initExtent)}
});
Just to flesh out Simon's answer somewhat, and give an example. Ideally you need two variables at the same scope as map:
initExtent to store the boundary of your valid extent, and
validExtent to store the last valid extent found while panning, so that you can bounce back to it.
I've used the newer dojo.on event syntax as well for this example, it's probably a good idea to move to this as per the documentation's recommendation - I assume ESRI will discontinue the older style at some point.
var map;
var validExtent;
var initExtent;
[...]
require(['dojo/on'], function(on) {
on(map, 'pan', function(evt) {
if ( !initExtent.contains(evt.extent) ) {
console.log('Outside bounds!');
} else {
console.log('Updated extent');
validExtent = evt.extent;
}
});
on(map, 'pan-end', function(evt) {
if ( !initExtent.contains(evt.extent) ) {
map.setExtent(validExtent);
}
});
});
You can do the same with the zoom events, or use extent-change if you want to trap everything. Up to you.
It looks like your extent changed function is setting the initial extent variable to the maps current extent and then checking if that extent contains the current extents centre point - which of course it always will.
Instead, declare initExtent at the same scope of the map variable. Then, change the on load event to set this global scope variable rather than a local variable. In the extent changed function, don't update the value of initExtent, simply check the initExtent contains the entire of the current extent.
Alternatively you could compare each bound of the current extent to each bound of the initExtent, e.g. is initExtent.xmin < map.extent.xmin and if any are, create a new extent setting any exceeded bounds to the initExtent values.
The only problem is these techniques will allow the initExtent to be exceeded briefly, but will then snap the extent back once the extent changed function fires and catches up.
I originally posted this solution on gis.stackexchange in answer to this question: https://gis.stackexchange.com/a/199366
Here's a code sample from that post:
//This function limits the extent of the map to prevent users from scrolling
//far away from the initial extent.
function limitMapExtent(map) {
var initialExtent = map.extent;
map.on('extent-change', function(event) {
//If the map has moved to the point where it's center is
//outside the initial boundaries, then move it back to the
//edge where it moved out
var currentCenter = map.extent.getCenter();
if (!initialExtent.contains(currentCenter) &&
event.delta.x !== 0 && event.delta.y !== 0) {
var newCenter = map.extent.getCenter();
//check each side of the initial extent and if the
//current center is outside that extent,
//set the new center to be on the edge that it went out on
if (currentCenter.x < initialExtent.xmin) {
newCenter.x = initialExtent.xmin;
}
if (currentCenter.x > initialExtent.xmax) {
newCenter.x = initialExtent.xmax;
}
if (currentCenter.y < initialExtent.ymin) {
newCenter.y = initialExtent.ymin;
}
if (currentCenter.y > initialExtent.ymax) {
newCenter.y = initialExtent.ymax;
}
map.centerAt(newCenter);
}
});
}
And here's a working jsFiddle example: http://jsfiddle.net/sirhcybe/aL1p24xy/
Does anyone know how I can ensure only the cuboid animation is used on my slider? I don't particularly love the slicing action but do like the rotating cube options both left to right and top to bottom. I have checked the docs but can only see how to change orientation of the slicing animation or set to random.
The JS settings referred to in the docs are here:
JS
$.Slicebox.defaults = {
// (v)ertical, (h)orizontal or (r)andom
orientation : 'h',
// perspective value
perspective : 1200,
// number of slices / cuboids
// needs to be an odd number 15 => number > 0 (if you want the limit higher, change the _validate function).
cuboidsCount : 5,
// if true then the number of slices / cuboids is going to be random (cuboidsCount is overwitten)
cuboidsRandom : false,
// the range of possible number of cuboids if cuboidsRandom is true
// it is strongly recommended that you do not set a very large number :)
maxCuboidsCount : 5,
// each cuboid will move x pixels left / top (depending on orientation). The middle cuboid doesn't move. the middle cuboid's neighbors will move disperseFactor pixels
disperseFactor : 0,
// color of the hidden sides
colorHiddenSides : '#222',
// the animation will start from left to right. The left most cuboid will be the first one to rotate
// this is the interval between each rotation in ms
sequentialFactor : 150,
// animation speed
// this is the speed that takes "1" cuboid to rotate
speed : 600,
// transition easing
easing : 'ease',
// if true the slicebox will start the animation automatically
autoplay : true,
// time (ms) between each rotation, if autoplay is true
interval: 3000,
// the fallback will just fade out / fade in the items
// this is the time fr the fade effect
fallbackFadeSpeed : 300,
// callbacks
onBeforeChange : function( position ) { return false; },
onAfterChange : function( position ) { return false; },
onReady : function() { return false; }
};
I haven't copied all the js code as there's lots!
You just need to change number of slices to 1
cuboidsCount : 1