jQuery wScratchPad: Responsive Design? - android

I have added the wScratchPad to a website project. And it's working.
But the project is based on responsive design. That means the size of all elements adapt to the screen size, so it's optimized for mobile devices too.
The situation is that the size of the scratch-area remains the same, no matter how small you make the browser-window.
<div id="wScratchPad"></div>
<script type="text/javascript">
var scratch = function(e, percent){
if ( percent > 50 ) {
sp.wScratchPad('clear');
}
}
var sp = $("#wScratchPad").wScratchPad({
width : 363,
height : 117,
realtimePercent : true,
scratchDown: scratch,
scratchMove: scratch,
cursor:'./cursors/coin.png',
scratchUp: scratch,
image: './images/1.png',
image2: './images/2.png'
});
</script>
The "width" and "height" are needed to calculate the overlaying canvas and the pixels for uncovering the background-image.
Is there any way to let the scratch area size adapt to the window-size?

One solution you could try would be to set the scratchpad's dimensions to be a percentage of the dimensions of the browser viewport, using document.documentElement.clientWidth/clientHeight. For example:
var sp = $("#wScratchPad").wScratchPad({
width : document.documentElement.clientWidth / 4,
height : document.documentElement.clientHeight / 4,
...
});

Related

How to detect Android device's default font size with CSS media queries?

