For other jquery interactions/widgets there are step values.
How might I go about forcing a resize to increase/decrease to certain values?
Must I do something like this?
(side issue, for a more effective hack, how can I know which compass point is being resized?)
var desiredStepPixels = 8;
var containmentLeftPos = 100;
var containmentTopPos = 250;
$("#resizeDivID").resizable({
resize: function( event, ui ) {
var newLeftPxValue = $("#resizeDivID").css("left");
var leftPxModulus = (newLeftPxValue - containmentLeftPos ) % desiredStepPixels;
if ( leftPxModulus != 0){
if (leftPxModulus>(desiredStepPixels/2)){
var forcedNewLeftPx = newLeftPxValue + (desiredStepPixels - leftPxModulus);
$("#resizeDivID").css({"left":forcedNewLeftPx});
}else{
//force a round down on the left side
}
}
//and then etc etc for top, width and height!
}
});
My bad, apologies. Of course Jquery have thought of this!
it's called GRID
http://api.jqueryui.com/resizable/#option-grid
I apologise, I had checked the documentation but just didn=t see what I was looking for!
Related
I have points generated one by one, and when a new point is generated, I want to draw a line segment connecting with the previous point. Like this:
var x by remember { mutableStateOf( 0.0f)}
var y by remember { mutableStateOf( 0.5f)}
var pStart by remember { mutableStateOf(Offset(0f, 0.5f))}
Canvas(modifier = Modifier.fillMaxSize()) {
canvasWidth = size.width
canvasHeight = size.height
val pEnd = Offset(x * canvasWidth, (1-y) * canvasHeight)
val col = if (pEnd.y < pStart.y) Color.Green else Color.Red
drawLine(
start = pStart,
end = pEnd,
strokeWidth = 4f,
color = col
)
pStart = pEnd
}
But this only draws the segment in a flash and no segments stay on the screen.
I know I can save the points to a list and redraw all the segments whenever a new point is added. But I just hope to economize. Is it possible?
There's no practical other way. You COULD in fact, keep track of just two points, adding a whole new Canvas (all Transparent and Filling the maximum Size, stacked on top of one another), for each extra point that is added. This does seem a bit impractical, but maybe try it out and do some benchmarking to see which one checks out. This is the only OTHER way I could think of, where you do not have to store all the points and recompose every time a point is added, since all the other lines would technically be frozen in space.
In response to the somewhat (unreasonably) aggressive comment below, here's some sample code. I assume you have a stream of new points coming in so a LiveData object is assumed to be the source of that, which I shall be converting to a MutableState<T> for my use-case.
var latestPoint by liveData.collectAsState()
var recordedPoint by remember { mutableStateOf(latestPoint) }
var triggerDraw by remember { mutableStateOf(false) }
var canvasList = mutableStateListOf<#Composable () -> Unit>Canvas>() // Canvas is the Composable
if(triggerDraw){
canvasList.add(
Canvas(){
/* you have the recordedPoint, and latestPoint, simply draw a line here */
}
)
triggerDraw = false
}
LaunchedEffect(latestPoint){
triggerDraw = true
}
canvasList.forEach {
it() // Invoke the Composable here
}
Thanks Dad!
I'm using the NVD3 library to draw a graph. I am using the interactive guideline and for some reason after I update the chart data + graph, the tooltip pointers stay at the old position.
When I update the data I do this:
chartData.datum(data).call(chart);
Everything updates fine except the position of the pointers of the tooltip. They seem to get stuck at the old position. I also tried calling this:
chart.update();
I noticed that when resizing my window and calling chart.update(), the pointers are set to the right position.
Someone any idea how to fix this?
Found it! I apparently had to add transition().duration(500);
So in order to update the graph I call this:
chartData.datum(data)
.transition().duration(500)
.call(chart);
Can you post a Little More description about the problem ... Becuase Previously i was having the Same .. that override the nvd3 tooltip function
`this._nvctp = nv.tooltip.calcTooltipPosition;
nv.tooltip.calcTooltipPosition = this.calcTooltipPosition;
calcTooltipPosition : function() {
this.findTotalOffsetTop = function(a, b) {
return 0;
};
this.findTotalOffsetLeft = function(a, b) {
return 0;
};
arguments[0] = [ window.event.pageX, window.event.pageY ];
var p = nvk.tooltip._nvctp.apply(this, arguments);
p.style.left = (window.event.clientX - (p.clientWidth / 2))
+ 'px';
p.style.top = (window.event.clientY - (p.clientHeight * 1.2))
+ 'px';
p.style.opacity = 1;
p.style.position = 'fixed';
return p;
}
I have two styles of interactions, one highlights the feature, the second places a tooltop with the feature name. Commenting both out, they're very fast, leave either in, the map application slows in IE and Firefox (but not Chrome).
map.addInteraction(new ol.interaction.Select({
condition: ol.events.condition.pointerMove,
layers: [stationLayer],
style: null // this is actually a style function but even as null it slows
}));
$(map.getViewport()).on('mousemove', function(evt) {
if(!dragging) {
var pixel = map.getEventPixel(evt.originalEvent);
var feature = null;
// this block directly below is the offending function, comment it out and it works fine
map.forEachFeatureAtPixel(pixel, function(f, l) {
if(f.get("type") === "station") {
feature = f;
}
});
// commenting out just below (getting the feature but doing nothing with it, still slow
if(feature) {
target.css("cursor", "pointer");
$("#FeatureTooltip").html(feature.get("name"))
.css({
top: pixel[1]-10,
left: pixel[0]+15
}).show();
} else {
target.css("cursor", "");
$("#FeatureTooltip").hide();
}
}
});
I mean this seems like an issue with OpenLayers-3 but I just wanted to be sure I wasn't overlooking something else here.
Oh yeah, there's roughly 600+ points. Which is a lot, but not unreasonably so I would think. Zooming-in to limit the features in view definitely helps. So I guess this is a # of features issue.
This is a known bug and needs more investigation. You can track progress here: https://github.com/openlayers/ol3/issues/4232.
However, there is one thing you can do to make things faster: return a truthy value from map.forEachFeatureAtPixel to stop checking for features once one was found:
var feature = map.forEachFeatureAtPixel(pixel, function(f) {
if (f.get('type') == 'station') {
return feature;
}
});
i had same issue, solved a problem by setInterval, about this later
1) every mouse move to 1 pixel fires event, and you will have a quee of event till you stop moving, and the quee will run in calback function, and freezes
2) if you have an objects with difficult styles, all element shown in canvas will take time to calculate for if they hit the cursor
resolve:
1. use setInterval
2. check for pixels moved size from preview, if less than N, return
3. for layers where multiple styles, try to simplify them by dividing into multiple ones, and let only one layer by interactive for cursor move
function mouseMove(evt) {
clearTimeout(mm.sheduled);
function squareDist(coord1, coord2) {
var dx = coord1[0] - coord2[0];
var dy = coord1[1] - coord2[1];
return dx * dx + dy * dy;
}
if (mm.isActive === false) {
map.unByKey(mm.listener);
return;
}
//shedules FIFO, last pixel processed after 200msec last process
const elapsed = (performance.now() - mm.finishTime);
const pixel = evt.pixel;
const distance = squareDist(mm.lastP, pixel);
if (distance > 0) {
mm.lastP = pixel;
mm.finishTime = performance.now();
mm.sheduled = setTimeout(function () {
mouseMove(evt);
}, MIN_ELAPSE_MSEC);
return;
} else if (elapsed < MIN_ELAPSE_MSEC || mm.working === true) {
// console.log(`distance = ${distance} and elapsed = ${elapsed} mesc , it never should happen`);
mm.sheduled = setTimeout(function () {
mouseMove(evt);
}, MIN_ELAPSE_MSEC);
return;
}
//while multithreading is not working on browsers, this flag is unusable
mm.working = true;
let t = performance.now();
//region drag map
const vStyle = map.getViewport().style;
vStyle.cursor = 'default';
if (evt.dragging) {
vStyle.cursor = 'grabbing';
}//endregion
else {
//todo replace calback with cursor=wait,cursor=busy
UtGeo.doInCallback(function () {
checkPixel(pixel);
});
}
mm.finishTime = performance.now();
mm.working = false;
console.log('mm finished', performance.now() - t);
}
In addition to #ahocevar's answer, a possible optimization for you is to utilize the select interaction's select event.
It appears that both the select interaction and your mousemove listener are both checking for hits on the same layers, doing double work. The select interaction will trigger select events whenever the set of selected features changes. You could listen to it, and show the popup whenever some feature is selected and hide it when not.
This should reduce the work by half, assuming that forEachFeatureAtPixel is what's hogging the system.
I've tried various web searches, but I can't seem to find anything that relates to my problem.
To quickly delineate the problem:
HTML5 Cordova iOS app (7.1 -> 8.1)
uses draggable elements
I only have issues on the iPad, not on the iPhone
The HTML5 app itself works flawlessly in a web-browser
The app itself is a biology app that teaches translation - decoding RNA into a amino acid sequence, i.e. a protein.
For this, the user sees the sequence and drags the correct amino acid onto it. The amino acid is a draggable element and the target div is a droppable. One amino acid at a time a chain is built. Please refer to the screenshot to get an idea (can't embed yet).
http://i.stack.imgur.com/S4UpF.png
In order to fit all screens, I "transform: scale" the app accordingly (fixed size is ~850x550). And to get rid of the associated jQuery bug with draggable (object movement would also change with the scaling factor), I've followed the instructions at http://gungfoo.wordpress.com/2013/02/15/jquery-ui-resizabledraggable-with-transform-scale-set/
// scaling to fit viewport
// sizing the page
var myPage = $('.page');
var pageWidth=myPage.width();
var pageHeight=myPage.height();
// sizing the iFrame
var myFrame = $('.container');
var frameWidth=myFrame.width();
var frameHeight=myFrame.height();
// scaleFactor horizontal
var horizontalScale=pageWidth/frameWidth;
// scaleFactor vertiacal
var verticalScale=pageHeight/frameHeight;
// global zoomScale variable
var zoomScale = 1; // default, required for draggable debug
// if page fits vertically - scale horizontally
if ((frameHeight * horizontalScale) <= pageHeight) {
myFrame.css({
'transform': 'scale('+horizontalScale+')',
'transform-origin': 'top',
});
// adding vertical margin, if possible
if (pageHeight > frameHeight*horizontalScale) {
var heightDifference = pageHeight - frameHeight*horizontalScale;
myPage.css({
'margin-top': heightDifference/2,
'height': pageHeight - heightDifference/2,
});
}
zoomScale = horizontalScale;
// else scale vertically
} else {
myFrame.css({
'transform': 'scale('+verticalScale+')',
'transform-origin': 'top',
});
zoomScale = verticalScale;
}
// draggable + scale transform fixes (http://gungfoo.wordpress.com/2013/02/15/jquery-ui-resizabledraggable-with-transform-scale-set/)
function startFix(event, ui) {
ui.position.left = 0;
ui.position.top = 0;
}
function dragFix(event, ui) {
var changeLeft = ui.position.left - ui.originalPosition.left; // find change in left
var newLeft = ui.originalPosition.left + changeLeft / zoomScale; // adjust new left by our zoomScale
var changeTop = ui.position.top - ui.originalPosition.top; // find change in top
var newTop = ui.originalPosition.top + changeTop / zoomScale; // adjust new top by our zoomScale
ui.position.left = newLeft;
ui.position.top = newTop;
}
I've already got a beta version on iTunes connect and it works great on an iPhone. On iPads, however, the droppable area is oddly small and shifted. That is, the div seems to be properly rendered - it is the box with the dashed border.
Has anyone else encountered a similar bug? I really have no idea how to fix it.
I have managed to solve the problem.
The issue was probably based on the viewport meta tag (0.5 scaling) interacting badly with the transform:scale resizing.
Simply removing all viewport meta arguments have solved the problem.
I'm using the I'm currently using the tooltip formatter function to control the look of the tooltip, including adding some custom css to create an arrow on the side of the tooltip box facing the mouse. I am also using the positioner callback to not only determine the placement of the tooltip, but when it changes from one side of the mouse to the other I'm updating the formatter callback to switch the side of the tooltip the arrow lies on (see code below). Everything works fine, except for the very first point which causes the tooltip to switch sides of the mouse. Its clear that a tooltip's "formatter" function is called before the tooltips "positioner" function ( for reasons that are probably obvious ). However, it prevents me from correctly drawing the tooltip when it changes sides. What I really need to be able to do in the positioner callback is to update the formatter function, and then redraw the tooltip. Is that possible?
positioner: function (boxWidth, boxHeight, point) {
// Set up the variables
var chart = this.chart;
var plotLeft = chart.plotLeft;
var plotTop = chart.plotTop;
var plotWidth = chart.plotWidth;
var plotHeight = chart.plotHeight;
var distance = 40;
var pointX = point.plotX;
var pointY = point.plotY;
// Determine if we need to flip the tooltip from following on the left to
// following on the right
if ((pointX - boxWidth - distance) < plotLeft) {
x = pointX + distance;
chart.tooltip.options.formatter = function () {
// UPATE THE TOOLTIP FORMATTER HERE
};
}
}
Here is a js fiddle example of the issue
http://jsfiddle.net/bteWs/
If you go slowly, and notice the very first point where the switch happens the arrow will be point the wrong direction. After that it corrects ( as described in my post ). Just looking for a solution to get the correct behavior in this case.
You can always have enabled both classes for tooltip, and just remove inproper in positioner, see: http://jsfiddle.net/bteWs/3/
Default formatter:
formatter: function () {
var s = '<div id="custom-tooltip" class="tooltip-left tooltip-right">';
var chart = null;
s += "<div style='padding:5px;color:white'>Some Tooltip</div></div>";
return s;
},
And removing:
if ((pointX - boxWidth - distance) < plotLeft) {
x = pointX + 60;
$("#custom-tooltip").removeClass('tooltip-right');
}
else {
x = Math.max(plotLeft, pointX - boxWidth - 10);
$("#custom-tooltip").removeClass('tooltip-left');
}
I had the same issue. The problem is that positioner is begin called after the formatter. I made a fix to your jsfiddle. It's a huge hack but you can see how you can overcome your problem.
In the fix, I made use of a global. You don't need to but I hacked your fiddle in a hurry. I also got rid of some of your duplicate code.
The trick is to force a refresh on the tooltip after the tooltip switch sides.
positioner: function (boxWidth, boxHeight, point) {
// Set up the variables
var chart = this.chart;
var plotLeft = chart.plotLeft;
var plotTop = chart.plotTop;
var plotWidth = chart.plotWidth;
var plotHeight = chart.plotHeight;
var distance = 40;
var pointX = point.plotX;
var pointY = point.plotY;
var refreshTooltip = false;
var previousAlign = chart.align;
if ((pointX - boxWidth - distance) < plotLeft) {
x = pointX + 60;
chart.align = "left";
}
else {
x = Math.max(plotLeft, pointX - boxWidth - 10);
chart.align = "right";
}
y = Math.min(plotTop + plotHeight - boxHeight, Math.max(plotTop, pointY - boxHeight + plotTop + boxHeight / 2));
if (previousAlign != null && chart.align != previousAlign) {
chart.tooltip.refresh(activePoint);
}
return { x: x, y: y };
}
See the complete fiddle here:
http://jsfiddle.net/anber500/bteWs/1/