I've been trying to make a simple input element to search for id's and focus on specific vertexes, as shown below:
Screen before Searching
And the code seems to be working with this simple method:
function searchForNode() {
const id = document.getElementById("input-search-id").value;
const { vertex } = findById(id);
const point = new mxPoint(-(vertex.geometry.x), -(vertex.geometry.y));
graph.view.translate = point;
}
The problem starts after the first search, where my top left screen focuses on the desired vertex but from there on I'm enabled to scroll up or left.
Screen after Searching
Also, if I scroll before searching for anything, it seems that my graph.view.translate doesn't update to my current position, so, when I search for a vertex it focuses on a different location instead of the expected vertex.
Is there any method from MxGraph Library that I'm missing? I've tried graph.getView().setTranslate(translate) and graph.panGraph(translate) but the behaviour is the same.
Thank you in advance
So, I found a solution for this problem but not in the most convenient way.
For what I understood, the native scrollbars and the mxGraph panning are different approaches to the display movement, when you scroll you aren't necessarily updating your mxGraph position but when you pan with the mxGraph library you are dragging all the cells along the screen, instead of "scrolling" to them.
I disabled the mxGraph panning option and simply used the javascript native method scrollTo, as show below, I hope it helps.
function searchForNode() {
const inputValue = document.getElementById("input-search-id").value;
const { vertex } = flowMap.findNodeByIdOrName(inputValue);
if( !vertex ) return null;
window.scrollTo({
top: vertex.geometry.y - MARGIN_TO_CENTER,
left: vertex.geometry.x - MARGIN_TO_CENTER,
behavior: 'smooth'
});
}
*The flowMap.findNodeByIdOrName on the last image and the findById(id) on the question post are my own methods to search on my array of vertexes.
Related
Some answers of our chatbot are very long. The webchat scrolls automatically to the bottom so users have to scroll up to get to the top of the bubble and start reading.
I've implemented a custom renderer (react) to wrap the answers into a custom component which simply wraps the answer into a div-tag. I also implemented a simple piece of code to scroll to the top of the bubble.
const MyCustomActivityContainer = ({ children }) => {
const triggerScrollTo = () => {
if (scrollRef && scrollRef.current) {
(scrollRef.current as any).scrollIntoView({
behavior: 'smooth',
block: 'start',
})
}
}
const scrollRef: React.RefObject<HTMLDivElement> = React.createRef()
return (
<div ref={ scrollRef } onClick={ triggerScrollTo }>
{ children }
</div>
)
}
export const activityMiddleware = () => next => card => {
if (/* some conditions */) {
return (
<MyCustomActivityContainer>
{ next(card) }
</MyCustomActivityContainer>
);
} else {
return (
{ next(card) }
)
}
};
But this only works if the scrollbar slider is not at its lowest position (there is at least 1 pixel left to scroll down, see here). The problem is the useScrollToBottom hook which always scrolls to bottom automatically if the scrollbar is completely scrolled down.
Is there any way to overwrite the scroll behavior or to temporarily disable the scrollToBottom feature?
As there is no reproducible example I can only guess.
And I'll have to make some guesses on the question too.
Because it's not clear what exactly in not working:
Do you mean that click on the <div> of MyCustomActivityContainer and subsequent call to triggerScrollTo doesn't result into a scroll?
That would be strange, but who knows. In this case I doubt anyone will help you without reproducible example.
Or do you mean that you can scroll the message into view, but if it is already in the view then new messages can result into a scroll while user is still reading a message.
That's so, but it contradicts with you statement that your messages are very long, because that would be the problem with short messages, not with the long ones.
But anyway, you should be able to fix that.
If it works fine with 1 pixel off the lowest position, then just scroll that 1 pixel. You'll need to find the scrollable element. And do scrollable_element.scrollTop -= 1. I tested this approach here. And it worked (there the scrollable element is the grandparent of <p>'s)
Or do you try to scroll automatically at the moment the message arrives? Аnd that is the real issue, but you forgot to mention it, and didn't posted the code that tries to auto-scroll?
In that case you can try to use setTimeout() and defer the scroll by, let's say, 200ms.
This number in based on what I gathered from the source:
BotFramework-WebChat uses react-scroll-to-bottom
In react-scroll-to-bottom there are some timeouts 100ms and 34ms
BotFramework-WebChat doesn't redefine them
There are some heuristics in react-scroll-to-bottom that probably coursing the trouble
https://github.com/compulim/react-scroll-to-bottom/blob/3eb21bc469ee5f5095a431ac584be29a0d2da950/packages/component/src/ScrollToBottom/Composer.js
Currently, there are no reliable way to check if the "scroll" event is trigger due to user gesture, programmatic scrolling, or Chrome-synthesized "scroll" event to compensate size change. Thus, we use our best-effort to guess if it is triggered by user gesture, and disable sticky if it is heading towards the start direction.
And
https://github.com/compulim/react-scroll-to-bottom/blob/f19b14d6db63dcb07ffa45b4433e72284a9d53b6/packages/component/src/ScrollToBottom/Composer.js#L91
For what we observed, #1 is fired about 20ms before #2. There is a chance that this stickyCheckTimeout is being scheduled between 1 and 2. That means, if we just look at #1 to decide if we should scroll, we will always scroll, in oppose to the user's intention.
That's why I think you should use setTimeout()
Since there isn't a reproducible code for me tweak and show you. My suggestion is tweak your code slightly. Chatbot requires constant streaming of data when a new message arrives calculate the height of the div element created for the message. If the div element is greater than the widget height scroll to the top else you can choose to leave it as it is.
For example, if the sprite is in the middle of the screen and you press somewhere around the lower right of the screen and move 4 units to the left, the sprite will also move 4 units to the left with respect to its current position. So basically, input.x is not necessarily sprite.x. I hoe you can help. Thanks!
If it wasn't explained well, you can check the app Ballz Rush and see how the control works. Thank you very much!
Basically instead of making the sprite movable, you want to instead check to see if the active pointer is in the state you want to track (in this case, down).
One way to do this is in your update() check and see if the pointer is down, and if it is, move the sprite based upon what the pointer is doing.
function update() {
// This depends upon pointerOrigin (Phaser.Point) being defined. You'll also need to update 'player' to match your specific needs.
if (this.game.input.activePointer.isDown) {
if (this.pointerOrigin) {
this.player.position.x += this.game.input.activePointer.position.x - this.pointerOrigin.x;
this.player.position.y += this.game.input.activePointer.position.y - this.pointerOrigin.y;
}
// Keep track of where the pointer has been so we can use it for the next update cycle.
this.pointerOrigin = this.game.input.activePointer.position.clone();
}
else {
this.pointerOrigin = null;
}
}
The clone() method is available off of the Phaser.Point type, and creates a copy to make sure we're not keeping a reference to the active pointer's position, since we want a snapshot in time.
jQuery documentation for offset() states:
Also, dimensions may be incorrect when the page is zoomed by the user; browsers do not expose an API to detect this condition.
However, is there a way I could calculate the correct offset in browsers in touch environment when using spread to zoom in the contents of a page?
I created a small sample demonstrating the issue: https://jsfiddle.net/dhykgsmp/4/ (open in Chrome). Please scroll down and click zoom in. The offset of the autocomplete input is wrong.
Thank you.
I had the same problem and found a workaround.
You need a root child element with zero offsets to make this work.
$.fn.oldOffset = $.fn.offset;
$.fn.offset = function () {
var c = $.fn.oldOffset.apply(this, arguments);
// root child element with zero offset
var wrc = $('body').children().first().oldOffset();
var needToFix = wrc.left > 0 || wrc.top > 0;
if (needToFix) {
return {
left: c.left - wrc.left,
top: c.top - wrc.top
};
} else {
return c;
}
}
I'm not sure what the intended functionality of this code is, but if you'd like the 'autocomplete input' element to be positioned relative to the 'autocomplete container' I would suggest using the .style.top attribute, or getting the location with Element.getBoundingClientRect() and setting the position accordingly in your positionDropdown() function.
I am teaching myself web programming. I'm also working on C++. I am having a problem with Javascript.
I need to know how to create an "if statement" based on the location of an image.
I am making a simple game that has a cannon that is moving back and forth. I want the user to press a button that will cause the cannon to stop and fire launching another image toward the target.
I have already created the images and a gif of the image that will travel from the cannon in an arc toward the target.
If the user fires when the cannon is in the correct position the image will hit the target.
Can someone tell me how to create an if statement based on position? Or can someone tell me how to make the cannon stop and make the gif appear?
To move the cannon, read up on the onkeyup() event - it will wait for when a key is released, and do something.
What will it do? Probably change the left position of the cannon somehow. I'd recommend setting the CSS style position:absolute on your cannon, then changing the .left property with Javascript.
For example, here's some Javascript to get you started (untested):
var cannon = document.getElementById("cannonPic");
var leftlim = 200;
document.body.onkeyup = function() {
// Remove 'px' from left position style
leftPosition = cannon.style.left.substring(0, cannon.style.left - 2);
// Stop the cannon?
if (leftPosition >= leftLim) {
return;
}
// Move cannon to new position
cannon.style.left = cannon.style.left.substr(0, cannon + 10);
}
And its companion HTML would look like...
...
<img id='cannonPic' src='cannon.jpg' />
...
<script type='text/javascript' src='cannon.js' />
The HTML could be styled like this:
#cannonPic {
left:0;
position:absolute;
}
To answer your "appear/reappear" sub-question, you can use the .display property, accessed via Javascript:
var cannon = document.getElementById("cannonPic");
function appear() {
cannon.style.display = '';
}
function hide() {
cannon.style.display = 'none';
}
A small word of warning, things traveling in arcs will require some math to translate them in two dimensions, depending on how accurate you want it. A fun exercise though if you like math :)
To get the very first image on your page's x and y position on the screen, for instance, try:
var xpos = document.getElementsByTagName('img')[0].x;
var ypos = document.getElementsByTagName('img')[0].y;
Just to add a little background, the way this is typically done:
You have a main loop that will "run" the game.
Each iteration of the loop, you a) update the positions of in-game objects (cannon, projectiles, and targets in your case) and b) render the resulting objects to the screen.
When you detect a "fire" keypress, you simply set the "speed" of your moving cannon to 0, causing it to "stop".
You can retrieve the object's position using Steve's or sajawikio's approach but your game logic determines (and should know) the position of all objects at all times. It is your game logic that says "draw the projectile at position (x,y)". Your game logic should NOT say "I have a projectile, not sure exactly where it is, HMM, so let's query it's position using Javascript". At least not in this case where you have simple, predictable movement.
The error I'm having is the following:
To the right of my page, I have a list of interesting points (displayed on a map to the left of the page).
Because the list is so long, I am using iScroll 4 (great plugin).
However, what I would like to do is fire an event when the user has scrolled the list, so I can check which items are currently visible to the user. iScroll already has an event set up for that, so I have a function that fires when needed. However, I cannot seem to get the proper coordinates of my list items.
What happens is the following:
When the list loads, I get an .offset() value for the first element. offset.top = 35, because my list starts 35px lower than the top edge of the page. This is good.
When I scroll the list; say I pull it down for 500px; I would expect the new location to be 35px - 500px = -465px. However, it sill says 35. Scrolling though the list does not affect the elemen'ts coordinates even though it has moved.
How can I get the actual coordinates?
Playing with iScroll, I think I found the solution. the offset() function is javascript refering to the #scroller div, which doesn't change. However internally, iScroll uses its own x and y properties.
So you can refer to the iScroll offset like this
var myScroll = new iScroll(...)
alert(myScroll.y) // -- will return your offset (negative number)
add in iscroll.js
getScrollY: function () { var that = this; return that.y; },