In Android, Settings > Accessibility > Font Size the user can set font size between "Small", "Default", "Large", "Largest". Among other things, this setting affects default font size of HTML content in WebViews.
I've developed my layouts to look well with the default font size. Setting font size to "Largest" causes text to get cut off in some places, a horizontal scrollbar appear on others etc. In these cases I can use an alternative layout (for example, stack things vertically instead of horizontally), but I'm not sure how to detect which layout to use.
Ideally, I would use CSS media queries. Something like:
#foo {
display: flex;
}
#media (min-width: 360px) {
#foo {
/* If at least 360px available, use a horizontal layout */
flex-direction: row;
}
}
Problem is, the 360px breakpoint isn't affected by the Font Size setting on the device (which makes sense). I've also tried other measurement units: rem, ch, cm – but none of them seemed to take into account device's font size.
I've thought about doing something like this on page load:
display a line of constant text (say, "0000000000") on the screen
measure it in JS
if the measured width / device width ratio is above some set constant, switch to the alternative layout
But this approach would add complexity, delay and repaints and flicker on page load.
Is there a way to take device font size in account in CSS (media queries or otherwise)?
Short Answer
No, you cannot do this just using CSS. However you can minimise impact using a method similar to the one you mentioned in your question (measuring font size and adjusting layout accordingly).
Long Answer
You cannot do this with just CSS, however it is possible to have a performant website without repaints and fall-back to your default styles for no JS.
There is one downside to this method, you do end up injecting a style sheet into the page which will affect first contentful paint times. However bear in mind that this is essentially the same as having a matching media query (so in reality, there is no difference between this and a media query other than it relies on JavaScript).
You can mitigate this by inlining the relevant styles but obviously that carries a page weight cost. You will have to decide which is the greater sin!
The solution is quite simple.
(1) Work out the user's font size using the method similar to the one you described.
(2) Load in conditional CSS that overrides the key layout options as you desire.
(2a) Alternatively add a class to the body and change the layout based on that from styles within existing style sheets or inlined in the document if above the fold.
1. Work out the user's font size
You can do this in vanilla JS right within the header of the page as an inline script so it does not delay anything (other than parsing the script) and it will still be performant.
Try the below example with you font size set to "medium" first, then set your font-size to "extra large" and run the script again. Your font size should show as 16px and 24px respectively.
var el = document.getElementById('foo');
var style = window.getComputedStyle(el, null).getPropertyValue('font-size');
var fontSize = style;
console.log(style)
<div id="foo">a</div>
We now have a font size that is relative to the user's scaling.
We can further improve this by simply dividing the resultant font size by 16 (default size) to get a %age scale.
var el = document.getElementById('foo');
var style = window.getComputedStyle(el, null).getPropertyValue('font-size');
var fontSize = style;
var fontSizePercentage = parseFloat(style) / 16 * 100;
console.log(style, fontSizePercentage + "%");
<div id="foo">a</div>
2. Load in conditional CSS
Now that we know if the font size has been scaled by the user we simply conditionally load CSS.
To do this we need a simple JavaScript check
//set to whatever criteria you need, if your site still works on "large" font size and only needs adjustment at "extra large" then use 124 etc.
if(fontSizePercentage > 100){
//add the CSS
}
In the below example I have 3 columns that turn into 3 rows to demonstrate how font size dictates which styles to apply.
Please note to simulate a dynamically added CSS file I added some inline CSS code that gets written to a style sheet, you would obviously just add a style sheet (I have included a function to do this and just commented out where that function is called).
var el = document.getElementById('foo');
var style = window.getComputedStyle(el, null).getPropertyValue('font-size');
var fontSize = style;
var fontSizePercentage = parseFloat(style) / 16 * 100;
el.remove();
console.log(fontSizePercentage);
//this is just to simulate adding CSS, you would obviously import a style sheet properly here. I have put the proper function further down.
var normal = `
.col3{
float: left;
width: 32%;
margin-right: 1%;
}
`
var bigger = `
.col3{
float: left;
width: 100%;
}
`
var styleSheet = document.createElement("style");
styleSheet.type = "text/css";
if(fontSizePercentage > 100){
styleSheet.innerText = bigger;
}else{
styleSheet.innerText = normal;
}
document.head.appendChild(styleSheet);
////actual style sheet code/////
function addCss(fileName) {
var head = document.head;
var link = document.createElement("link");
link.type = "text/css";
link.rel = "stylesheet";
link.href = fileName;
head.appendChild(link);
}
if(fontSizePercentage > 100){
//addCss('url-to-large-font-size-layout');
}else{
//addCss('url-to-normal-font-size-layout');
}
<div id="foo">a</div>
<div class="col3">column</div>
<div class="col3">column</div>
<div class="col3">column</div>
You will see from the example we add the stylesheet dynamically, choosing between two style sheets in the example. In reality you would probably only need to do the check for the requirement to use the large fonts stylesheet as your standard font size will be covered by your main CSS.
pros
This method effectively is the same as a 'font size media query' and carries a tiny JavaScript overhead.
cons
If you are bothered by repaints then performance is obviously important to you, this method adds an additional request and can delay First Contentful Paint / Initial page rendering for "above the fold content".
Because of this I offer a second suggestion:
2a. Add a class to the body.
Use exactly the same method as above but instead of inserting a style sheet simply use the check for font size to add a class to the body on page load.
The simply include the styles within your current style sheets but with the additional body class as a qualifier.
var el = document.getElementById('foo');
var style = window.getComputedStyle(el, null).getPropertyValue('font-size');
var fontSize = style;
var fontSizePercentage = parseFloat(style) / 16 * 100;
el.remove();
var bod = document.getElementById('simulated-body');
if(fontSizePercentage > 100){
bod.classList.add('large-font-layout');
}
console.log(fontSizePercentage);
.col3{
float: left;
width: 32%;
margin-right: 1%;
}
.large-font-layout .col3{
float: left;
width: 100%;
}
<div id="simulated-body">
<div id="foo">a</div>
<div class="col3">column</div>
<div class="col3">column</div>
<div class="col3">column</div>
</div>
pros this doesn't add any extra requests, should not affect your page paint. Generally this option will be preferable to the first option as you should only need to override a handful of CSS classes so the added weight is negligible.
cons - adds extra weight to your CSS.
Conclusion
Somebody should add a "user font size" media query :-P
Seriously though I would go with the (2a) option I gave you and inline your critical CSS. If you are changing more than 100 classes in your CSS (and so CSS weight becomes a problem) then something is wrong with your design so the speed difference will be negligible. Couple that with the fact that the JS is less than 1kb and it will not affect your paints and it is a simple but effective solution to your problem.
Bonus info for option 2
As an additional thought you can combine option 2 with checking the screen width to really minimise the amount of data sent down the wire. However this then starts adding considerable complexity which is something you said you wanted to avoid. I have included it here for completeness.
function getFontSizePercentage(){
var el = document.getElementById('foo');
var style = window.getComputedStyle(el, null).getPropertyValue('font-size');
var fontSize = style;
var fontSizePercentage = parseFloat(style) / 16 * 100;
el.remove();
return fontSizePercentage;
}
function getPageWidth(){
return Math.max(
document.body.scrollWidth,
document.documentElement.scrollWidth,
document.body.offsetWidth,
document.documentElement.offsetWidth,
document.documentElement.clientWidth
);
}
function addCSS(fileName) {
var head = document.head;
var link = document.createElement("link");
link.type = "text/css";
link.rel = "stylesheet";
link.href = fileName;
head.appendChild(link);
}
var fontSizePercentage = getFontSizePercentage();
var pageWidth = getPageWidth();
var cssSize = "1920";
switch(true) {
case (pageWidth < 1366):
cssSize = "1366";
break;
case (pageWidth < 728):
var cssSize = "728";
break;
default:
cssSize = "1920";
}
var cssPath = "styles/screen-width-" + cssSize + "-font-size-adjust";
if(fontSizePercentage > 100){
console.log("adding CSS for width: " + cssSize, cssPath);
addCSS(cssPath);
}else{
console.log("not adding CSS", cssPath);
}
<div id="foo">a</div>
you can use the VH (viewport height) or VW(viewport width) font-size measures and the font will resize based on % of device viewport for example:
1vh = 1% of viewport heigth
2vw = 2% of viewport width etc
If you are doing media selector you could as well use rem or px as font-size.
You will still have to add #media selector for different devices, keep in mind viewport is the actual size of a device screen, like 10inch screens will have a #media selector up to 1280px or bellow.
/* please use caniuse.com for browser compatibility table*/
h1 {
font-size: 2.5vw;
}
h2 {
font-size: 2vw;
}
p {
font-size: 12px;
}
#media only screen and (max-width:768px) {
h1 {
font-size: 5vw;
}
h2 {
font-size: 4vw;
}
p {
font-size: 18px;
}
}
<h1>text</h1>
<h2>text</h2>
<p>some normal font controled by media query</p>
Is there a way to take device font size in account in CSS (media queries or otherwise)?
Media queries are used to set font size based on device width, not the contrary.
The unit you might use will always be based on the device width, not on the font size (as this can't be already defined at this point).
What you can do is, instead of defining the width of your columns based on percentage or using px units, you can set them on the base font size.
For instance, instead of
.flex-col {
flex: 0 1 30%;
}
you may use
.flex-col {
flex: 0 1 20rem;
}
The real problem are not your media queries but the fact that your containers are not defined in a responsive way (with both fix width and height for instance) causing cut off and scrollbars.

Viewport meta tag ignored in Android 4.4 WebView

I have an App/WebApp that runs on Android and IOS. I deploy it both as a WebApp and a native PhoneGap app on IOS and Android. I build the native apps with PhoneGap Build. The tablet UI is designed for an internal pixel width of 768 which works fine on iPad and iPad Mini. For Nexus (601px wide in portrait) I fake it by setting the viewport width to 768px and setting the scaling as:
var gViewportScale = (Math.min(window.screen.width, window.screen.height) / window.devicePixelRatio) / 768;
if (gViewportScale >= 1) {
document.getElementById("viewport").content
= "width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no";
} else {
document.getElementById("viewport").content
= "width=768, initial-scale=" + gViewportScale + ", minimum-scale=" + gViewportScale + ", maximum-scale=" + gViewportScale + ", user-scalable=yes";
}
This mimics the iPad Mini behavior. This worked fine up until several months ago. I think this may have broken when I upgraded to Android 4.4 with the change to Chromium. Now the active area for the Android PGB native App is wider than the display. The WebApp continues to display properly in Android Chrome (33.0.1750.136) as it did before (i.e. it fills the display width in portrait). Also works fine as a native App or WebApp in IOS.
I've tried several alternatives and all the current versions of PGB and near as I can tell the Android WebView is ignoring the viewport meta tag with PhoneGap but not on Chrome. It certainly seems like an Android bug that it works on one and not the other.
I've asked this question in the PhoneGap support community and so far no good answers. I thought I'd try here. Of course, I could have separate CSS for Nexus, but true scaling of widths and fonts would be better.
You need to set webView.getSettings().setUseWideViewPort(true); else WebView will ignore the meta tag
I have a workaround that doesn't fix the fact that the viewport tag is ignored, but it solves the original viewport scaling problem in a manner that's probably better than playing with the viewport tag.
Inspired by:
Dynamic viewport resizing - Phonegap ignores viewport
and monaca:
https://github.com/monaca/monaca.js
I used a fixed viewport tag content:
width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no
and then use zoom, which Android and Chrome apparently support:
/*
* Make sure that the device is scaled so that it is at least minWidth px in width
* in any orientation. This is done by setting the zoom appropriately.
* Right now, we only need this on Android, which supports zoom.
* Plan B is to use transforms with scale and transform-origin.
*/
function setupScale (minWidth) {
var viewWidth = Math.max(document.documentElement.clientWidth, window.innerWidth);
var viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight);
var portWidth = Math.min(viewWidth, viewHeight);
var landWidth = Math.max(viewWidth, viewHeight);
var fixScale = function () {
if (Math.abs(window.orientation) != 90) {
// portrait
document.body.style.zoom = portWidth / minWidth;
} else if (landWidth < minWidth) {
// landscape, but < minWidth
document.body.style.zoom = landWidth / minWidth;
} else {
// landscape >= minWidth. Turn off zoom.
// This will make things "larger" in landscape.
document.body.style.zoom = 1;
}
};
if (gPortWidth >= minWidth) {
return; // device is greater than minWidth even in portrait.
}
fixScale(); // fix the current scale.
window.onorientationchange = fixScale; // and when orientation is changed
}
For my app, it lets me set the minimum width of the webpage as 768. Which is the same as the iPad (Mini and regular). On the Nexus 7 which is reports screen width of 601px, I can use the same CSS as iPad.
I just created a little plugin to fix this prob (by using setUseWideViewPort(true)): https://github.com/gitawego/cordova-viewport-fix, if you don't wanna modify directly in the source code, you can use this plugin.
In order to use viewport in webView you need to enable setUseWideViewport and setLoadWithOverviewMode
mWebView.getSettings().setUseWideViewPort(true);
mWebView.getSettings().setLoadWithOverviewMode(true);
Refer: Pixel perfect UI in the WebView

