Disabling map animations when the zoom is changed via the ZoomBar component - animation

Is there a way to stop animations when the zoom is changed via the ZoomBar component?
I tried setAttributes('none', undefined, undefined, undefined, undefined) but that doesn't appear to make any difference. I'm verifying that animations are occurring by monitoring the transitionstart and transitionend events.
Alternatively, is there a way to stop getting fractional zooms reported in the callback set by addObserver("zoomLevel"...)
The animation and fractional zoom is new behavior introduced in 2.5.3 which breaks my app.

The easiest way to obtain integer zoomLevels is to use the Math.floor() function. Something like:
map.addObserver('zoomLevel', function () {
if (map.zoomLevel === Math.floor(map.zoomLevel){
// doSomething
}
});
An example of this can be found on the HERE Maps Github Pages
However observing zoomLevel like this is very inefficient, since the value is still changing through a multitude of fractional values and firing many times whilst updating. It would be much better to listen for the mapviewchangeend event instead:
map.addListener('mapviewchangeend', function (evt) {
// doSomething
}, false);

Related

Laravel + InertiaJS + Vue 3: Play transition on page BEFORE navigation with Link, button, etc

TL;DR: In a Laravel + InertiaJS + Vue 3 developed application, how can I achieve to have a transition between "pages" loaded within a persistent layout <main> section - for example, have that section animate (say, fade out) before loading the next page, then animate (fade) that new page in - when using standard Inertia routing for navigation? I have managed to do it on entering/showing the page, but have found no way to animate before navigation happens.
LONG(ish): The Way I'm trying to do it
Let's assume there is an application (developed with Laravel + InertiaJS + Vue 3).
I have an element in the markup of an Inertia persistent layout that is conditionally shown if a value is true (v-if="shouldAnimate") that is initially set to false when declared, and when onMounted fires, it sets that value to true which in turn triggers the animation to run (doesn't really matter how the animation works, but just in case, I have options to use either GSAP or anime.js).
Up to this point, all is good: every time I navigate to a page (using Inertia-adequate methods such as the Link component) the animation triggers and I am a happy guy.
BUT: I would very much like to be able to play another animation (the reverse of the previous one) before, say, navigation to the next page occurs. I have tried almost everything I can think of and have not been successful. Here's what got the closest to what I need:
I tried hooking into the InertiaJS event Inertia.on('before', ...): effectively, the event fires up right before navigation (checked with some good-old console log), so I tried firing up the animation at this point, only to find out that the Inertia event looks like it is destroying the page immediately before the animation has had time to play; no problem: I'll just event.preventDefault() it, run the animation and THEN, using a setTimeout timed to the length of the animation (300ms) I'll resume navigation, say, by using Inertia.visit.
Doesn't work. Somehow, the default behaviour is prevented (stops the navigation), the animation plays back, but when it comes to the "resume navigation part" I have had mixed results depending on what I use:
Code looks roughly like this:
let removeListener = Inertia.on('before', (event) => {
event.preventDefault()
// Play animation here
setTimeout(() => {
// SOME INERTIA ACTION DESCRIBED BELOW
}, 300)
})
Independently of whether I use Inertia.get(event.detail.visit.url) or Inertia.visit(event.detail.visit.url) what happens is the animation runs its course, and then the timer runs out and this whole code RUNS AGAIN AND AGAIN in intervals equal to the timer. I also tried to do this using the complete event of the animation to trigger the navigation but it behaves the same.
I know this is related to me being an ignorant about how both Inertia and events work, and I am sure there is a proper (correct? right?) way to achieve what I need, but either I have failed in using the correct terms to look for it, or I am approaching this the wrong way. Hopefully this information is enough to explain my issue.
Any help or pointer would be GREATLY appreciated, so thanks in advance.

Tooltip issue in amchart v4 , not reload on pie chart slice

series.slices.template.events.on("over", function(ev) {
series.slices.template.tooltipHTML=showHtml(ev, series.name);
}, this);
Tooltip issue in amchart4: not reload on pie chart slice.
I am using custom tooltip like generating custom html on mouse hover. Everything is working but when I move cursor from one slice to another, the popup html is not refreshing. When I mouse out and then again mouse over the same slice, the popup html shows correct data.
I think the previous html data is cached somewhere. Please help me.
It could possibly help to see what showHtml is doing. But we'll make do without that.
There are a few problems with this code.
Code-wise, the most obvious is this line:
series.slices.template.tooltipHTML=showHtml(ev, series.name);
You're resetting the template's tooltipHTML instead of the actual slice's tooltipHTML.The template has applyOnClones as true by default so it will propagate to all the other slices, so if this worked it was by accident. If there's any slice-specific settings in showHtml that don't rely on data placeholders this will break tooltipHTML for the next slice that's hovered over.
The real issue, however, is the approach.
So long as tooltipHTML or tooltipText are set, a tooltip will appear on hover.
It's better to use an adapter for tooltipHTML in this case.
As a quick test to see which wins the race condition, the hover event or the tooltipText/HTML adapter, make a handler for each, and hover over a slice:
series.slices.template.events.on("over", function(){
console.log("hover");
});
// override tooltipText so tooltipHTML is actually used
series.slices.template.tooltipHTML = "something...";
series.slices.template.adapter.add("tooltipHTML", function(tooltipHTML) {
console.log("adapter");
return tooltipHTML;
});
// console:
// "adapter"
// "hover"
You'll find that the adapter triggers first, so by the time you hover, the tooltip is already on its way with its HTML and all.
So use an adapter, just be sure to adjust your showHtml function so its first argument takes target itself instead of event (it may not be necessary to have another argument with the tooltipHTML that's being modified because it will always be sent the original tooltipHTML unformatted string, so that could be referred to manually whether as a string or variable).
The adapter can look like this:
var originalTooltipHTML = "<strong>{country}: </strong>"
series.slices.template.tooltipHTML = originalTooltipHTML;
series.slices.template.adapter.add("tooltipHTML", function(tooltipHTML, target) {
// #2: If we had used an event, here you would work on the target itself, event.target, not the template.
// The showHtml might not even be needed, perhaps whatever it does can go in here instead.
return showHtml(target);
});
Here's a demo with all that thrown together:
https://codepen.io/team/amcharts/pen/a0122e572d27cf513a78384345cad3a6

Possible navigation issue in React Native/Redux app

During navigation in big React Native app with using Redux all visited scenes (scenes from navigation stack) are staying mounted. All these scenes receive props and get rendered in the order they were visited when any action is dispatched from last scene component. It causes freezes and visible delays between dispatching and last scene rendering.
For navigation I am using react-native-router-flux but same issue happens with original React Native Navigator too.
Video Possible navigation issues in React Native/Redux app
Code react-redux-navigation-test
Would be nice to know how to prevent passing props to not focused components from the navigation chain.
At the moment I am checking in shouldComponentUpdate of each component if this one is focused(visible) and return false in opposite case.
Is there any better solution?
I recommend that you calculate the scene distance between the current scene and scene being rendered using the navigation index in the scene and header rendering methods. If the distance > 1 return null.
This should prevent rendering the entire navigation history. It doesn't make any sense to me that this kind of behaviour is not available out of the box, but there it is.
I have not used the react-native-router-flux, however I do have experience with a fairly large React Native and Redux app. I have noticed that sometimes if the data you are working with gets large enough it can cause noticeable delays, but those are mostly limited to working in development. When the app is built it is usually noticeably faster. It seems as though Redux does things a little differently in development and production modes.
Regarding scenes in the navigation stack still being mounted, that is by design. Even when you navigator.push to another screen those previous screens remain mounted until they are either popped off the currentRouteStack or replaced from the currentRouteStack.
Also, it's probably worth noting that your simulator is in Slow Animations mode. Not sure if you did that for the video sake or not, but the navigation slowness is in part due to that. Just a heads up in case that wasn't on purpose.
I would check how the app seems to function after you've built it and it's not in development mode before troubleshooting the performance issues much further. May not be an issue for the final app product.
I'm going to go out on a limb here but are you using one of the below methods to prevent re-rendering?
PureComponents (added in React 15.3 I think)
Manually with shallow compare props and state with shouldComponentUpdate method.
By default React will re-render all components upon update unless you correctly handle the shouldComponentUpdate.
I'm doing something similar and not having these issues at all
We solved it by simply wrapping all screens in a component.
We have one ScreenView component that wraps the whole screen, and we pass it a parameter "restrictRerendersToRoutes".
This screen is connected to the state, and we update the currentScrene in our state and expose it to this screen view.
Then we simply restrict rerenders when the screen is in the background with this shouldComponentUpdate implementation:
shouldComponentUpdate(nextProps) {
if (!_.empty(this.props.restrictRerendersToRoutes)) {
return !!this.props.restrictRerendersToRoutes
.find((routeKey) => routeKey === nextProps.currentScene.name);
}
return true;
}
The problem is indeed that the whole navigation stack is connected to the store( because components are not unmounted unless you use resetto or resetnavigation )
For what it's worth here is what I do right now.
I have been working with a slightly modified react redux implementation that skips updates for scenes that are not in view ( see below )
To use it you need to:
Store the route for one component tree in context
We created a wrapper for this
const CurrentRoute = React.createClass({
childContextTypes : { currentRoute: PropTypes.object },
getChildContext() {
return { currentRoute: this.props.route }
},
render() { return this.props.children; }
})
And use it in render scene
<CurrentRoute route={route}><CurrentScene navigate={navigate} route={route} index={index} /></CurrentRoute>
Then you can access the route a component has been rendered into.
Store the navigation stack in a singleton
We use this code in configure scene
let routeStack = [];
export const updateRouteStack = stack => routeStack = stack;
Then you can use this slightly modified react-redux connect function, it will skip updates if component is rendered in another component tree then the currently displayed one
( or a similar implementation )
https://github.com/reactjs/react-redux/compare/master...ganmor:master
It might be possible to package this but I haven't had the time to look into it. If anyone feels like doing it..
Hope that helps

Disable 2 finger swipe back navigation in Chrome on OSX [duplicate]

I want to disable the two finger swipe that causes Chrome going back or forward.
I have a website where the user might lose progress on his work if he doesn't specifically saves.
I have tried using window.onbeforeunload but that doesn't seem to work if I have hashes in the url (back forward would change between www.example.com/work/#step1#unsaved www.example.com/work/#step0) and the event doesn't seem to trigger.
I was about to switch to another solution but today I noticed that in Google Docs it's completely disabled. How did they achieve that?
Disable Chrome two fingers back/forward swipe
This worked for me. In your CSS file:
html {
overscroll-behavior-x: none;
}
body {
overscroll-behavior-x: none;
}
Make the specific page open in a new tab/window by default (by putting target="_blank"> in hyperlink). That way there'll be no previous page to go back to.
Or prevent Horizontal Scrolling by default. To do that, you can use jquery.mousewheel to do:
$(document).on("mousewheel",function(event,delta){
// prevent horizontal scrolling
if (event.originalEvent.wheelDeltaX !== 0) {
event.preventDefault();
}
});
Assuming you have a horizontal-scrolling element, adding overscroll-behavior-x: contain; is the easiest way prevent the scroll action from spilling out into the page and causing the navigation.
https://dev.to/danburzo/css-micro-tip-prevent-history-navigation-on-horizontally-scrolling-elements-3iil
https://caniuse.com/#feat=css-overscroll-behavior
Disable or replace swipe gestures for Google Chrome 61
The question that leads me here was marked "duplicate" and closed to answers. I believe this answer is better suited for the "duplicated" question, however, I feel this answer could possibly save time for someone landing on either question.
Better question:
Disable navigation swipe on Chrome browser in javascript
This Google developers article helped me to allow the e.preventDefault() to work and prevent swipe gestures as of Chrome 61.
https://developers.google.com/web/updates/2017/01/scrolling-intervention
givanse's answer to the following was the code that I used to write my own swipe event handlers:
Detect a finger swipe through JavaScript on the iPhone and Android
In summary, the following two events are used to implement the swipe gestures:
handleTouchStart (e) {
...
},
handleTouchMove (e) {
...
e.preventDefault()
}
As of Chrome 56, the default behavior is to make the event listeners passive and thus disable the ability to prevent Chrome's swipe gestures. To override this behavior, event listeners can be added as follows:
document.addEventListener(
'touchstart',
this.handleTouchStart,
{passive: false}
)
document.addEventListener(
'touchmove',
this.handleTouchMove,
{passive: false}
)
By passing the {passive: false} object as the third parameter to the addEventListener method, the listener is registered as active and can stop Chrome's default behavior with the e.preventDefault() event method.
Building on both the previous answers given by #roy riojas and #redgetan - I combined their answers to allow for this to be dynamic and prevent both forward and backwards - again - per #roy's comments - you must know the class of your element, and for this implementation - the class of the nested element that is actually being scrolled
(function ($) {
$(document).on('mousewheel', function(e) {
var $target = $(e.target).closest('.scrollable-h');
var scroll = $target.scrollLeft();
var maxScroll = $target.find('.scrollable-h-content').width() - $target.width();
if(scroll <= 0) {
if(scroll <= 0 && e.originalEvent.wheelDeltaX >= 0) {
e.preventDefault();
}
}
if(scroll >= maxScroll) {
if (scroll >1 && e.originalEvent.wheelDeltaX <= 0) {
e.preventDefault();
}
}
});}(jQuery));
I've been working on something similar where I want to override the forward/backward history swiping gesture. Depending on what your swipe area is you can tweak the selector as follows:
html { touch-action:none; }
This is the associated documentation that gives you all the properties to all touch actions like panning or zooming features built into the browser.
https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action
I was able to disable it by typing chrome://flags in the address bar and heading down to "Overscroll history navigation" and setting it to "Disabled" from the dropdown.
You can disable back/forward with this code:
document.addEventListener("wheel", function(event) {
event.preventDefault();
}, { passive: false });
Note that adding { passive: false } is essential, at least in Chrome. If you only want to disable back/forward in certain areas you can use code like this (assuming you're using jquery and you add the class disable-back-forward to the sections where you want to disable back/forward):
document.addEventListener("wheel", function(event) {
if ($(event.target).closest('.disable-back-forward').length)
event.preventDefault();
}, { passive: false });
Hi this worked for me on chrome but not for the entire page, but for places where I have scrollable content.
In Google Docs (Spreadsheets) it seems to be working because they don't have a back page to go. If you navigate to another URL (manually) it will not prevent you from navigating back.
$(document).on('mousewheel', function(e) {
var $target = $(e.target).closest('.scrollable-h');
if ($target.scrollLeft () <= 4) {
$target.scrollLeft(5);
return false;
}
});
One thing to keep in mind is that the code above is making two assumptions:
your element with horizontal scrollable content has a class scrollable-h
If checks if the scrollLeft if bigger less than 4px and then just make it scroll to 5px
returning false effectively cancel the back gesture
Important:
- This only prevents the back swipe gesture, when is done fast, if you do it very slow it will still trigger sometimes.
Also this does not prevent the forward swipe gesture, but it could also be done by checking if the element has reached the maximum scrollLeft. If that is the case then move it 20px back and return false to prevent the event from happening... It is up to you to add this use case if it happens to make sense to you.
You can take a look to a proof of concept here. http://jsfiddle.net/royriojas/JVA6m/#base
You're looking at the problem at the wrong level. OnBeforeUnload is simply not triggered because there is nothing being unloaded from the browsers perspective. Therefore you have, quite bluntly, implemented the wrong mechanism for versioning - fragments are for page states, not document states as you are using it now.
If you insist on maintaining state through hash fragments you need to use another mechanism to guard against page state changing. Since all current browsers support LocalStorage I'd use that. And well, while at it, put all the document state data there instead of in the URL, since that is how Google Docs does it and that is why they don't have this issue.

How to make it so events cannot occur while an event is running in DOJO?

Is there some way I can disable all events until an event is completed in DOJO? For instance I am fading elements and the user can click the event again and it will not complete the last event.
If you control all events that need to be disabled, you could try using a global variable as a "lock" - set it on when you start the animation (and have all events abort if they find this flag triggered) and unset it when it ends.
Javascript is not concurrent (so you don't need to worry about timing issues and having an "actual" lock) but perhaps the fading uses setTimeout behind the scenes (allowing other events to trigger before it is done). If this is the case, just remember that you would need to use the onEnd callback to properly detect when the anim is over
var lock = false;
function my_event_handler(evt){
if(lock) return; //someone else is using the lock;
//perhaps cancel event propagation as well?
lock = true;
dojo.anim({
...
onEnd: function(){
lock = false;
}
});
}
caveat: this is pseudocode off the top of my head. I haven't used dojo animations in a while if you didn't notice already :P
I'm not sure I understand what you mean by events here, but if you want to prevent interaction with elements on a page, you can put up a modal shield... basically a transparent DIV element to capture events, positioned over your content with a high z-index

Resources