I noticed when I'm try to drag some item into mobile chromium, touchmove event rised after some gap from touchstart.
I wrote script to find out this gap in pixels:
`
deadZone
<script type="text/javascript">
window.addEventListener('load', function() {
var box = document.getElementById("box");
var status = document.getElementById("status");
var startPoint = 0;
var moveStart = false;
box.addEventListener('touchstart', function(e) {
startPoint = parseInt(e.changedTouches[0].clientX);
e.preventDefault();
});
box.addEventListener('touchmove', function(e) {
if(moveStart) return;
moveStart = true;
status.innerHTML = "deadZone: " + (e.changedTouches[0].clientX - startPoint) + "px";
e.preventDefault();
});
box.addEventListener('touchend', function(e) {
e.preventDefault();
moveStart = false;
});
});
</script>
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
`
And my results are: 14-30px on the right/left drag and 2-10px on up/down. It seems like box snapped to smthg in the starting of the moving it.
How can i avoid this and start tracking touchmove from real start point?
This is a bug in Chrome for Android (M31 - the current version). Luckily it will be fixed soon and should be in M33 or M34: https://code.google.com/p/chromium/issues/detail?id=323677
The basic summary is that Android suppresses the 'slop' events so that your last chance to prevent scrolling is always the first touchmove.
Related
I'm developing an HTML5 canvas-based game that's running in a WebView within an existing application. On first running the game, it loads successfully in the background, and all logs suggest it's ready and running. However, nothing is displayed in the WebView. If I load something else into the WebView and then return to my game, it loads successfully as before, and then renders. What could prevent a canvas from displaying on first being run, only to work on subsequent reloads?
For detail:
I get no console errors on loading the game for the first time.
The game loads successfully when running in the iOS version of the application.
The CSS applied to the canvas element doesn't render, suggesting it isn't an issue loading my assets before displaying them.
All the issues I've investigated were caused by trying to render assets before they were displayed, so subsequent reloads were fixed by these now cached images being displayed, but I can't find anything concerning the canvas failing to display at all in Android.
Here's the HTML loaded by the WebView:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
<title>Darts</title>
<meta name="description" content="Darts GamePad Game">
<script src="js/dummy-gamepad-functions.js"></script>
<script src="js/libs/polyfill.js"></script>
<script src="js/gamepad-client.js"></script>
<script src="js/darts-boot.js"></script>
<script src="js/darts-loader.js"></script>
<link rel="stylesheet" href="css/darts.css">
</head>
<body onload=bootGame()>
<div id="darts-game-container"></div>
</body>
</html>
And this is my onload script:
var bootGame = () => {
//create canvas element
let canvas = document.createElement('canvas');
canvas.id = "darts-canvas";
canvas.width = 740;
canvas.height = 400;
let gameContainer = document.getElementById("darts-game-container");
gameContainer.appendChild(canvas);
//scale canvas to view window
let gamepadViewport = document.getElementById("darts-game-container"),
gamepadCanvas = document.getElementById("darts-canvas"),
viewportWidth = window.innerWidth,
canvasWidth = gamepadCanvas.width,
viewportHeight = window.innerHeight,
canvasHeight = gamepadCanvas.height;
let scaleRatio = Math.min(viewportWidth/canvasWidth, viewportHeight/canvasHeight);
if (scaleRatio < 1) {
gamepadViewport.style.transform = `scale(${scaleRatio}, ${scaleRatio})`;
gamepadViewport.style.webkitTransform = `scale(${scaleRatio}, ${scaleRatio})`;
gamepadViewport.style.mozTransform = `scale(${scaleRatio}, ${scaleRatio})`;
gamepadViewport.style.oTransform = `scale(${scaleRatio}, ${scaleRatio})`;
}
//initialise Loader
Darts.Loader = new Loader();
//initialise GamePad API, then initialise core classes when loaded
GamePadClient = new GamePadClient();
GamePadClient.initialise()
.then(() => {
//load all scripts
return Darts.Loader.loadScripts(LIBS_TO_LOAD, COMMON_LIB_PATH, LIB_NAME_SUFFIX)
})
.then(() => {
return Darts.Loader.loadScripts(SCRIPTS_TO_LOAD, COMMON_SCRIPT_PATH, SCRIPT_NAME_SUFFIX)
})
.then(() => {
//initalise core classes
Darts.Controller = new Controller();
Darts.Logic = new Logic();
Darts.Display = new Display();
Darts.GameProps = new GameProps();
Darts.GameState = new GameState();
//loads display elements and scripts, then inits game once complete
Darts.Controller.initGame();
});
}
I've solved it after I narrowed it down to my canvas-resizing method. window.innerWidth and window.innerHeight are not set when window.onload is called, having an initial value of 0. They're only properly set after the window.onresize event is called, so listening for that and then scaling the canvas gives the proper result.
Is there any alternative for -webkit-touch-callout, which works on Android based mobiles.
I'm trying to disable the long touch popup in mobile devices.
I've tried to bind jQuerys taphold event to return false; but no luck...
Any idea?
Thanks!
<!DOCTYPE html>
<html>
<head>
<script>
function absorbEvent_(event) {
var e = event || window.event;
e.preventDefault && e.preventDefault();
e.stopPropagation && e.stopPropagation();
e.cancelBubble = true;
e.returnValue = false;
return false;
}
function preventLongPressMenu(node) {
node.ontouchstart = absorbEvent_;
node.ontouchmove = absorbEvent_;
node.ontouchend = absorbEvent_;
node.ontouchcancel = absorbEvent_;
}
function init() {
preventLongPressMenu(document.getElementById('theimage'));
}
</script>
</head>
<body onload="init()">
<img id="theimage" src="http://www.google.com/logos/arthurboyd2010-hp.jpg" width="400">
</body>
</html>
Source: Disabling the context menu on long taps on Android
(This is a split off from my other post I made, Google Maps API v3 in PhoneGap: markers not drawing properly after move) . I've been working on a Google Maps API 3 application on Android using PhoneGap. It should track the users location and mark the location with a marker and a circle around it. I had been developing this on Android 2.3 and it was working fine. I then upgraded to a phone with Android 4.x and it was still working well, I thought.
Then last week I began noticing Google Maps doing some strange things when I moved the Marker and Circle. It would seem to create a duplicate Marker and Circle instead of just moving them. In the other post, I got an answer for the Marker issue, but not for the Circle. Sometimes if I change zoom then the duplicate Circle seems to go away. But also, it occasionally draws some weird tangent line from the top. See images below.
I started noticing this shortly after I received an Android update on my phone to version 4.1.1. Not sure if that's related, I can't find any info about a problem.
I reduced the map and phonegap code to a pretty minimal sample and its still doing it. I'm pretty sure it isn't related to phonegap. I upgraded my phonegap to 2.2 anyway to be sure, but as expected it didn't help. Can anyone tell if I am doing something wrong when moving the circle?
One solution that I tried was to hide the circle before moving it and then show it again after I move it. That seemed to solve the duplicate and tangent issue, but it made the map flash, which is really annoying when you move it every 3 seconds or so. So that doesn't seem like an acceptable solution. Test code is below. Note that I removed my Google Maps API key in test code.
I've also learned that one way to get it to redraw correctly is to drag the map so that no part of any circle is visible. Then when I drag it back so that the circle is visible, it is redrawn as just one correct circle.
Thanks, Eric
<!DOCTYPE HTML>
<html>
<head>
<title>Marker Test</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
html {height: 100%}
body {height: 100%; margin:0; padding:0}
#map_canvas {height: 100%}
</style>
<script type="text/javascript" charset="utf-8" src="js/phonegap.js"></script>
<script type="text/javascript" charset="utf-8" src="http://maps.googleapis.com/maps/api/js?key=YOUR_KEY_HERE&sensor=true"></script>
<script type="text/javascript" charset="utf-8">
// Wait for PhoneGap to load
//
document.addEventListener("deviceready", onDeviceReady, false);
// globals
var watchID = null;
var map = null;
var myLocationMarker = null;
var searchCircle = null;
// PhoneGap is ready
//
function onDeviceReady() {
startGPS();
}
function onUnLoad() {
console.log("clearing watch " + watchID);
navigator.geolocation.clearWatch(watchID);
}
function startGPS() {
console.log("In startGPS");
var refreshMilliseconds = 5000;
var options = { frequency: refreshMilliseconds, enableHighAccuracy: true};
watchID = navigator.geolocation.watchPosition(onGPSSuccess, onGPSError, options);
// create Google map
var mapOptions = {
zoom: 14,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
myLocationMarker = new google.maps.Marker({
title: 'This is me!',
zIndex: 90,
optimized: false,
map:map
});
searchCircle = new google.maps.Circle({
fillColor: '#c0e4dd',
strokeColor: '#f15f22',
fillOpacity: 0.5,
radius: 1500,
map:map
});
}
var onGPSSuccess = function(p) {
// get the new coordinates
var lat = p.coords.latitude;
var lng = p.coords.longitude;
console.log("watch ID " + watchID);
// now that we have the coordinates, we can move the marker and circle on the Google Map
MoveMarkerAndCircle(lat, lng);
};
var MoveMarkerAndCircle = function(lat, lng) {
var myLocation = new google.maps.LatLng(lat, lng);
myLocationMarker.setPosition(myLocation);
// searchCircle.setVisible(false);
searchCircle.setCenter(myLocation);
// searchCircle.setVisible(true);
map.setCenter(myLocation);
}
var onGPSError = function() {
console.log("GPS Error");
};
var GenerateFakeMovement = function() {
var currentPosition = myLocationMarker.getPosition();
var newLat = currentPosition.lat() + 0.01;
var newLng = currentPosition.lng() + 0.01;
MoveMarkerAndCircle(newLat, newLng);
}
</script>
</head>
<body style="height:100%;text-align:center" onunload="onUnLoad()">
<div id="map_canvas" style="width: 100%;height:80%"></div>
MAKE FAKE MOVEMENT
</body>
</html>
<!DOCTYPE HTML>
<html>
<head>
<title>Marker Test</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
html {height: 100%}
body {height: 100%; margin:0; padding:0}
#map_canvas {height: 100%}
</style>
<script type="text/javascript" charset="utf-8" src="http://maps.googleapis.com/maps/api/js?v=3&sensor=true"></script>
<script type="text/javascript" charset="utf-8">
var watchID = null;
var map = null;
var myLocationMarker = null;
var searchCircle = null;
function onDeviceReady() {
startGPS();
}
function onUnLoad() {
console.log("clearing watch " + watchID);
navigator.geolocation.clearWatch(watchID);
}
function startGPS() {
console.log("In startGPS");
var refreshMilliseconds = 5000;
var options = { frequency: refreshMilliseconds, enableHighAccuracy: true};
if(watchID ==null)
watchID = navigator.geolocation.watchPosition(onGPSSuccess, onGPSError, options);
// create Google map
var mapOptions = {
zoom: 14,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
myLocationMarker = new google.maps.Marker({
title: 'This is me!',
zIndex: 90,
optimized: false,
map:map
});
searchCircle = new google.maps.Circle({
fillColor: '#c0e4dd',
strokeColor: '#f15f22',
fillOpacity: 0.5,
radius: 1500,
map:map
});
}
var onGPSSuccess = function(p) {
// get the new coordinates
var lat = p.coords.latitude;
var lng = p.coords.longitude;
console.log("watch ID " + watchID);
// now that we have the coordinates, we can move the marker and circle on the Google Map
MoveMarkerAndCircle(lat, lng);
};
var MoveMarkerAndCircle = function(lat, lng) {
var myLocation = new google.maps.LatLng(lat, lng);
myLocationMarker.setPosition(myLocation);
// searchCircle.setVisible(false);
searchCircle.setCenter(myLocation);
// searchCircle.setVisible(true);
map.setCenter(myLocation);
}
var onGPSError = function() {
console.log("GPS Error");
};
function GenerateFakeMovement() {
var currentPosition = myLocationMarker.getPosition();
var newLat = currentPosition.lat() + 0.01;
var newLng = currentPosition.lng() + 0.01;
MoveMarkerAndCircle(newLat, newLng);
}
</script>
</head>
<body style="height:100%;text-align:center" onload="onDeviceReady()" onunload="onUnLoad()">
<div id="map_canvas" style="width: 100%;height:80%"></div>
MAKE FAKE MOVEMENT
</body>
</html>
May not be entirely related to your problem, but if you bind a circle to a marker you don't have to set the circle position manually.
if (myMarker == undefined) {
myMarker = new google.maps.Marker({
position: mouseEvent.latLng,
title: 'My Position',
map: map,
draggable: true
});
var circle = new google.maps.Circle({
map: map,
radius: 100000, // 100 km
fillOpacity: 0.1,
strokeOpacity: 0.5,
strokeWeight: 1
});
circle.bindTo('center', myMarker, 'position'); //This will set the circle bound to the marker at center
}
So you only need to update the position of the marker.
Hope this helps.
I hope that I'm doing the right thing by posting this as an "answer". I still have the issue, but I have narrowed my own example down to the point that it seems clear it is an issue with Google Maps API v3 on Android 4.1.1 only. The code for the narrowed down test case is below and no longer involves PhoneGap.
I've posted the test page online here:
http://kcwebprogrammers.com/MarkerTest-NoGps.html
If you browse there in Windows (Chrome or IE) or with the Internet browser on Android 2.3, then moving the circle works just fine. If you browse to the same page with the Internet browser on Android 4.1.1, then you'll see that the original circle is still visible after the circle is moved, causing two circles. So I've posted the issue to the Google Maps issue list.
Thanks for the suggestions above. Please let me know if anyone thinks I should do anything different with this post.
<!DOCTYPE HTML>
<html>
<head>
<title>Marker Test</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
html {height: 100%}
body {height: 100%; margin:0; padding:0}
#map_canvas {height: 100%}
</style>
<script type="text/javascript" charset="utf-8" src="http://maps.googleapis.com/maps/api/js?key=AIzaSyDTGR9VjM2t5ixumkk_jHg155s0C1S5g3Y&sensor=true"></script>
<script type="text/javascript" charset="utf-8">
// globals
var watchID = null;
var map = null;
var myLocationMarker = null;
var searchCircle = null;
var myLocation = null;
function onLoad() {
startGPS();
}
function onUnLoad() {
console.log("clearing watch " + watchID);
}
function startGPS() {
// simulate getting position from GPS
myLocation = new google.maps.LatLng(39, -90);
// create Google map
var mapOptions = {
zoom: 14,
mapTypeId: google.maps.MapTypeId.ROADMAP,
center:myLocation
};
map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
google.maps.event.trigger(map, 'resize');
myLocationMarker = new google.maps.Marker({
title: 'This is me!',
zIndex: 90,
optimized: false,
map:map,
position: myLocation
});
searchCircle = new google.maps.Circle({
fillColor: '#c0e4dd',
strokeColor: '#f15f22',
fillOpacity: 0.5,
radius: 1500,
map:map,
center:myLocation
});
// using bindTo instead does not fix the problem
// searchCircle.bindTo('center', myLocationMarker, 'position'); //This will set the circle bound to the marker at center
}
var MoveMarkerAndCircle = function(lat, lng) {
myLocation = new google.maps.LatLng(lat, lng);
myLocationMarker.setPosition(myLocation);
// set circle invisible before moving and visible after does fix redraw for 4.1.1
// but causes some annoying flashing of the circle for 4.1.1 and everything else
// searchCircle.setVisible(false);
searchCircle.setCenter(myLocation);
// searchCircle.setVisible(true);
}
var GenerateFakeMovement = function() {
var currentPosition = myLocationMarker.getPosition();
var newLat = currentPosition.lat() + 0.001;
var newLng = currentPosition.lng() + 0.001;
MoveMarkerAndCircle(newLat, newLng);
}
</script>
</head>
<body style="height:100%;text-align:center" onunload="onUnLoad()" onload="startGPS()">
<div id="map_canvas" style="width: 100%;height:90%"></div>
MOVE MARKER AND CIRCLE
</body>
</html>
I need help with a little problem I can't seem to get a grip to:
I have spans that show up on the bottom of the screen when I touch a character (a lot of characters -> a lot of spans). In the span are some information about the character - as I want to make it mobilefriendly I want the user to scroll throught that information.
So far I found a little bit code that helped me a little:
<body onload="touchScroll('test')">
<script>
function isTouchDevice(){
try{
document.createEvent("TouchEvent");
return true;
}catch(e){
return false;
}
}
function touchScroll(id){
if(isTouchDevice()){ //if touch events exist...
var el=document.getElementById(id);
var scrollStartPos=0;
document.getElementById(id).addEventListener("touchstart", function(event) {
scrollStartPos=this.scrollTop+event.touches[0].pageY;
event.preventDefault();
},false);
document.getElementById(id).addEventListener("touchmove", function(event) {
this.scrollTop=scrollStartPos-event.touches[0].pageY;
event.preventDefault();
},false);
}
}
And the span with the id "test" becomes scrollable. Great. But as I have hundreds of span I can't assign an id to every single one. So I was wondering if you could help come up with a way to make every class "tt" scrollable.
I tried reassigning the "test"-id to the next element upon touch. But that didn't work. I tried adding the "test"-id to every class "tt" - didn't work either.
If you want to make yourself a picture of the situation: Here's my testsite!
I found a solution on this site in the comments:
<script type='text/javascript'>
// Determine if this is a touch device
var hasTouch = 'ontouchstart' in document.documentElement,
startEvent = hasTouch ? 'touchstart' : 'mouseover',
moveEvent = hasTouch ? 'touchmove' : 'mousemove',
endEvent = hasTouch ? 'touchend' : 'mouseup';
var Last;
$(".tttw").live(startEvent, function() {
if (Last) Last.removeClass('ttth');
$(this).addClass("ttth");
Last=$(this);
});
function isTouchDevice(){
try{
document.createEvent("TouchEvent");
return true;
}catch(e){
return false;
}
}
function touchScroll(selector) {
if (isTouchDevice()) {
var scrollStartPosY=0;
var scrollStartPosX=0;
$('body').delegate(selector, 'touchstart', function(e) {
scrollStartPosY=this.scrollTop+e.originalEvent.touches[0].pageY;
scrollStartPosX=this.scrollLeft+e.originalEvent.touches[0].pageX;
});
$('body').delegate(selector, 'touchmove', function(e) {
if ((this.scrollTop < this.scrollHeight-this.offsetHeight &&
this.scrollTop+e.originalEvent.touches[0].pageY < scrollStartPosY-5) ||
(this.scrollTop != 0 && this.scrollTop+e.originalEvent.touches[0].pageY > scrollStartPosY+5))
e.preventDefault();
if ((this.scrollLeft < this.scrollWidth-this.offsetWidth &&
this.scrollLeft+e.originalEvent.touches[0].pageX < scrollStartPosX-5) ||
(this.scrollLeft != 0 && this.scrollLeft+e.originalEvent.touches[0].pageX > scrollStartPosX+5))
e.preventDefault();
this.scrollTop=scrollStartPosY-e.originalEvent.touches[0].pageY;
this.scrollLeft=scrollStartPosX-e.originalEvent.touches[0].pageX;
});
}
}
$(document).ready(function(){
touchScroll('.tt');
});
</script>
Working like a charm now ;-)
Does somebody know how to hide URL on Android (like full screen) using HTML/javascript?
On iPad (Safari) this is simple and can be done using only a few meta tags.
I have tried something like that:
$(document).ready(function () {
scrollTo(0, 1);
});
But, on Motorola T1, the URL bar is still displayed :(
None of any solution above worked for me on Samsung S3 mini phone with Android 4.1.1
But I have followed the mentioned url and there was the absolutely right solution.
Thanks for it.
https://gist.github.com/1183357
See Fresheyeball's implementation. That works perfectly in portrait and landscape mode as well.
I just copy here my full example:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<script type="text/javascript" src="jquery-1.8.3.min.js"></script>
<script type="text/javascript">
$(function() {
hideAddressBar();
});
function hideAddressBar() {
if (navigator.userAgent.match(/Android/i)) {
window.scrollTo(0, 0); // reset in case prev not scrolled
var nPageH = $(document).height();
var nViewH = window.outerHeight;
if (nViewH > nPageH) {
nViewH = nViewH / window.devicePixelRatio;
$('BODY').css('height', nViewH + 'px');
}
window.scrollTo(0, 1);
} else {
addEventListener("load", function() {
setTimeout(hideURLbar, 0);
setTimeout(hideURLbar, 500);
}, false);
}
function hideURLbar() {
if (!pageYOffset) {
window.scrollTo(0, 1);
}
}
return this;
}
</script>
</head>
<body>
<section>
<div>
<h1>First title</h1>
<p>Just some content</p>
</div>
</section>
<section>
<div>Any text</div>
</section>
</body>
</html>
Of course you need to put the jQuery main js file as well in order this example work properly. You can download from here http://jquery.com/download/
try this
$(document).ready(function() {
if (navigator.userAgent.match(/Android/i)) {
window.scrollTo(0,0); // reset in case prev not scrolled
var nPageH = $(document).height();
var nViewH = window.outerHeight;
if (nViewH > nPageH) {
nViewH -= 250;
$('BODY').css('height',nViewH + 'px');
}
window.scrollTo(0,1);
}
});
it works even if your page is not long enough
Try this one. I've used it and it seems to work perfectly on Android. It's from here:
https://gist.github.com/1183357
/*
* Normalized hide address bar for iOS & Android
* (c) Scott Jehl, scottjehl.com
* MIT License
*/
(function( win ){
var doc = win.document;
// If there's a hash, or addEventListener is undefined, stop here
if( !location.hash && win.addEventListener ){
//scroll to 1
window.scrollTo( 0, 1 );
var scrollTop = 1,
getScrollTop = function(){
return win.pageYOffset || doc.compatMode === "CSS1Compat" && doc.documentElement.scrollTop || doc.body.scrollTop || 0;
},
//reset to 0 on bodyready, if needed
bodycheck = setInterval(function(){
if( doc.body ){
clearInterval( bodycheck );
scrollTop = getScrollTop();
win.scrollTo( 0, scrollTop === 1 ? 0 : 1 );
}
}, 15 );
win.addEventListener( "load", function(){
setTimeout(function(){
//at load, if user hasn't scrolled more than 20 or so...
if( getScrollTop() < 20 ){
//reset to hide addr bar at onload
win.scrollTo( 0, scrollTop === 1 ? 0 : 1 );
}
}, 0);
} );
}
})( this );