I'm using A-Frame (webVR) to make a multi panorama explorer. The world has a few 3d buttons (with panorama mapped spheres at each button position). You click on a button, and an animation component moves the camera from its current location to the button location. A custom component containing a listener (for click events on buttons), uses setAttribute to set the 'to' and 'from' attributes of an animation component on the camera. An emit command triggers the animation. Unfortunately it fails, and I can see that the 'from' attribute is not setting correctly (it is setting to the same as 'to'). It doesn't throw errors, but I can see in the console that the from will not set,even though I set it explicitly.
I've already got this working in a previous version. You can see and test it here:
https://glitch.com/~camera-jumper
In this version, I don't set the from attribute, and it still works fine, presumably because it takes the previous value as a default.
Then I built in some other functionality and now it is failing. You can see the current version here:
https://glitch.com/~panojumper
AFRAME.registerComponent('buttoncontrol', {
schema: {
pano:{type:'string',default: 'pSphere1'}
},
init: function(){
var el = this.el;
var cam = document.querySelector('#camera');
var panoManEntity = document.querySelector('#panoMan');
var panoName = this.data.pano;
console.log(cam.getAttribute('animation__jump', 'to'));
this.el.addEventListener('click', function(evt){
// Animate the camera moving to the button position
var btnpos = el.getAttribute('position');
var cam = document.querySelector('#camera');
var campos = cam.getAttribute('position');
cam.setAttribute('animation__jump','to', {x: btnpos.x, y:
btnpos.y, z: btnpos.z });
cam.setAttribute('animation__jump','from', {x: campos.x, y:
campos.y, z: campos.z });
cam.emit('camjump');
});
}
});
I expect setAttribute for 'from' to be what I am setting it to, but it is the same as the 'to' value.
Setting the from attribute seems to be working, you can verify it by modifying your first glitch. Add the from attribute, and its still working properly:
var campos = cam.getAttribute('position')
// Animate the camera moving to the button position
cam.setAttribute('animation__jump','to', {x: btnpos.x, y: btnpos.y, z: btnpos.z });
cam.setAttribute('animation__jump','from', {x: campos.x, y: campos.y, z: campos.z });
fiddle here.
The other glitch will also work if you register the custom components in the <head> or at least before the <a-scene>. There is also a note on it in the docs.
fiddle here.
You nailed it! Custom components must not be in the scene, but in the head. I moved it because my assets tag was before the scene tag, which threw an error, so I moved the scene tag too far north. Moving the scene tag below registerComponents solved it.
All of this is in the docs, but I'm a noob. Thanks for your help!
Related
I'm trying to trigger some functionality based on the click of a marker on a GeoJSON layer in Leaflet. The eventual functionality I'm trying to implement is a flyout, or scroll out type modal populated from the individual feature's JSON attributes. Essentially, I'm trying to implement the functionality in this Tutsplus Tutorial with dynamic feature content based on the marker click.
I THINK I've figured out most of the pieces I need, but I'm struggling with how to add a data attribute, specifically data-open, to the individual marker. Building on an earlier question of mine I've realized it's not enough to just update a DOM element's CSS, but rather my app should be implementing changes based on data attributes to fully get the functionality I want.
From this question I know that this should be done by extending the L.Icon class that Leaflet provides, but the answer is a bit too terse for my current JS skills. I apologize for this effectively being a "ELI5" of a previously asked question, but I'm not sure where the options and slug come into function. I think they're implied by the question, rather than the answer I'm citing and being set on the marker itself.
Here's a simplified version of the the click handler on my markers, which grabs and zooms to location, gets feature info, and populates that info to a div. The zoom functionality works, as does extracting and placing the feature info, but I'm struggling with how to connect the functionality to trigger the modal and place the div with the feature info over the map.
function zoomToFeature(e) {
var latLngs = [e.target.getLatLng()];
var markerBounds = L.latLngBounds(latLngs);
var street = e.target.feature.properties.str_addr;
document.getElementById('street').textContent = street;
mymap.fitBounds(markerBounds);
//where the modal trigger should be
document.getElementById('infoBox').classList.add('is-visible');
}
Here are the event listeners taken from the linked tutorial, which are currently not firing, but I have them working in a standalone implementation:
const openEls = document.querySelectorAll("[data-open]");
const closeEls = document.querySelectorAll("[data-close]");
const isVisible = "is-visible";
//this is the event I want to trigger on marker click
for (const el of openEls) {
el.addEventListener("click", function() {
const modalId = this.dataset.open;
console.log(this);
document.getElementById(modalId).classList.add(isVisible);
});
}
for (const el of closeEls) {
el.addEventListener("click", function() {
this.parentElement.parentElement.parentElement.classList.remove(isVisible);
});
}
document.addEventListener("click", e => {
if (e.target == document.querySelector(".modal.is-visible")) {
document.querySelector(".modal.is-visible").classList.remove(isVisible);
}
});
So, where I'm trying to get is that when my markers are clicked, the trigger the modal to appear over the map. So, I think I'm missing connecting the marker click event with the event that triggers the modal. I think what's missing is adding the data attribute to the markers, or some way chain the events without the data attributes. As there's no direct way to add an attribute to the markers, I try to add slug option on my circle markers:
var circleMarkerOptions = {
radius: 2,
weight: 1,
opacity: 1,
fillOpacity: 0.8,
slug: 'open',
}
and If I read the previously asked question's answer correctly, than extending the Icon Class this way should add a data-open attribute.
L.Icon.DataMarkup = L.Icon.extend({
_setIconStyles: function(img, name) {
L.Icon.prototype._setIconStyles.call(this, img, name);
if (options.slug) {
img.dataset.slug = options.slug;
}
}
});
A stripped down version of my code is here (thanks #ghybs). My full implementation pulls the markers from a PostGIS table. It's a bit hard to see in the Plunker, but this code adds my class to my modal, but doesn't trigger the functionality. It does trigger the visibility if the class is manually updated to modal.is-visible, but the current implementation which renders modal is-visbile doesn't, which I think is because the CSS is interpreted on page load(?) and not in response to the update via the dev tools, while the concatenated css class matches extactly(?). When I do trigger the modal via the dev tools, the close modal listeners don't seem to work, so I'm also missing that piece of the puzzle.
So, it's a work-around to setting the data attribute, but I realized I was shoe-horning a solution where it wasn't needed. Assuming someone ends up with the same mental block. Appropriate listeners on the modal close button and another function passed to the existing marker click listener produce the desired functionality.
const closeM = document.querySelector(".close-modal");
closeM.addEventListener("click", closeMe);
var modal = document.getElementById('infoBox');
and
function modalAction(){
modal.style.display = 'block';
}
function closeMe(){
modal.style.display = 'none';
}
I'm having issues finding / figuring out how to add options to hammer.js when using it's jQuery plugin. This is how I initiate hammer:
/* Create Hammer object for swipable game cards */
$('.game-card-mobile')
.hammer()
.bind('swiperight swipeleft press', function(ev) {
console.log(ev.type);
});
I find it that in order for swipe action to be performed a relatively fast swipe needs to occur, I want to decrease speed of swipe required to fire the event. After reading through the docs: http://hammerjs.github.io/recognizer-swipe/ There seems to be a velocity option that defaults to 0.65, so I assume this can be changed somehow to lets say 0.5 in order to fire event on slower swipes?
EDIT: if this would be of any help here are some examples of tests for swipe written in javascript: https://github.com/hammerjs/hammer.js/blob/master/tests/unit/gestures/test_swipe.js this shows example of how {threshold: 1} is set, I'm trying to achieve this while using jQuery plugin.
The link you post, is from a library Called Hammer.js. You must import this library in your project to make it work.
Minimal velocity required before recognizing, unit is in px per ms as they say, then it´s 0.65px each millisecond, then making it 0.50 should make the event occur with less velocity in the touch.
0.50 -> will fire before
0.80 -> more difficult to fire
var hammertime = new Hammer(myElement, myOptions);
hammertime.on('swipe', function(ev) {
alert(ev);
});
hammertime.get('swipe').set({ velocity: 0.80});
Is maybe the right way?
Hoy it helps you.
Edit: As said in the jquery api page: The Hammer instance is stored at $element.data("hammer").
Then maybe
$("#yourdivtoswipe_id").data('hammer').get('swipe').set({ velocity: 0.80});
Regards
This is how:
$("#yourelemenet").hammer({options}).bind("tap", function(ev) {});
note the options object. Here is a quick example from some of my code:
$("#yourelemenet").hammer({threshold: 10, posThreshold:50, time:1000}).bind("tap", function(ev) {});
edit: add velocity where your see threshold, posThreshold, time above..
I've noticed that if all OpenLayers.Layers have visibility set to false, I can no longer zoom in and out using my mousewheel. I can still use the OpenLayers.Control.Zoom buttons, but no longer the mouse wheel.
Is there anyway to disable this 'feature'?
Edit:
Here is a jsfiddle link. Set the layer to invisible, then scroll, then set it back to visible. It doesn't change. Now set it invisible, use the zoom control button, then set it back visible.
http://jsfiddle.net/a8kK4/56/
Here's some code for how the map is instantiated because SO won't let me post jsfiddle without it:
var map = new OpenLayers.Map({
div: "map",
projection: new OpenLayers.Projection("EPSG:900913"),
displayProjection: new OpenLayers.Projection("EPSG:4326"),
layers: [ new OpenLayers.Layer.OSM() ]
});
The issue is to do with the onWheelEvent of the MouseWheel handler, see the code here on github. The issue arises with the lines
if (!overScrollableDiv && overMapDiv) {
if (allowScroll) {
as with the map hidden, you will never get it so that all of these conditions are met and therefore the call to this.wheelZoom(e) which actually does the zoom in/out never gets called. The behavior you are seeing is, therefore, by design.
One, somewhat crude way to fix this, is to override the whole function and just set the line where allowScroll is initialized to true, ie, you put the the whole function into your code, after the main OpenLayers.js has downloaded, and just change that one line
OpenLayers.Handler.MouseWheel.prototype.onWheelEvent= function(e){
if (!this.map || !this.checkModifiers(e)) {
return;
}
var overScrollableDiv = false;
//CHANGE THIS LINE TO TRUE
var allowScroll = true;
var overMapDiv = false;
//rest of function .......
};
The problem of doing this is that a scroll will now happen if you use the mouse wheel anywhere. There is probably a more elegant way by changing the conditions under which the values of overMapDiv and allowScroll and overScrollableDiv get set. I could not get this to work on jsFiddle (I think due to version conflicts), but locally it worked as expected. I hope this helps.
I'd like to dynamically change the popover placement (left/right, top/bottom) depending on where the element is located on the screen.
//get_popover_placement(dom_el) returns 'left', 'right', 'top', or 'bottom'
function set_popover(dom_el) {
var the_placement = get_popover_placement(dom_el);
$(dom_el).popover({
offset: 10,
placement: the_placement
}).popover('show');
}
//set the placement on every hover
$('a[data-rel=popover]').hover(function(){
set_popover(this);
}, function(){});
It works the first time, but if the position of the element changes (when the window is resized, for example), the placement is not updated with subsequent calls to set_popover.
I added a little bit of code to get_popover_placement that adds a different color border to the element, depending on the placement. The border color gets updated every time, indicating the code is being called and the calculations are being done correctly, but the placement does not get updated.
It appears as though the placement option can only be set one time. How can I get around this limitation? Is there a variable somewhere that can be cleared to reset popovers?
Try this change, using the .on() function in jQuery to attach an event listener:
Changed this reply by updating Kevin Ansfield's - added code to the placement function.
$('a[data-rel=popover]').popover({
placement: get_popover_placement
});
function get_popover_placement(pop, dom_el) {
var width = window.innerWidth;
if (width<500) return 'bottom';
var left_pos = $(dom_el).offset().left;
if (width - left_pos > 400) return 'right';
return 'left';
}
I just checked the bootstrap source again and realised that functions passed to the placement property get passed arguments. I managed to achieve a similar thing to what you were attempting, try the following and see if it works for you:
$('a[data-rel=popover]').popover({
offset: 10,
placement: get_popover_placement
});
function get_popover_placement(pop, dom_el) {
// your placement function code here
}
I want to show a popup many on click. I want that many to be in a bubble. So I created a demo: here. But that Bubble generator plugin i use tends to keep tons of trash in the DOM each time it shows a popup. Well so I tried to destroy trash via
$('.grumble-text').remove();
$('.grumble').remove();
$('.grumble-button').remove();
But it somehow brakes it at all=( So how to change grumble-bubble popup plugin code to make it either keep DOM clean or at least make plugin independent of trash it creates?
I've recently updated the plugin to provide better control of positioning and angle. The update also persists the grumble, invoking the plugin more than once on an element will not create extra left over DOM.
Try updating to the latest code. The code below should now work as you expect.
var html = ''
+'Download me'
+'<br/>'
+'Edit me'
+'<br/>'
+'Delete me';
var $grumble = $('#grumble3');
$grumble.mouseup(function(eventObj) {
$grumble.grumble({
text: html ,
angle: (Math.random() * 360 + 150),
distance: 30,
hideOnClick: true,
onShow: function() {
$grumble.addClass("hilight");
},
onBeginHide: function() {
$grumble.removeClass("hilight");
}
});
}).mousedown(function() {
$grumble.addClass("hilight");
});
Thanks for your interest. If there are any further problems please raise them as bugs on the github page. https://github.com/jamescryer/grumble.js
Use the grumble and button parameters on the onHide callback like this:
$('#grumble').grumble({
text: 'Whoaaa, this is a lot of text that i couldn\'t predict',
angle: 85,
distance: 50,
showAfter: 4000,
hideAfter: 2000,
onHide: function(grumble, button) {
grumble.bubble.remove();
grumble.text.remove();
button && button.remove();
}
});
This allows you to remove only the "trash" (I prefer "leftovers") associated with that specific tooltip/popup/bubble. Note that button only exists if hasHideButton is true, hence the button && existence check.
Why do you want to remove it? Is the 'trash' causing problems with browser performance?
In general, the only way to do this is to dig into the plugin source and add a function to remove the plugin, if one is not already present. If you just remove the related DOM elements you will leave behind references to them and events handlers that access them.