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.
Related
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.
For an android webview application, a dynamic webview page (page A) loads items from local storage correctly when the page A loads for the first time. Then, on Page B another local storage item is added. Then, page A is loaded using window.location.herf= "page A". But, page A does not load the extra local storage item added on Page B. It seems like page A always loads from cache after the 1st time load. This problem happens with any android device or emulator running on any android 4.3 or less operating system.
NOTE:*************
Page A refreshes correctly on an emulator running on android 4.4 or on chrome web.
So... I want a work around for any android OS 4.3 or less.
html.js function that dynamically fills content on page A
function ShowResults(value) {
var overNight = getNetWork(value); /// it the function to get local storage based on value key
document.write('<li><a href="file:///android_asset /SettingEditProgram.html?overnightkey='+value+'">')
document.write('<div class="column">')
document.write('<div class="rectangle"><network>'+ overNight.network + '</network></br>')
document.write('<program>'+ overNight.program +'</program></br>')
document.write('<text>' + overNight.start +'-'+ overNight.end + '</text></br>')
document.write('<text>' + overNight.demo + ', ' + overNight.region + '</text>')
document.write('</div>')
document.write('</div></a></li>')
}
Page A:_________________________
<head>
<meta http-equiv='cache-control' content='no-cache'>
<meta http-equiv='expires' content='-1'>
<meta http-equiv='pragma' content='no-cache'>
<meta http-equiv="refresh" content="4">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.5.min.js"></script>
<link rel="stylesheet" href="file:///android_asset/style.css" type="text/css" />
<script type="text/javascript" src="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.js"></script>
<script src="html.js"></script>
<script>
$(document).ready(function() {
$.ajaxSetup({ cache: false});
var parameters = $(location).attr('search');
parameter = parameters.replace("?overnightkey=","");
// 'Setting' data-attributes using setAttribute
var newKey = document.getElementById("overnight");
var oldValue = newKey.getAttribute("data-overnight",newKey);
newKey.setAttribute("data-overnight",newKey);
var newKeyvalue ="oldValue="+oldValue+" newValue:"+newKey;
console.log("newKeyvalue");
});
</script>
</head>
<body>
<div data-role="page" id="album-list">
<div id="headerBar">
<img class="goback" src="file:///android_asset/img/ic_goback-web.png">
<img class="appicon" src="file:///android_asset/img/ic_launcher-web.png"><p class="apptitle">Program Setting</p>
<img class="add" src="file:///android_asset/img/ic_add-web.png">
</div>
<div data-role="content">
<ul id="overnight" data-role="listview" data-split-icon="gear" data-split-theme="d" data-overnight="0">
<script>
getIDs().forEach(ShowResults);
</script>
</ul>
</div><!-- /content -->
</div><!-- /page -->
</body>
</html>
page B that set local storage and then re-direct to page A:
$('#savebutton').click(function() {
var keyOverNight = localStorage.getItem("keyOverNightAll");
var keyOverNightArray = new Array();
if (keyOverNight) {
try {
keyOverNightArray = JSON.parse(keyOverNight); // use JSON3 to handle multiple browsers/versions
} catch (e) {
//keyOverNightArray = new Array(); // for parse failure --> return an empty Array
// do nothing
}
}
//Get a new key for setting page based on the local storage
//OvernightKeys
var newKey;
if (keyOverNightArray.length == 0)
{
newKey =1;
}
else
{
newKey= keyOverNightArray[keyOverNightArray.length-1]+1;
}
//Store the overnight key
keyOverNightArray.push(newKey);
localStorage.setItem("keyOverNightAll",JSON.stringify(keyOverNightArray));
//Now store Program values into the newKey
var overnightKey = "overNight"+ newKey.toString();
var jsonOvernight = {"network": NetworkSelected, "program": ProgramSelected, "start": STimeSelected, "end": ETimeSelected, "region": RegionSelected, "demo": DemoSelected }
localStorage.setItem(overnightKey, JSON.stringify(jsonOvernight));
//window.location = "file:///android_asset/SettingHomePage.html";
window.location.href = 'file:///android_asset/SettingHomePage.html?newKey='+newKey;
//window.location.reload(true);
});
Things I tried on Page A
Refreshed every 10 seconds.. It does not read the newest element from local storage.
Set the cache to no content
Set a new data-attribute everytime it comes to this page after adding an item on PageB
tricked page A to load a html.js script with time stamp on it.. eg. html.js?'+timeStamp+'somestring:"
reloads page A. then it runs infinitely.
If I closed the app, and re-run it, it will load the newest localstoarge items again only on the 1st load.
I've tried almost most of the stack over flow help.. Cannot find any answer yet.
check out targetSdkVersion.
I meet same problem. I set targetSdkVesrion to under 16. then sloved.
I want to make an app which gets my coördinates and shows them in google maps (using phonegap and testing on emulator 2.2). Then stores those coördinates in localstorage. I have never used localstorage before so i am not sure on how to do this. I got the geolocation part working. If anyone could adjust my code or show a tutorial which explains how to save the lat/long that i will retreive as i don't have a lot of experience using web-langauges. It would be greatly appreciated.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta name="viewport" content="width=device-width; height=device-height; user-scalable=no" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Beer Me</title>
<link rel="stylesheet" href="/master.css" type="text/css" media="screen" />
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true"></script>
<script type="text/javascript" charset="utf-8" src="cordova-2.0.0.js"></script>
<script type="text/javascript">
function onLoad() {
document.addEventListener("deviceready", onDeviceReady, false);
}
function onDeviceReady() {
}
//GEOLOCATION
var onSuccess = function(position) {
var myLat = position.coords.latitude;
var myLong = position.coords.longitude;
//MAP
var mapOptions = {
center: new google.maps.LatLng(myLat, myLong),
zoom: 14,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map_canvas"),
mapOptions);
var marker = new google.maps.Marker({
position: new google.maps.LatLng(myLat, myLong),
map: map,
title:"Hello World!"
});
};
// onError Callback receives a PositionError object
//
function onError(error) {
alert('code: ' + error.code + '\n' +
'message: ' + error.message + '\n');
}
navigator.geolocation.getCurrentPosition(onSuccess, onError,{'enableHighAccuracy':true,'timeout':10000});
</script>
</head>
<body onload="onLoad()">
<div id="map_canvas" style="width:400px; height: 400px;">
</div>
</body>
</html>
I would suggest you use the SQLite Databases from Android.
You can then store the locations with two fields and access the data with SQL queries.
A nice tutorial is the one from Lars Vogel.
You could also use the Internal Storage from Android. Using a simple file is easy but not as powerful as the sql variant. When the data size increases reading and storing to the file becomes more cumbersome.
When trying to execute a phonegap plugin method, the application raises this error:
TypeError 'undefined' is not a function (evaluating 'cordova.exec( ...
Code included in app:
Javascript plugin link (file settingswrite.js)
window.SettingsWrite = function(objectData, successCallback, failureCallback) {
var options = {};
for (var key in objectData) {
options[key] = objectData[key];
}
cordova.exec(
successCallback,
failureCallback,
'SettingsWrite',
'set',
new Array(options)
);
};
Javascript code to make use of plugin (file app.js)
function setActualPosition() {
// appMap is an application global object
var map = appMap.getMapEdgesProjection();
window.SettingsWrite([{
x: map.minh,
y: map.maxh
}],
function(r){
alert(r);
},
function(e){
alert("Operation error");
console.log('ERROR: ' + e);
});
}
Plugin is declared in config.xml as:
<plugin name="SettingsWrite" value="es.mycompany.cordova.plugin.SettingsWrite"/>
Javascript code is executed index.html (located at assets folder and included into WebView):
<!DOCTYPE html>
<html>
<head>
<title>Test Mobile</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<meta name="apple-mobile-web-app-capable" content="yes">
<script src="assets/js/cordova-2.0.0.js"></script>
<script src="assets/js/jquery-1.7.2.min.js"></script>
<script src="assets/js/settingswrite.js"></script>
<script src="assets/js/app.js"></script>
</head>
<body>
<div id="divMapContainer"></div>
</body>
</html>
And the plugin Java class (as defined in Phonegap docs):
public class SettingsWrite extends Plugin {
public static final String ACTION = "set";
#Override
public PluginResult execute(String action, JSONArray data, String callbackId) {
if(ACTION.equals(action)) {
.
.
.
} else {
.
.
.
}
}
The plugin runs in Android 3.1 device and all needed files are correctly included to the project(cordova-2.0.0.js and cordova-2.0.0.jar). May someone help me?
At what point are you calling setActualPosition? Has the deviceready event fired? If it has not fired, you will not have access to the cordova objects.
I was getting the same error as you. There is a fix in this post:
datePicker plugin not working in Phonegap 2.0
Trying to load an image into onto an html5 canvas and then running the html5 on Android using Phonegap. Here is my HTML.
<!DOCTYPE HTML>
<html>
<body>
<canvas id="myCanvas" width="200" height="100" style="border:1px solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var cxt=c.getContext("2d");
var img=new Image()
img.src="img_flwr.png"
cxt.drawImage(img,0,0);
</script>
<img src="img_flwr.png"/>
</body>
</html>
I have included the standard img tag to demonstrate the problem.
Under Firefox, this page correctly shows the image rendered on the canvas and in the standard img tag.
When I deploy to Android emulator using Phonegap, only the standard img displays the picture.
Both the html and the .png file are in the assets/www folder of my phonegap project.
How can I get the image to render correctly on the canvas?
EDIT.. Fixed (thanks Avinash).. its all about timing.. you need to wait until the img is loaded before drawing onto the canvas ..vis
var c=document.getElementById("myCanvas");
var cxt=c.getContext("2d");
var img=new Image()
img.src="img_flwr.png";
img.onload = function() {
cxt.drawImage(img,0,0);
};
It might be the delay in loading the image(it's just a Guess of mine, I'm not sure. If it helps I'll be Glad) ...
Try this code(which waits until the page is loaded completely)
<!DOCTYPE HTML>
<html>
<body>
<canvas id="myCanvas" width="200" height="100" style="border:1px solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
<script type="text/javascript">
window.onload = function() {
var c=document.getElementById('myCanvas');
var cxt=c.getContext('2d');
var img=new Image();
img.src='img_flwr.png';
cxt.drawImage(img,0,0);
};
</script>
<img src="img_flwr.png"/>
</body>
</html>
Or you can do after the Image is loaded as follows
var c=document.getElementById('myCanvas');
var cxt=c.getContext('2d');
var img=new Image();
img.onload = function() {
cxt.drawImage(img,0,0);
};
img.src='img_flwr.png';
Please let me know if the problem still persists ...
I haven't had a chance to try this but try to refer to your image as:
file://android_asset/www/img_flwr.png
I tried without the img tags for PhoneGap 1.1.0 Xcode 4.2 and the Image() class onload events fire but the images report zero widths and heights. If the img html tags are added they work. So I am thinking device ready is not a place to do this or this shows an incompatibility with how a js programmer might expect the events and sequencing to work. Having the images floating around off screen open issues with multiple resolutions.
Here is the stub code.
var tileLoaded = true;
var dirtLoaded = true;
function onBodyLoad()
{
document.addEventListener("deviceready", onDeviceReady, false);
}
/* When this function is called, PhoneGap has been initialized and is ready to roll */
/* If you are supporting your own protocol, the var invokeString will contain any arguments to the app launch.
see http://iosdevelopertips.com/cocoa/launching-your-own-application-via-a-custom-url-scheme.html
for more details -jm */
function onDeviceReady()
{
// do your thing!
//navigator.notification.alert("PhoneGap is working");
console.log("device on ready started");
canvas = document.getElementById('myCanvas');
c = canvas.getContext('2d');
canvas.addEventListener('mousedown', handleMouseDown, false);
window.addEventListener('keydown', handleKeyDown, false);
tileMap = [];
tile.src = "img/tile.png";
//tile.onLoad = tileImageLoaded();
dirt.src = "img/dirt.png";
//dirt.onLoad = dirtImageLoaded();
console.log("device on ready ended");
draw();
}
function tileImageLoaded()
{
console.log("tile loaded");
console.log("draw - tile.width:" + tile.width);
console.log("draw - tile.height:" + tile.height);
tileLoaded = true;
draw();
}
function dirtImageLoaded()
{
console.log("dirt loaded");
console.log("draw - dirt.width:" + dirt.width);
console.log("draw - dirt.height:" + dirt.height);
dirtLoaded = true;
draw();
}