Device width in phonegap android

I am developing an android application in phonegap / HTML5.
As its first step I am taking device's width using
var viewport = {
width : parseInt($(window).width()),
height : parseInt($(window).height())
};
alert(viewport.width);
But first time wile deploying its showing 320 as alert, but when I restart application its showing 0. after that oftenly showing 320 ( in rare )
How to get the actual width of the device permanently ?
Sorry for my typos
have you tried this
$(function(){
document.addEventListener("deviceready", function() {
var windowWidth = $(window).width();
var windowHeight = $(window).height();
});
});
This question is made every now and then. See my previous answer at, and read the blog article itself: JavaScript+Phonegap+Android: screen.width returns bad (almost random) values
Hope this helps you out.

Get webpage center coordinates on tablet devices using JavaScript

I'm trying to compute the viewport geometric center on a webpage, rendered with Tablet devices (iOS and Android), ie the actual CENTER of the viewport (what you see) according to current translation and current zoom level - don't want the center of the document itself, I want the center of the screen what I'm viewing.
The problem is that this calculation is does not take into account any kind of zoom (an then) translation.
On iOS, I've tried with some of these answers, on the question detecting pinch to zoom on iOS,
I was able to catch the event "OnZoom" but didn't get any value, though.
On Android, I can't even catch any event related to zoom. I'm aware of touchmove and touchstart events, but how can I distinguish then in order to get zoom (and zoom value)
I'm using jQuery 1.7.2 library.
I have made a demo page which is confirmed to work on iPhone iPad, Samsung Galaxy Tab 10. I have only attached the click event on a huge div, so tap on the screen after zooming to update the display.
The calculation is very simple, use screen.width/window.innerWidth to get the zoom level. screen.width will always be in device pixels and window.innerWidth is always in css pixels, which also take the zoom into account.
Further calculation is simple math:
// round the result down to avoid "half pixels" for odd zoom levels
Math.floor(window.scrollY + window.innerHeight/2);
Math.floor(window.scrollX + window.innerWidth/2);
To check whether the user is zooming, attach the listener to window.resize and window.scroll which will fire after orientationchange hiding the address bar and zooming.
Here's my demo JavaScript:
var dot = document.getElementById("dot");
document.getElementById("main").addEventListener("click", function(e) {
var zoom = screen.width / window.innerWidth;
alert("zoom: " + zoom + "\n" + "ScrollY: " + window.scrollY);
dot.style.top = Math.floor(window.scrollY + window.innerHeight/2 - 5) + "px";
dot.style.left = Math.floor(window.scrollX + window.innerWidth/2 - 5) + "px";
}, false);
Setting up a simple HTML page, I can get pretty close to center.
With the code below, I'm just checking the width and height of a fixed div, and then combining that with the document offset and some simple maths to figure out the center, and place a single black dot there. I can get it pretty close, but it varies on my iPhone 4S.
Haven't tried on Android devices.
I don't think this would work on iOS <= 4, as they don't support fixed-positioning.
<style>
#fixed{
position: fixed;
left: 0; right: 0; top: 0; bottom: 0;
background: orange; opacity: 0.25;
}
.black{
position: absolute;
top:0;left:0;
width: 1px; height: 1px;
background: black;
}
</style>
<body>
<div id="fixed"></div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
<script>
jQuery(function($){
setInterval(function(){
var top = ($("#fixed").height()/2) + $("#fixed").offset().top;
var left = ($("#fixed").width()/2) + $("#fixed").offset().left;
$("body").append('<div class="black" style="top: '+top+'px; left: '+left+'px;" />');
}, 1500)
});
</script>
</body>
I had a client request a page zoom detection method in javascript a couple years ago.
In my case, he wanted it to work on a facebook app. Through the iframe canvas/viewport.
I used Max and Min functions
function getDocHeight() {
var D = document;
return Math.max(
Math.max(D.body.scrollHeight, D.documentElement.scrollHeight),
Math.max(D.body.offsetHeight, D.documentElement.offsetHeight),
Math.max(D.body.clientHeight, D.documentElement.clientHeight)
);
}
function getDocWidth() {
var D = document;
return Math.max(
Math.max(D.body.scrollWidth, D.documentElement.scrollWidth),
Math.max(D.body.offsetWidth, D.documentElement.offsetWidth),
Math.max(D.body.clientWidth, D.documentElement.clientWidth)
);
}
function getMinHeight(h) {
return Math.min(viewport.currentHeight, getDocHeight(), h);
}
getMinWidth was similar, but I had to apply browser-based tweaks to it.
I created an object called viewport which stored the properties of a fixed position div, specifically currentHeight and currentWidth were the offsetHeight and offsetWidth of the div element.
I ended up initializing a window.intervalTimer to run checks on the state of that div, comparedTo the stored values within the viewport object.

