A-Frame + AR.js: How to add objects dynamically in Arjs when tapping on screen? - augmented-reality

I'm currently working on an A-Frame AR app by using ar.js (I have to use ar.js). The goal is to dynamically add objects to the scene when tapping on the scene. Unfortunately nothing seems to work out. I managed to change the colors of an existing object when tapping on the screen but not adding a new one. Could someone please help me out here? I would be really grateful!
AFRAME.registerComponent('cursor-listener', {
init: function () {
var currentEl = this.el;
currentEl.addEventListener("click", function (evt) {
console.log("I was clicked!");
currentEl.setAttribute('material', {"color": "blue"});
var el = document.createElement("a-entity");
el.setAttribute('geometry', { "primitive": "sphere", "radius": "1" });
el.setAttribute('position', this.el.object3D.position)
const randomYRotation = Math.random() * 360
el.setAttribute('rotation', `0 ${randomYRotation} 0`)
el.setAttribute('visible', 'true')
el.setAttribute('shadow', {receive: false})
this.el.sceneEl.appendChild(el);
});
},
});

this.el.sceneEl.appendChild(el);
You should append the element to the marker node, and treat it as your "root" element - as it's the one being tracked when detected by camera:
<script>
AFRAME.registerComponent('cursor-listener', {
init:function () {
const marker = document.querySelector("a-marker")
var el = document.createElement("a-sphere");
marker.appendChild(el);
}
})
</script>
<!-- scene and stuff -->
<a-marker preset="hiro>
</a-marker>

Related

Openlayers 3 popover putting each word on one line

Working on an OL3 map, and am getting stuck on popover style. I haven't added any CSS to change it at all and it is currently being handled by default Bootstrap settings.
Okay so the problem: each word is being displayed on it's own line. How can I change this? Or, a better question, how do I control the style of the popovers?
This is was it looks like now: http://i.imgur.com/lrLYbag.png
The code handling popups:
//popups
var element = document.getElementById('popup');
var popup = new ol.Overlay({
element: element,
positioning: 'bottom-center',
stopEvent: false
});
map.addOverlay(popup);
// display popup on hover
map.on('pointermove', function(evt) {
var feature = map.forEachFeatureAtPixel(evt.pixel,
function(feature, layer) {
return feature;
});
if (feature) {
var geometry = feature.getGeometry();
var coord = geometry.getCoordinates();
popup.setPosition(coord);
$(element).popover({
'placement': 'top',
'html': true,
'content': feature.get('name')
});
$(element).popover('show');
} else {
$(element).popover('destroy');
}
});
Thanks!
I think you just need to add some css to the #popup element you're using for the popup.
Something like:
#popup {
min-width: 280px;
}
Take a look at this example http://openlayers.org/en/master/examples/popup.html for styling ideas.

map.on('click') coordinates wrong in Firefox

I am using OpenLayers 3.10.1 and the following code:
map.on('click', function (map, evt) {
var mouseCoords = [evt.originalEvent.offsetX, evt.originalEvent.offsetY];
alert(mouseCoords);
});
When I click as near as I can to the upper left corner of the map in IE or Chrome, I get an alert with the mouse coordinates offset from the top left corner of the map div, i.e. something sensible like [3,4].
But when I try the same things I get the mouse coordinates relative to the browser window and not the map div. Does anyone know why this is? Am I using an outdated way to find the mouse coordinates (ultimately so I can calculate which feature was clicked)?
Some alternatives:
map.on('click', function(evt){
console.info(evt.pixel);
console.info(map.getPixelFromCoordinate(evt.coordinate));
});
The following code works for me (openlayer 4.4, angular 4+)
_initMap() {
console.info("_initMap");
this.mapW = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
target: 'myMapDiv',
controls: ol.control.defaults({
attributionOptions: /** #type {olx.control.AttributionOptions} */ ({
collapsible: false
})
}),
view: new ol.View({
center: ol.proj.fromLonLat([this.lon, this.lat]);,
zoom: 12
})
});
const theMap = this.mapW;
// display on click
this.mapW.on('click', function(evt) {
/* // you want to detect feature click
var feature = theMap.forEachFeatureAtPixel(evt.pixel,
function(feature, layer) {
return feature;
});
if (feature) {
var fname = feature.getProperties().name;
console.info("feature clicked:" + fname);
return;
}
*/
console.info(evt.coordinate);
console.info(ol.proj.toLonLat(evt.coordinate)); // <=== coordinate projection
});
JS console results (when clicking close to Grenoble in France):
(2) [637000.3050167585, 5652336.68427412]
(2) [5.722271099853512, 45.19540527485873]