How to fix viewport scaling with JQuery Mobile on Android HDPI devices

I find that JQuery Mobile pages look good on MDPI devices (like G1) but they look extremely small on HDPI devices (like Samsung Galaxy S).
Here image from Android emulator with resolution 320x480 and 160 dpi:
Here image from Android emulator with resolution 480x800 and 240 dpi:
To see disproportions compare size of JQuery text with size of native Android interface (clock).
EDITED: screenshots taked with the following viewport settings:
<meta name="viewport" content="initial-scale=1, width=device-width, target-densitydpi=device-dpi"/>
From JQM version 1.1 hack with zoom level doesn't work any more because zoom is disabled by JQM.
Page meta[name='viewport'] content, after activity is started and first page is loaded:
initial value initial-scale=1
JQM 1.0.1 initial-scale=1
JQM 1.1.0 initial-scale=1,maximum-scale=1, user-scalable=no
So the solution is to remove densitydpi=device-dpi from meta[name='viewport'] content, because it's preventing "native scaling" performed by android (see thorough explanation on http://developer.android.com/guide/webapps/targeting.html, section Defining the viewport target density).
The most effective way to maintain consistency between screen sizes across Android devices is to change the zoom level of the WebView that loads you (web)app.
It doesn't require code changes at HTML level.
public class YourApp extends DroidGap {
/** Called when the activity is first created. */
// declare the original size of the iPad app
protected float ORIG_APP_W = 320;
protected float ORIG_APP_H = 480;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.loadUrl("file:///android_asset/www/index.html", 3000);
// get actual screen size
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
// calculate target scale (only dealing with portrait orientation)
double globalScale = Math.ceil( ( width / ORIG_APP_W ) * 100 );
// make sure we're all good
Log.v( TAG, "ORIG_APP_W" + " = " + ORIG_APP_W );
Log.v( TAG, "ORIG_APP_H" + " = " + ORIG_APP_H );
Log.v( TAG, "width" + " = " + width );
Log.v( TAG, "this.appView.getMeasuredHeight() = " + height );
Log.v( TAG, "globalScale = " + globalScale );
Log.v( TAG, "this.appView.getScale() index=" + this.appView.getScale() );
// set the scale
this.appView.setInitialScale( (int)globalScale );
}
}
Yep, the resolutions are not proportional across the devices you mentioned, right?
ie. 320:480 is not same as 480:800
The device has upscaled your app's width and height proportionally, which leaves you with empty space in the height component.
So that has nothing to do with jQuery as such, just that your app needs design enhancements to support high end devices.
I don't know what your requirement is but here's what I did to solve this for my app:
Introduce useful content to fill up the height for the high res devices. To explain at a code level, I had additional content inside a div with id moreContentForHighEndDevices, and used CSS media queries to conditionally display it, like below:
#moreContentForHighEndDevices {
display:none;
}
#media screen and (-webkit-device-pixel-ratio: 1.5) {
#moreContentForHighEndDevices {
display: block;
}
}

Categories

Resources