How do I stop my fixed navigation from moving like this when the virtual keyboard opens in Mobile Safari?

I understand that mobile safari has a lot of bugs around fixed elements, but for the most part I've managed to get my layout working correctly until I added a much needed text input to the fixed navigation at the bottom. Now when the user focuses on the text input element and the virtual keyboard appears, my navigation, which is otherwise always fixed at the bottom of the page, jumps up to a really strange spot in the middle of the page.
I'd add some of my code to this post, but I wouldn't be sure where to start. That navigation is fixed at the bottom and positioned to the left and bottom 0, and 100% width. From there, I don't know what's going on, I can only assume it's a mobile safari bug.
It also appears to lose it's position fixed and become relative, only while the text input element is focused on and the virtual keyboard is open.
http://dansajin.com/2012/12/07/fix-position-fixed/ this is one of the solutions proposed. Seems worth a shot.
In short: set fixed elements to position:absolute when any input is focused and reset them when that element is blurred
.header {
position: fixed;
}
.footer {
position: fixed;
}
.fixfixed .header,
.fixfixed .footer {
position: absolute;
}
and
if ('ontouchstart' in window) {
/* cache dom references */
var $body = $('body');
/* bind events */
$(document)
.on('focus', 'input', function() {
$body.addClass('fixfixed');
})
.on('blur', 'input', function() {
$body.removeClass('fixfixed');
});
}
The solutions on the top are some ways to go and fix the problem, but I think adding extra css class or using moderniz we are complicating things.If you want a more simple solution, here is a non-modernizr non-extra-css but pure jquery solution and work on every device and browsers I use this fix on all my projects
if ('ontouchstart' in window) {
$(document).on('focus', 'textarea,input,select', function() {
$('.navbar.navbar-fixed-top').css('position', 'absolute');
}).on('blur', 'textarea,input,select', function() {
$('.navbar.navbar-fixed-top').css('position', '');
});
}
I had a similar problem, but I found a workaround by adding the following css class to the body element on input focus and then removing it again on unfocus:
.u-oh {
overflow: hidden;
height: 100%;
width: 100%;
position: fixed;
}
Taking from what sylowgreen did, the key is to fix the body on entering the input. Thus:
$("#myInput").on("focus", function () {
$("body").css("position", "fixed");
});
$("#myInput").on("blur", function () {
$("body").css("position", "static");
});
Add javascript like this:
$(function() {
var $body;
if ('ontouchstart' in window) {
$body = $("body");
document.addEventListener('focusin', function() {
return $body.addClass("fixfixed");
});
return document.addEventListener('focusout', function() {
$body.removeClass("fixfixed");
return setTimeout(function() {
return $(window).scrollLeft(0);
}, 20);
});
}
});
and add class like this:
.fixfixed header{
position: absolute;
}
you can reference this article: http://dansajin.com/2012/12/07/fix-position-fixed/
I really like the solution above. I packaged it up into a little jQuery plugin so I could:
Set which parent gets the class
Set which elements this applies to (don't forget "textarea" and "select").
Set what the parent class name is
Allow it to be chained
Allow it to be used multiple times
Code example:
$.fn.mobileFix = function (options) {
var $parent = $(this),
$fixedElements = $(options.fixedElements);
$(document)
.on('focus', options.inputElements, function(e) {
$parent.addClass(options.addClass);
})
.on('blur', options.inputElements, function(e) {
$parent.removeClass(options.addClass);
// Fix for some scenarios where you need to start scrolling
setTimeout(function() {
$(document).scrollTop($(document).scrollTop())
}, 1);
});
return this; // Allowing chaining
};
// Only on touch devices
if (Modernizr.touch) {
$("body").mobileFix({ // Pass parent to apply to
inputElements: "input,textarea,select", // Pass activation child elements
addClass: "fixfixed" // Pass class name
});
}
I use this jQuery script:
var focus = 0;
var yourInput = $(".yourInputClass");
yourInput.focusin(function(){
if(!focus) {
yourInput.blur();
$("html, body").scrollTop($(document).height());
focus = 1;
}
if(focus) {
yourInput.focus();
focus = 0;
}
});
Works perfectly for me.
The focusin and focusout events seem to be better suited to this problem than the focus and blur events since the former bubble up to the root element. See this answer on SO.
Personally I use AngularJS, so I implemented it like this:
$window.document.body.addEventListener('focusin', function(event) {
var element = event.target;
var tagName = element.tagName.toLowerCase();
if(!$rootScope.inputOverlay && (tagName === 'input' || tagName === 'textarea' || tagName === 'select')) {
$rootScope.$apply(function() {
$rootScope.inputOverlay = true;
});
}
});
$window.document.body.addEventListener('focusout', function() {
if($rootScope.inputOverlay) {
$rootScope.$apply(function() {
$rootScope.inputOverlay = false;
});
}
});
Note: I am conditionally running this script if this is mobile Safari.
I put an ng-class attribute on my navbar:
<div class="navbar navbar-default navbar-fixed-top" ng-class="{'navbar-absolute': inputOverlay}">
using the following CSS:
.navbar-absolute {
position: absolute !important;
}
You can read more about focusin here and focusout here.
Test this one. It works. I just test it.
$(document).on('focus','input', function() {
setTimeout(function() {
$('#footer1').css('position', 'absolute');
$('#header1').css('position', 'absolute');
}, 0);
});
$(document).on('blur','input', function() {
setTimeout(function() {
$('#footer1').css('position', 'fixed');
$('#header1').css('position', 'fixed');
}, 800);
});
None of these solutions worked for me because my DOM is complicated and I have dynamic infinite scroll pages, so I had to create my own.
Background: I am using a fixed header and an element further down that sticks below it once the user scrolls that far down. This element has a search input field. In addition, I have dynamic pages added during forward and backwards scroll.
Problem: In iOS, anytime the user clicked on the input in the fixed element, the browser would scroll all the way to the top of the page. This not only caused undesired behavior, it also triggered my dynamic page add at the top of the page.
Expected Solution: No scroll in iOS (none at all) when the user clicks on the input in the sticky element.
Solution:
/*Returns a function, that, as long as it continues to be invoked, will not
be triggered. The function will be called after it stops being called for
N milliseconds. If `immediate` is passed, trigger the function on the
leading edge, instead of the trailing.*/
function debounce(func, wait, immediate) {
var timeout;
return function () {
var context = this, args = arguments;
var later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
function is_iOS() {
var iDevices = [
'iPad Simulator',
'iPhone Simulator',
'iPod Simulator',
'iPad',
'iPhone',
'iPod'
];
while (iDevices.length) {
if (navigator.platform === iDevices.pop()) { return true; }
}
return false;
}
$(document).on("scrollstop", debounce(function () {
//console.log("Stopped scrolling!");
if (is_iOS()) {
var yScrollPos = $(document).scrollTop();
if (yScrollPos > 200) { //200 here to offset my fixed header (50px) and top banner (150px)
$('#searchBarDiv').css('position', 'absolute');
$('#searchBarDiv').css('top', yScrollPos + 50 + 'px'); //50 for fixed header
}
else {
$('#searchBarDiv').css('position', 'inherit');
}
}
},250,true));
$(document).on("scrollstart", debounce(function () {
//console.log("Started scrolling!");
if (is_iOS()) {
var yScrollPos = $(document).scrollTop();
if (yScrollPos > 200) { //200 here to offset my fixed header (50px) and top banner (150px)
$('#searchBarDiv').css('position', 'fixed');
$('#searchBarDiv').css('width', '100%');
$('#searchBarDiv').css('top', '50px'); //50 for fixed header
}
}
},250,true));
Requirements: JQuery mobile is required for the startsroll and stopscroll functions to work.
Debounce is included to smooth out any lag created by the sticky element.
Tested in iOS10.
I wasn't having any luck with the solution proposed by Dan Sajin. Perhaps the bug has changed since he wrote that blog post, but on iOS 7.1, the bug will always surface when the position is changed back to fixed after the input is blurred, even if you delay until the software keyboard is hidden completely. The solution I came to involves waiting for a touchstart event rather than the blur event since the fixed element always snaps back into proper position when the page is scrolled.
if (Modernizr.touch) {
var $el, focused;
$el = $('body');
focused = false;
$(document).on('focus', 'input, textarea, select', function() {
focused = true;
$el.addClass('u-fixedFix');
}).on('touchstart', 'input, textarea, select', function() {
// always execute this function after the `focus` handler:
setTimeout(function() {
if (focused) {
return $el.removeClass('u-fixedFix');
}
}, 1);
});
}
HTH

Google Maps and ASP .NET MVC - Center the map at a marker

I'm building a page with a Google MAP that has a side bar with dynamically created divs linked to positions of markers in the map.
I'm using ASP.NET MVC with JQuery and Google Maps API v3.
Here is a look of it.
This page is loaded in a splash window and is generated dynamically.
In the background page the user types a state or city in an input field and in my controller action I search for all people that are located in that area and return a JSON.
I get the JSON and populate the map with markers and then I make the list of divs in the side bar.
I'd like to add a function to those divs that when they are clicked the map will center at the marker.
I came up with an idea for this solution which I could not finish.
I added class="clickable" and the attributes "Lat" and "Lng" with the values equal to the ones in the markers they are related to, and I tried to get their click event with JQuery and then set the map center with its Lat and Lng like this:
$(".clickable div").click(function(){
$('map_canvas').panTo($(this).attr('lat'), $(this).attr('lng'));
}
I had 2 problems with this approach.
- First, I didn't know how to get the map with JQuery.
I found 2 ways using like $('map_canvas').gMap but it didn't work. Tried a couple more things that I've found here in Stackoverflow but also didn't work.
Second - The JQuery would not catch the event click from the DIVs.
I tested on Google Chrome console the JQuery code and It worked but then my code would not trigger the click.
I tried something like $(".clickable div").click(function(){ alert("test"); } on Google Chrome and it worked, but in my script it did not.
I also tried to add listeners using addDomListener in my code but couldn't get around that either.
Could anyone please give me a light what would be a good way to do this without having to recreate the map when a div is clicked.
I also don't like the idea of adding Lat and Lng attributes to the divs, I don't know if that would work in any browser or if its good practice. I'm just out of solutions to this.
Here is the code I'm using. (I removed some of it to make it shorter and easier to read)
$(document).ready(function () {
//Google Maps API V3 - Prepares the ajaxForm options.
var options = {
beforeSubmit: showRequestPesquisaAdvogados,
success: showResponsePesquisaAdvogados,
type: 'post',
resetForm: false
};
$('#form-pesquisaAdvogado').ajaxForm(options);
//$(".clickable").click(function () { alert($(this).attr('lat')) });
});
function showRequestPesquisaAdvogados(formData, jqForm, options) {
$("#modal-processing-background").show(); //Shows processing splash window
}
function showResponsePesquisaAdvogados(responseText, statusText, xhr, $form) {
$("#modal-processing-background").hide();
//Hide processing window
loadSplashWindow();
CreateMap(responseText);
CreateSideBar(responseText);
}
}
function CreateMap(json) {
var mapOptions = {
center: new google.maps.LatLng(json[0].Endereco.Lat, json[0].Endereco.Lng),
zoom: 5,
mapTypeId: google.maps.MapTypeId.ROADMAP
// marker:true
};
var infoWindow = new google.maps.InfoWindow();
var map = new google.maps.Map(document.getElementById("map-result"), mapOptions);
for (i = 0; i < json.length; i++) {
var data = json[i]
var myLatlng = new google.maps.LatLng(data.Endereco.Lat, data.Endereco.Lng);
var marker = new google.maps.Marker({
position: myLatlng,
map: map,
title: data.Endereco.Logradouro
});
(function (marker, data) {
// Attaching a click event to the current marker
google.maps.event.addListener(marker, "click", function (e) {
// Prepare the infoWindows content.
var contentString = //my content;
infoWindow.setContent(contentString);
infoWindow.open(map, marker);
});
//here I tried to add the listener to the div.
google.maps.event.addDomListener($(".clickable")[i], 'click',
function () {
map.panTo(new google.maps.LatLng($(this).attr('lat'),
$(this).attr('lng')));
});
})(marker, data);
}
}
function CreateSideBar(json) {
$(".sideBarConteiner").empty();
for (i = 0; i < json.length; i++) {
var contentString =
"<div class='clickable' lat='" + data.Endereco.Lat +
"' lng='" + data.Endereco.Lng + "' >" +
//...div's content here
"</div>";
$(".sideBarConteiner").append(contentString);
}
}
If you have any suggestions to make the code better or better practices, since I have only 3 months of experience with programming I might be going in the wrong direction without knowing, so please, feel free to change something if you think it'd be a better way.
I know my post is a bit lenghty, I just wanted to make it clear.
Thank you for your support in advance.
Regards,
Cesar.
I've found a way to do this by creating a global variable in javascript and keeping the map information in order to call it again later.
To to this I just added a var "map" right at the top of the .
<script type="text/javascript">
$.ajaxSetup({ cache: false }); //Forces IE to renew the cash for ajax.
var zoom = 8;
var mapOptions, map;
And then call a method to pan to the right point.
I added the properties Lat and Lng to the div and then pass the div in the javascript function and get the attributes from it.
function CreateSideBar(json) {
$(".sideBarConteiner").empty();
for (i = 0; i < json.length; i++) {
var contentString =
"<div class='clickable' data-lat='" + data.Endereco.Lat +
"' data-lng='" + data.Endereco.Lng + "' onclick='MarkerFocus(this)'>" +
//...div's content here
"</div>";
$(".sideBarConteiner").append(contentString);
}
}
And in my function:
function MarkerFocus(obj) {
var myLatlng =
new google.maps.LatLng($(obj).attr('data-lat'), $(obj).attr('data-lng'));
map.panTo(myLatlng);
}
It worked for me. I hope it helps you too.
Thanks to all for the help and support.
Cesar

Why is my Google Map in a jQuery UI tab only displaying one tile at the top left corner of the screen?

I have two Google Map API v3 Maps in jQuery tabs. They both display the first time they appear in their tabs, but the initially deselected tab only displays a tile or so at the top left-hand corner.
Calling google.maps.event.trigger(all_map, 'resize') periodically does not change this.
In my code I have:
<div id='all_map_canvas'>
</div>
<script>
function show_all_map()
{
var center = new google.maps.LatLng(%(center_latitude)s - .15, %(center_longitude)s + .2);
var myoptions = {
zoom: %(zoom)s,
center: center,
mapTypeId: google.maps.MapTypeId.TERRAIN
};
all_map = new google.maps.Map(document.getElementById('all_map_canvas'), myoptions);
all_map.setZoom(%(zoom)s);
jQuery('#all_map_canvas').show();
google.maps.event.trigger(all_map, 'resize');
}
function show_all_map_markers()
{
%(all_map_markers)s
}
var markers = [];
jQuery('#all_map_canvas').width(jQuery(window).width() - 300);
jQuery('#all_map_canvas').height(jQuery(window).height() - 125);
How can I make both maps display in full after tab swaps?
I had the same problem: Google map in div that has style display:none;.
Solved it with this two steps:
Removing all additional styling of the div I'm placing the map except height:100%;
Initiate the map after the holding div is displayed not when the it is created. In my case in the callback function of the show method:
CSS
.main-window #map{
height:100%;
}
JS
$('.main-window').show(3000, function() {
// create the map after the div is displayed
var mapOptions = {
center: new google.maps.LatLng(42.499591, 27.474961),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map"), mapOptions);
});
HTML
<div class="main-window">
<div id="map"></div>
</div>
Initiating it in events like jQuery's '$(document).ready(...' or Google's 'google.maps.event.addDomListener(window, "load", ...' is not working for some reason.
Hope it helps.
I am using gmaps.js which is a simplified api for google map. But it's simply a matter of attaching a tabsshow event in javascript and refreshing the map in the delegate's function.
Here's the code I use for gmaps.js
$('#tabs').bind('tabsshow', function(event, ui) {
event.preventDefault();
if (ui.panel.id == "tabs2") {
//gmaps.js method
map.refresh();
map.setCenter(lastLoc.lat(), lastLoc.lng());
//for native google.maps api
google.maps.event.trigger(map, 'resize');
map.panTo(new google.maps.LatLng(lastLoc.lat(), lastLoc.lng()));
}
});

Resources