Performance: Looping through array to update positions - javascript

I'm developing a relatively simple Cordova application and I'm trying to position elements around the screen based on device orientation.
I'm listening for a change in orientation and firing an event:
window.addEventListener('deviceorientation', function(e) {
doTheThings(e.alpha);
});
$scope.doTheThings(d) {
for (var i = 0; i < $scope.myThings.length; i++) {
// I've calculated their bearing to the user already. myThings is an array of objects.
var compass = $scope.myThings[i].bearing - degree;
if (compass < 0) {
compass = 360 + compass;
} else if (compass > 360) {
compass = ((compass / 360) - 1) * 360;
}
var top = 0;
var left = 0;
// TOP if 315 - 44 (LEFT SET)
if (compass > 314 || compass <= 44) {
if (compass > 314) {
left = ((compass - 315) / 45) * halfCanvas;
} else {
left = ((compass / 45) * halfCanvas) + (halfCanvas);
}
} else if (compass > 44 && compass <= 134) {
left = canvasMaxWidth;
top = ((compass - 45) / 90) * canvasMaxHeight;
} else if (compass > 134 && compass <= 224) {
top = canvasMaxHeight;
left = (1 - ((compass - 135) / 90)) * canvasMaxWidth;
} else if (compass > 224 && compass <= 314) {
top = (1 - ((compass - 225) / 90)) * canvasMaxHeight;
}
$scope.$apply(function() {
$scope.myThings[i].positioned = true;
$scope.myThings[i].top = top;
$scope.myThings[i].left = left;
});
}
}
It's not terribly complex code and it gets the job done. I'm displaying the items in the DOM as follows:
<div ng-repeat="thing in myThings" style="transform: translate3d({{ thing.left }}px, {{ thing.top }}px, 0px);">
Yay!
</div>
Works a treat on a powerful device (My Nexus 6P and Nexus 5), but slows a little with 15ish objects, but is essentially a slideshow on slower devices (iPhone 4s, for example). It renders maybe once every couple seconds - totally brutal and unacceptable.
I figure I'm probably doing something wrong here and there's a way to optimize this somehow. Any help?

Related

Is it impossible to get window scale? [duplicate]

How can I detect the page zoom level in all modern browsers? While this thread tells how to do it in IE7 and IE8, I can't find a good cross-browser solution.
Firefox stores the page zoom level for future access. On the first page load, would I be able to get the zoom level? Somewhere I read it works when a zoom change occurs after the page is loaded.
Is there a way to trap the 'zoom' event?
I need this because some of my calculations are pixel-based and they may fluctuate when zoomed.
Modified sample given by #tfl
This page alerts different height values when zoomed. [jsFiddle]
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js" type="text/javascript"/></script>
</head>
<body>
<div id="xy" style="border:1px solid #f00; width:100px;">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque sollicitudin tortor in lacus tincidunt volutpat. Integer dignissim imperdiet mollis. Suspendisse quis tortor velit, placerat tempor neque. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Praesent bibendum auctor lorem vitae tempor. Nullam condimentum aliquam elementum. Nullam egestas gravida elementum. Maecenas mattis molestie nisl sit amet vehicula. Donec semper tristique blandit. Vestibulum adipiscing placerat mollis.</div>
<button onclick="alert($('#xy').height());">Show</button>
</body>
</html>
Now it's an even bigger mess than it was when this question was first asked. From reading all the responses and blog posts I could find, here's a summary. I also set up this page to test all these methods of measuring the zoom level. [↑ broken link. Archived copy → here].
Edit (2011-12-12): I've added a project that can be cloned: https://github.com/tombigel/detect-zoom
IE8: screen.deviceXDPI / screen.logicalXDPI (or, for the zoom level relative to default zoom, screen.systemXDPI / screen.logicalXDPI)
IE7: var body = document.body,r = body.getBoundingClientRect(); return (r.left-r.right)/body.offsetWidth; (thanks to this example or this answer)
FF3.5 ONLY: screen.width / media query screen width (see below) (takes advantage of the fact that screen.width uses device pixels but MQ width uses CSS pixels--thanks to Quirksmode widths)
FF3.6: no known method
FF4+: media queries binary search (see below)
WebKit: https://www.chromestatus.com/feature/5737866978131968 (thanks to Teo in the comments)
WebKit: measure the preferred size of a div with -webkit-text-size-adjust:none.
WebKit: (broken since r72591) document.width / jQuery(document).width() (thanks to Dirk van Oosterbosch above). To get ratio in terms of device pixels (instead of relative to default zoom), multiply by window.devicePixelRatio.
Old WebKit? (unverified): parseInt(getComputedStyle(document.documentElement,null).width) / document.documentElement.clientWidth (from this answer)
Opera: document.documentElement.offsetWidth / width of a position:fixed; width:100% div. from here (Quirksmode's widths table says it's a bug; innerWidth should be CSS px). We use the position:fixed element to get the width of the viewport including the space where the scrollbars are; document.documentElement.clientWidth excludes this width. This is broken since sometime in 2011; I know no way to get the zoom level in Opera anymore.
Other: Flash solution from Sebastian
Unreliable: listen to mouse events and measure change in screenX / change in clientX
Here's a binary search for Firefox 4, since I don't know of any variable where it is exposed:
<style id=binarysearch></style>
<div id=dummyElement>Dummy element to test media queries.</div>
<script>
var mediaQueryMatches = function(property, r) {
var style = document.getElementById('binarysearch');
var dummyElement = document.getElementById('dummyElement');
style.sheet.insertRule('#media (' + property + ':' + r +
') {#dummyElement ' +
'{text-decoration: underline} }', 0);
var matched = getComputedStyle(dummyElement, null).textDecoration
== 'underline';
style.sheet.deleteRule(0);
return matched;
};
var mediaQueryBinarySearch = function(
property, unit, a, b, maxIter, epsilon) {
var mid = (a + b)/2;
if (maxIter == 0 || b - a < epsilon) return mid;
if (mediaQueryMatches(property, mid + unit)) {
return mediaQueryBinarySearch(
property, unit, mid, b, maxIter-1, epsilon);
} else {
return mediaQueryBinarySearch(
property, unit, a, mid, maxIter-1, epsilon);
}
};
var mozDevicePixelRatio = mediaQueryBinarySearch(
'min--moz-device-pixel-ratio', '', a, b, maxIter, epsilon);
var ff35DevicePixelRatio = screen.width / mediaQueryBinarySearch(
'min-device-width', 'px', 0, 6000, 25, .0001);
</script>
You can try
var browserZoomLevel = Math.round(window.devicePixelRatio * 100);
This will give you browser zoom percentage level on non-retina displays. For high DPI/retina displays, it would yield different values (e.g., 200 for Chrome and Safari, 140 for Firefox).
To catch zoom event you can use
$(window).resize(function() {
// your code
});
For me, for Chrome/Webkit, document.width / jQuery(document).width() did not work. When I made my window small and zoomed into my site such that horizontal scrollbars appeared, document.width / jQuery(document).width() did not equal 1 at the default zoom. This is because document.width includes part of the document outside the viewport.
Using window.innerWidth and window.outerWidth worked. For some reason in Chrome, outerWidth is measured in screen pixels and innerWidth is measured in css pixels.
var screenCssPixelRatio = (window.outerWidth - 8) / window.innerWidth;
if (screenCssPixelRatio >= .46 && screenCssPixelRatio <= .54) {
zoomLevel = "-4";
} else if (screenCssPixelRatio <= .64) {
zoomLevel = "-3";
} else if (screenCssPixelRatio <= .76) {
zoomLevel = "-2";
} else if (screenCssPixelRatio <= .92) {
zoomLevel = "-1";
} else if (screenCssPixelRatio <= 1.10) {
zoomLevel = "0";
} else if (screenCssPixelRatio <= 1.32) {
zoomLevel = "1";
} else if (screenCssPixelRatio <= 1.58) {
zoomLevel = "2";
} else if (screenCssPixelRatio <= 1.90) {
zoomLevel = "3";
} else if (screenCssPixelRatio <= 2.28) {
zoomLevel = "4";
} else if (screenCssPixelRatio <= 2.70) {
zoomLevel = "5";
} else {
zoomLevel = "unknown";
}
My coworker and I used the script from https://github.com/tombigel/detect-zoom. In addition, we also dynamically created a svg element and check its currentScale property. It works great on Chrome and likely most browsers too. On FF the "zoom text only" feature has to be turned off though. SVG is supported on most browsers. At the time of this writing, tested on IE10, FF19 and Chrome28.
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
svg.setAttribute('version', '1.1');
document.body.appendChild(svg);
var z = svg.currentScale;
... more code ...
document.body.removeChild(svg);
I found this article enormously helpful. Huge thanks to yonran. I wanted to pass on some additional learning I found while implementing some of the techniques he provided. In FF6 and Chrome 9, support for media queries from JS was added, which can greatly simplify the media query approach necessary for determining zoom in FF. See the docs at MDN here. For my purposes, I only needed to detect whether the browser was zoomed in or out, I had no need for the actual zoom factor. I was able to get my answer with one line of JavaScript:
var isZoomed = window.matchMedia('(max--moz-device-pixel-ratio:0.99), (min--moz-device-pixel-ratio:1.01)').matches;
Combining this with the IE8+ and Webkit solutions, which were also single lines, I was able to detect zoom on the vast majority of browsers hitting our app with only a few lines of code.
zoom = ( window.outerWidth - 10 ) / window.innerWidth
That's all you need.
You can use the Visual Viewport API:
window.visualViewport.scale;
It is standard and works both on desktop and mobile: browser support.
I have a solution for this as of Jan 2016. Tested working in Chrome, Firefox and MS Edge browsers.
The principle is as follows. Collect 2 MouseEvent points that are far apart. Each mouse event comes with screen and document coordinates. Measure the distance between the 2 points in both coordinate systems. Although there are variable fixed offsets between the coordinate systems due to the browser furniture, the distance between the points should be identical if the page is not zoomed. The reason for specifying "far apart" (I put this as 12 pixels) is so that small zoom changes (e.g. 90% or 110%) are detectable.
Reference:
https://developer.mozilla.org/en/docs/Web/Events/mousemove
Steps:
Add a mouse move listener
window.addEventListener("mousemove", function(event) {
// handle event
});
Capture 4 measurements from mouse events:
event.clientX, event.clientY, event.screenX, event.screenY
Measure the distance d_c between the 2 points in the client system
Measure the distance d_s between the 2 points in the screen system
If d_c != d_s then zoom is applied. The difference between the two tells you the amount of zoom.
N.B. Only do the distance calculations rarely, e.g. when you can sample a new mouse event that's far from the previous one.
Limitations: Assumes user will move the mouse at least a little, and zoom is unknowable until this time.
What i came up with is :
1) Make a position:fixed <div> with width:100% (id=zoomdiv)
2) when the page loads :
zoomlevel=$("#zoomdiv").width()*1.0 / screen.availWidth
And it worked for me for ctrl+ and ctrl- zooms.
or i can add the line to a $(window).onresize() event to get the active zoom level
Code:
<script>
var zoom=$("#zoomdiv").width()*1.0 / screen.availWidth;
$(window).resize(function(){
zoom=$("#zoomdiv").width()*1.0 / screen.availWidth;
alert(zoom);
});
</script>
<body>
<div id=zoomdiv style="width:100%;position:fixed;"></div>
</body>
P.S. : this is my first post, pardon any mistakes
Basically, we have:
window.devicePixelRatio which takes into account both browser-level zoom* as well as system zoom/pixel density.
* — on Mac/Safari zoom level is not taken into account
media queries
vw/vh CSS units
resize event, which is triggered upon zoom level change, cause effective size of the window changes
that should be enough for normal UX. If you need to detect zoom level that might be a sign of bad UI design.
Pitch-zoom is harder to track and is not considered currently.
In Internet Explorer 7, 8 & 9, this works:
function getZoom() {
var screen;
screen = document.frames.screen;
return ((screen.deviceXDPI / screen.systemXDPI) * 100 + 0.9).toFixed();
}
The "+0.9" is added to prevent rounding errors (otherwise, you would get 104% and 109% when the browser zoom is set to 105% and 110% respectively).
In IE6 zoom doesn't exists, so it is unnecessary to check the zoom.
This has worked great for me in webkit-based browsers (Chrome, Safari):
function isZoomed() {
var width, mediaQuery;
width = document.body.clientWidth;
mediaQuery = '(max-width: ' + width + 'px) and (min-width: ' + width + 'px)';
return !window.matchMedia(mediaQuery).matches;
}
Doesn't seem to work in Firefox though.
This also works in WebKit:
var zoomLevel = document.width / document.body.clientWidth;
On Chrome
var ratio = (screen.availWidth / document.documentElement.clientWidth);
var zoomLevel = Number(ratio.toFixed(1).replace(".", "") + "0");
try this
alert(Math.round(window.devicePixelRatio * 100));
I fixed by the below code.
HTML:
<div class="mt-5"
[ngStyle]="getStyles()">
TS:
getStyles() {
const screenWidth = screen.width;
const windowWidth = window.innerWidth;
if (windowWidth != screenWidth) {
const percentDifference = Math.ceil((screenWidth / windowWidth) * 100);
if (percentDifference > 100) {
this.bannerBackgroundImageSize = '20%, 74%';
} else if (percentDifference === 100) {
this.bannerBackgroundImageSize = '20%, 72%';
} else if (percentDifference >= 90 && percentDifference <= 99) {
this.bannerBackgroundImageSize = '25%, 70%';
} else if (percentDifference >= 80 && percentDifference <= 89) {
this.bannerBackgroundImageSize = '28%, 68%';
} else if (percentDifference >= 75 && percentDifference <= 79) {
this.bannerBackgroundImageSize = '29%, 67%';
} else if (percentDifference >= 67 && percentDifference <= 74) {
this.bannerBackgroundImageSize = '30%, 65%';
} else if (percentDifference >= 50 && percentDifference <= 66) {
this.bannerBackgroundImageSize = '30%, 61%';
} else if (percentDifference < 50) {
this.bannerBackgroundImageSize = '30%, 58%';
}
} else {
this.bannerBackgroundImageSize = '20%, 72%';
}
const myStyles = {
'background-size': this.bannerBackgroundImageSize,
};
return myStyles;
}
I Hope this will work with all zoom levels and it can be considered with all styles.
Your calculations are still based on a number of CSS pixels. They're just a different size on the screen now. That's the point of full page zoom.
What would you want to happen on a browser on a 192dpi device which therefore normally displayed four device pixels for each pixel in an image? At 50% zoom this device now displays one image pixel in one device pixel.
Didn't test this for IE, but if you make an element elem with
min-width: 100%
then
window.document.width / elem.clientWidth
will give you your browser zoom level (including the document.body.style.zoom factor).
This is for Chrome, in the wake of user800583 answer ...
I spent a few hours on this problem and have not found a better approach, but :
There are 16 'zoomLevel' and not 10
When Chrome is fullscreen/maximized the ratio is window.outerWidth/window.innerWidth, and when it is not, the ratio seems to be (window.outerWidth-16)/window.innerWidth, however the 1st case can be approached by the 2nd one.
So I came to the following ...
But this approach has limitations : for example if you play the accordion with the application window (rapidly enlarge and reduce the width of the window) then you will get gaps between zoom levels although the zoom has not changed (may be outerWidth and innerWidth are not exactly updated in the same time).
var snap = function (r, snaps)
{
var i;
for (i=0; i < 16; i++) { if ( r < snaps[i] ) return i; }
};
var w, l, r;
w = window.outerWidth, l = window.innerWidth;
return snap((w - 16) / l,
[ 0.29, 0.42, 0.58, 0.71, 0.83, 0.95, 1.05, 1.18, 1.38, 1.63, 1.88, 2.25, 2.75, 3.5, 4.5, 100 ],
);
And if you want the factor :
var snap = function (r, snaps, ratios)
{
var i;
for (i=0; i < 16; i++) { if ( r < snaps[i] ) return eval(ratios[i]); }
};
var w, l, r;
w = window.outerWidth, l = window.innerWidth;
return snap((w - 16) / l,
[ 0.29, 0.42, 0.58, 0.71, 0.83, 0.95, 1.05, 1.18, 1.38, 1.63, 1.88, 2.25, 2.75, 3.5, 4.5, 100 ],
[ 0.25, '1/3', 0.5, '2/3', 0.75, 0.9, 1, 1.1, 1.25, 1.5, 1.75, 2, 2.5, 3, 4, 5 ]
);
I have this solution for mobile only (tested with Android):
jQuery(function($){
zoom_level = function(){
$("body").prepend('<div class="overlay" ' +
'style="position:fixed; top:0%; left:0%; ' +
'width:100%; height:100%; z-index:1;"></div>');
var ratio = $("body .overlay:eq(0)").outerWidth() / $(window).width();
$("body .overlay:eq(0)").remove();
return ratio;
}
alert(zoom_level());
});
If you want the zoom level right after the pinch move, you will probably have to set a little timeout because of the rendering delay (but I'm not sure because I didn't test it).
This answer is based on comments about devicePixelRatio coming back incorrectly on user1080381's answer.
I found that this command was coming back incorrectly in some instances too when working with a desktop, Surface Pro 3, and Surface Pro 4.
What I found is that it worked on my desktop, but the SP3 and SP4 were giving different numbers from each other and the desktop.
I noticed though that the SP3 was coming back as 1 and a half times the zoom level I was expecting. When I took a look at the display settings, the SP3 was actually set to 150% instead of the 100% I had on my desktop.
So, the solution to the comments should be to divide the returned zoom level by the scale of the machine you are currently on.
I was able to get the scale in the Windows settings by doing the following:
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_DesktopMonitor");
double deviceScale = Convert.ToDouble(searcher.Get().OfType<ManagementObject>().FirstOrDefault()["PixelsPerXLogicalInch"]);
int standardPixelPerInch = 96;
return deviceScale / standardPixelPerInch;
So in the case of my SP3, this is how this code looks at 100% zoom:
devicePixelRatio = 1.5
deviceScale = 144
deviceScale / standardPixelPerInch = 1.5
devicePixelRatio / (deviceScale / standardPixelPerInch) = 1
Multiplying by the 100 in user1080381's original answer then would give you a zoom of 100 (%).
On mobile devices (with Chrome for Android or Opera Mobile) you can detect zoom by window.visualViewport.scale.
https://developer.mozilla.org/en-US/docs/Web/API/Visual_Viewport_API
Detect on Safari: document.documentElement.clientWidth / window.innerWidth (return 1 if no zooming on device).
Have it currently working however still need to separate by browser.
Tested successfully on Chrome(75) and Safari(11.1) (haven't found way for FF as of yet).
It also gets the zoom value correct on retina display and calculations are triggered on resize event.
private pixelRatio() {
const styleString = "(min-resolution: 2dppx), (-webkit-min-device-pixel-ratio: 1.5),(-moz-min-device-pixel-ratio: 1.5),(min-device-pixel-ratio: 1.5)";
const chromeRatio = (Math.round((this.window.outerWidth / this.window.innerWidth)*100) / 100);
const otherRatio = (Math.round(window.devicePixelRatio * 100) / 100);
const resizeValue = (this.isChrome()) ? chromeRatio : otherRatio;
return resizeValue || (this.window.matchMedia && this.window.matchMedia(styleString).matches ? 2 : 1) || 1;
}
private isChrome():boolean {
return (!!this.window.chrome && !(!!this.window.opera || this.window.navigator.userAgent.indexOf(' Opera') >= 0))
}
private chrome() {
const zoomChrome = Math.round(((this.window.outerWidth) / this.window.innerWidth)*100) / 100;
return {
zoom: zoomChrome,
devicePxPerCssPx: zoomChrome1 * this.pixelRatio()
};
}
This detects scale.
html code:
<body id="blog">
js:
function scale(){
return Math.round(100/(d.getElementById('blog').offsetWidth/d.getElementById('blog').getBoundingClientRect().width))/100;
}
Zoom factor and scale are not to be confused with each other. Users control "zoom";
"scale" is a CSS transformation. 11 years and 10 months ago, however, understack noted:
Basically I want to know the dimension of a DIV at 100% zoom.
This does that. I use it in conjunction with #media queries to detect in JavaScript device scaling, i.e. 360x720 might apply 0.5 scaling; 720x360, 0.75.
var d=document;
I used a different approach, i created a dom element with a fixed height and width of 100px, position fixed and opacity of 0, basically a hidden element.
I then used dom-to-image to capture this element as an image which might sound like overkill but i wanted a bullet proof solution and we were already using this package anyways, then validated the output image width, if it came back with 110, the zoom is at 110%, it's very accurate.
const ORIGINAL_ZOOM_SIZE = 100;
async function isValidateZoomLevel() {
const base64Render = await domToImage({
ref: hiddenElementReference,
});
const pixleRatio = Math.round(window.devicePixelRatio);
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(ORIGINAL_ZOOM_SIZE === img.width / pixleRatio && ORIGINAL_ZOOM_SIZE === (img.height / pixleRatio));
img.onerror = reject;
img.src = base64Render;
});
}
This may or may not help anyone, but I had a page I could not get to center correctly no matter what Css tricks I tried so I wrote a JQuery file call Center Page:
The problem occurred with zoom level of the browser, the page would shift based upon if you were 100%, 125%, 150%, etc.
The code below is in a JQuery file called centerpage.js.
From my page I had to link to JQuery and this file to get it work, even though my master page already had a link to JQuery.
<title>Home Page.</title>
<script src="Scripts/jquery-1.7.1.min.js"></script>
<script src="Scripts/centerpage.js"></script>
centerpage.js:
// centering page element
function centerPage() {
// get body element
var body = document.body;
// if the body element exists
if (body != null) {
// get the clientWidth
var clientWidth = body.clientWidth;
// request data for centering
var windowWidth = document.documentElement.clientWidth;
var left = (windowWidth - bodyWidth) / 2;
// this is a hack, but it works for me a better method is to determine the
// scale but for now it works for my needs
if (left > 84) {
// the zoom level is most likely around 150 or higher
$('#MainBody').removeClass('body').addClass('body150');
} else if (left < 100) {
// the zoom level is most likely around 110 - 140
$('#MainBody').removeClass('body').addClass('body125');
}
}
}
// CONTROLLING EVENTS IN jQuery
$(document).ready(function() {
// center the page
centerPage();
});
Also if you want to center a panel:
// centering panel
function centerPanel($panelControl) {
// if the panel control exists
if ($panelControl && $panelControl.length) {
// request data for centering
var windowWidth = document.documentElement.clientWidth;
var windowHeight = document.documentElement.clientHeight;
var panelHeight = $panelControl.height();
var panelWidth = $panelControl.width();
// centering
$panelControl.css({
'position': 'absolute',
'top': (windowHeight - panelHeight) / 2,
'left': (windowWidth - panelWidth) / 2
});
// only need force for IE6
$('#backgroundPanel').css('height', windowHeight);
}
}
This is question was posted like ages back, but today when i was looking for the same answer "How to detect zoom in and out event", i couldn't find one answer that would fit all the browsers.
As on now : For Firefox/Chrome/IE8 and IE9 , the zoom in and out fires a window.resize event.
This can be captured using:
$(window).resize(function() {
//YOUR CODE.
});
A workaround for FireFox 16+ to find DPPX (zoom level) purely with JavaScript:
var dppx = (function (precision) {
var searchDPPX = function(level, min, divisor) {
var wmq = window.matchMedia;
while (level >= min && !wmq("(min-resolution: " + (level/divisor) + "dppx)").matches) {
level--;
}
return level;
};
var maxDPPX = 5.0; // Firefox 22 has 3.0 as maximum, but testing a bit greater values does not cost much
var minDPPX = 0.1; // Firefox 22 has 0.3 as minimum, but testing a bit smaller values does not cost anything
var divisor = 1;
var result;
for (var i = 0; i < precision; i++) {
result = 10 * searchDPPX (maxDPPX, minDPPX, divisor);
maxDPPX = result + 9;
minDPPX = result;
divisor *= 10;
}
return result / divisor;
}) (5);
The problem lies in the types of monitor used, a 4k monitor vs standard monitor. Chrome is by far the smartest at being able to tell us what the zoom level is just by using window.devicePixelRatio, apparently it can tell what the pixel density is and reports back the same number to matter what.
Other browsers, not so much. IE and Edge are probably the worst, as they handle zoom levels much differently. To get the same size text on a 4k monitor, you have to select 200%, instead of 100% on a standard monitor.
As of May 2018, here's what I have to detect zoom levels for a few of the most popular browsers, Chrome, Firefox, and IE11. I have it tell me what the zoom percentage is. For IE, it reports 100% even for 4k monitors that are actually at 200%, but the text size is really the same.
Here's a fiddle: https://jsfiddle.net/ae1hdogr/
If anyone would care to take a stab at other browsers and update the fiddle, then please do so. My main goal was to get these 3 browsers covered to detect if people were using a zoom factor greater than 100% to use my web application and display a notice suggesting a lessor zoom factor.
here it does not change!:
<html>
<head>
<title></title>
</head>
<body>
<div id="xy" style="width:400px;">
foobar
</div>
<div>
<button onclick="alert(document.getElementById('xy').style.width);">Show</button>
</div>
</body>
</html>
create a simple html file, click on the button. regardless of what zoom level: it will show you the width of 400px (at least with firefox and ie8)
function supportFullCss3()
{
var div = document.createElement("div");
div.style.display = 'flex';
var s1 = div.style.display == 'flex';
var s2 = 'perspective' in div.style;
return (s1 && s2);
};
function getZoomLevel()
{
var screenPixelRatio = 0, zoomLevel = 0;
if(window.devicePixelRatio && supportFullCss3())
screenPixelRatio = window.devicePixelRatio;
else if(window.screenX == '0')
screenPixelRatio = (window.outerWidth - 8) / window.innerWidth;
else
{
var scr = window.frames.screen;
screenPixelRatio = scr.deviceXDPI / scr.systemXDPI;
}
//---------------------------------------
if (screenPixelRatio <= .11){ //screenPixelRatio >= .01 &&
zoomLevel = "-7";
} else if (screenPixelRatio <= .25) {
zoomLevel = "-6";
}else if (screenPixelRatio <= .33) {
zoomLevel = "-5.5";
} else if (screenPixelRatio <= .40) {
zoomLevel = "-5";
} else if (screenPixelRatio <= .50) {
zoomLevel = "-4";
} else if (screenPixelRatio <= .67) {
zoomLevel = "-3";
} else if (screenPixelRatio <= .75) {
zoomLevel = "-2";
} else if (screenPixelRatio <= .85) {
zoomLevel = "-1.5";
} else if (screenPixelRatio <= .98) {
zoomLevel = "-1";
} else if (screenPixelRatio <= 1.03) {
zoomLevel = "0";
} else if (screenPixelRatio <= 1.12) {
zoomLevel = "1";
} else if (screenPixelRatio <= 1.2) {
zoomLevel = "1.5";
} else if (screenPixelRatio <= 1.3) {
zoomLevel = "2";
} else if (screenPixelRatio <= 1.4) {
zoomLevel = "2.5";
} else if (screenPixelRatio <= 1.5) {
zoomLevel = "3";
} else if (screenPixelRatio <= 1.6) {
zoomLevel = "3.3";
} else if (screenPixelRatio <= 1.7) {
zoomLevel = "3.7";
} else if (screenPixelRatio <= 1.8) {
zoomLevel = "4";
} else if (screenPixelRatio <= 1.9) {
zoomLevel = "4.5";
} else if (screenPixelRatio <= 2) {
zoomLevel = "5";
} else if (screenPixelRatio <= 2.1) {
zoomLevel = "5.2";
} else if (screenPixelRatio <= 2.2) {
zoomLevel = "5.4";
} else if (screenPixelRatio <= 2.3) {
zoomLevel = "5.6";
} else if (screenPixelRatio <= 2.4) {
zoomLevel = "5.8";
} else if (screenPixelRatio <= 2.5) {
zoomLevel = "6";
} else if (screenPixelRatio <= 2.6) {
zoomLevel = "6.2";
} else if (screenPixelRatio <= 2.7) {
zoomLevel = "6.4";
} else if (screenPixelRatio <= 2.8) {
zoomLevel = "6.6";
} else if (screenPixelRatio <= 2.9) {
zoomLevel = "6.8";
} else if (screenPixelRatio <= 3) {
zoomLevel = "7";
} else if (screenPixelRatio <= 3.1) {
zoomLevel = "7.1";
} else if (screenPixelRatio <= 3.2) {
zoomLevel = "7.2";
} else if (screenPixelRatio <= 3.3) {
zoomLevel = "7.3";
} else if (screenPixelRatio <= 3.4) {
zoomLevel = "7.4";
} else if (screenPixelRatio <= 3.5) {
zoomLevel = "7.5";
} else if (screenPixelRatio <= 3.6) {
zoomLevel = "7.6";
} else if (screenPixelRatio <= 3.7) {
zoomLevel = "7.7";
} else if (screenPixelRatio <= 3.8) {
zoomLevel = "7.8";
} else if (screenPixelRatio <= 3.9) {
zoomLevel = "7.9";
} else if (screenPixelRatio <= 4) {
zoomLevel = "8";
} else if (screenPixelRatio <= 4.1) {
zoomLevel = "8.1";
} else if (screenPixelRatio <= 4.2) {
zoomLevel = "8.2";
} else if (screenPixelRatio <= 4.3) {
zoomLevel = "8.3";
} else if (screenPixelRatio <= 4.4) {
zoomLevel = "8.4";
} else if (screenPixelRatio <= 4.5) {
zoomLevel = "8.5";
} else if (screenPixelRatio <= 4.6) {
zoomLevel = "8.6";
} else if (screenPixelRatio <= 4.7) {
zoomLevel = "8.7";
} else if (screenPixelRatio <= 4.8) {
zoomLevel = "8.8";
} else if (screenPixelRatio <= 4.9) {
zoomLevel = "8.9";
} else if (screenPixelRatio <= 5) {
zoomLevel = "9";
}else {
zoomLevel = "unknown";
}
return zoomLevel;
};

Pass scroll value as percentage as argument, calculate distance between two points?

My code:
var vert = $(this).scrollTop();
function scrollBetween(startPoint, endPoint, name){
var progressNumber;
progressNumber = (1 / (endPoint - startPoint)) * (vert - startPoint);
if (progressNumber >= 0 && progressNumber <= 1) {
name.progress(progressNumber);
}
else if(progressNumber < 0) {
name.progress(0);
}
else if(progressNumber > 1) {
name.progress(1);
}
}
An example of it being called:
scrollBetween(300, 700, anim1);
So as it is currently written, I have two points in pixels being passed to my function. What I can't figure out is how to convert these points to percentages of the total viewport instead. So I want to achieve this:
scrollBetween(2%, 15%, anim1);
But I keep getting errors with the various approaches I have tried. I know I can't pass it like ^ above but I haven't been able to figure out how to get the % instead of pixels.
I want to use % instead of px as I want the page to remain responsive.
Thanks.
#krisph helped me with this, I did the following to get responsive percentage values:
var vert = $(this).scrollTop();
var docHeight = $(document).height();
function scrollBetween(startPoint, endPoint, name){
var progressNumber;
startPoint = (docHeight/100) * startPoint;
endPoint = (docHeight/100) * endPoint;
progressNumber = (1 / (endPoint - startPoint)) * (vert - startPoint);
if (progressNumber >= 0 && progressNumber <= 1) {
name.progress(progressNumber);
}
else if(progressNumber < 0) {
name.progress(0);
}
else if(progressNumber > 1) {
name.progress(1);
}
}
scrollBetween(2, 15, anim1);

(JS/Canvas) Collision/Movement for sidescroller

http://jsfiddle.net/xknbn/
http://jsfiddle.net/xknbn/embedded/result/
Hey guys. I'm having issues getting my collision system to function without problems. At the moment, if I'm going too fast, I get embedded in the object and my collision resolution fails. I'm fairly certain that the problem happens because of how I have velocity set up, but I don't know how I can go about fixing the issue. Any help would be greatly appreciated.
friction = 0.9,
gravity = 0.3;
function keycheck() {
if (keys[32]) {
if (!player.jumping) {
player.jumping = true;
player.vely = -player.speed;
}
}
if (keys[68]) {
if (player.velx < player.speed) {
player.velx++;
}
}
if (keys[65]) {
if (player.velx > -player.speed) {
player.velx--;
}
}
}
ctx.startx = player.x;
ctx.starty = player.y;
function playerupdate() {
player.Intersects = function(object) {
return player.x < object.x + object.width + 1 &&
player.x + player.width + 1 > object.x &&
player.y < object.y + object.height + 1 &&
player.y + player.height + 1 > object.y;
}
solidcount = 0;
for (i = 0; i < tgen.length; i++) {
for (ii = 0; ii < tgen[i].length; ii++) {
if ( player.Intersects(tgen[i][ii]) && tgen[i][ii].solid ) {
var pright = player.x + player.width,
pleft = player.x,
ptop = player.y,
pbottom = player.y + player.height,
oright = tgen[i][ii].x + tgen[i][ii].width,
oleft = tgen[i][ii].x,
otop = tgen[i][ii].y,
obottom = tgen[i][ii].y + tgen[i][ii].height;
//if player is to the right of his previous position, and his right side has collided with objects left
if (player.x >= ctx.startx && pright > oleft - 1) {
player.x--;
}
//if player is to the left of his previous position, and his left side has collided with objects right
if (player.x <= ctx.startx && pleft < oright + 1) {
player.x++;
}
//if player is above his previous position, and his top side has collided with objects bottom
if (player.y <= ctx.starty && ptop < obottom + 1) {
player.y++;
}
//if player is below his previous position, and his bottom side has collided with objects top
if (player.y >= ctx.starty && pbottom > otop - 1) {
player.y--;
player.vely = 0;
player.jumping = false;
}
solidcount++;
}
}
if ( i == tgen.length - 1 && solidcount == 0 ) {
player.jumping = true;
}
}
ctx.diffx = player.x - ctx.startx;
ctx.diffy = player.y - ctx.starty;
if (player.x <= bgimg.width - width/2 && player.x >= width/2) {
ctx.translate(-ctx.diffx,0);
}
if (player.y <= bgimg.height - 360 - player.height) {
ctx.translate(0,-ctx.diffy);
}
ctx.startx = player.x;
ctx.starty = player.y;
ctx.clearRect(player.x-width/2,player.y-height/2,width,height);
ctx.fillStyle = 'red';
ctx.fillRect(player.x,player.y,player.width,player.height);
keycheck();
player.velx *= friction;
if (player.jumping) {player.vely += gravity;}
player.x += player.velx;
player.x = Math.round(player.x);
player.y += player.vely;
player.y = Math.round(player.y);
if (player.y >= bgimg.height - player.height - 1) {
player.y = bgimg.height - player.height - 1;
player.vely = 0;
player.jumping = false;
}
if (player.x > bgimg.width - player.width) {
player.x = bgimg.width - player.width;
player.velx = 0;
};
if (player.x < 0) {
player.x = 0;
player.velx = 0;
}
if (!keys[65] && !keys[68] && (player.velx < 0.2 && player.velx > -0.2)) {player.velx = 0};
console.log(player.x,player.y)
requestAnimationFrame(playerupdate);
}
So this is a common platform engine problem. If your player was moving too fast, it would go straight though the obstacles, because no collision testing occurs between the current position and the next position. Your aim is to test collisions as few times as possible but so that all possible collisions between the start position and the target are tested. Also when you detect a collision in the path, you have to handle it properly.
Moving objects safely
To move any objects safely you need to move them in steps, and test for collisions at each step. The simplest choice is to move the player by 1 pixel at a time, and then test for a collision. This does mean doing a lot of collision tests though.
To improve this, if you are only doing box collision testing, you can move the player by a maximum of the width of the player each time.
Dealing with collisions
So in your example, you move the player back 1 pixel depending on the direction of the player. If the player were to sink 5 pixels into the obstacle, then your collision handling mechanism only moves the player back to 4 pixels into the obstacle. It appears as if the player slowly moves through the obstacle.
If you move the player 1 pixel at a time, the approach you have taken works. If you move the player more than 1 pixel at a time then you have to move the player back until it no longer overlaps an obstacle. Something like while (overlapping) -> move back one pixel

Change scroll direction at Anchor point

on my site I have 6 divs each div the same height and width of the browser window, so 5 are out of view, 3 left aligned then 3 more on top, as so....
When my visitors scroll, id like to scroll along to the 3rd div, then scroll up to the 4th, then continue scrolling horizontally. Is this possible with jQuery, and if so how can it be done?
Check this out. It allows you to scroll in different directions and set up "slides" like above
http://joelb.me/scrollpath/
Simple (maybe helpful) and not perfect:
var win = $(window),
doc = $(document),
bodyH = doc.height() - win.height(),
page4 = $('#page4');
win.scrollTop(bodyH);
$(document).on('DOMMouseScroll mousewheel', function(e, delta) {
delta = delta || -e.originalEvent.detail / 3 || e.originalEvent.wheelDelta / 120;
if(delta < 0) {
if(win.scrollLeft() > page4.offset().left && win.scrollTop() !== 0) {
win.scrollTop(win.scrollTop() + delta * 30);
} else {
win.scrollLeft(win.scrollLeft() - delta * 30);
}
} else {
if(win.scrollLeft() < page4.offset().left && (win.scrollTop() > 0 || win.scrollTop() === 0) && win.scrollTop() !== bodyH) {
win.scrollTop(win.scrollTop() + delta * 30);
} else {
win.scrollLeft(win.scrollLeft() - delta * 30);
}
}
e.preventDefault();
});
demo

Making an image jump up (and fall back)

I am making a platform game in JavaScript and I need some help.
How do I make an image jump up 50px in JavaScript when the spacebar pushed then fall back to a certain position?
If you mean jumping as in jumping with gravity - you'd need a parabola formula.
E.g.: http://jsfiddle.net/pimvdb/7JFU3/.
var x = 0;
var interval = setInterval(function() {
x++;
image.style.top = 50 - (-0.1 * x * (x - 50)) + 'px';
if(x >= 50) clearInterval(interval);
}, 20);
I'm working on exact the same thing. Here's my github project link https://github.com/beothorn/webcomicsgame
Look at MainCharacter.js at
if(gameCommandState.up){
if(this.canJump(game))
this.element.yAccelerate(-this.yAcceleration);
}
You can fork it if you want, but I'm doing it using canvas.
We had fun extending this to have an image keep bouncing up and down
var x = 0;
var goingUp = true;
var interval = setInterval(function() {
if(goingUp==true) {
x++;
if(x >= 50) {
goingUp=false;
//alert("go down")
}
} else {
x--;
if(x <= -50) {
goingUp=true;
//alert("go up")
}
}
$('#demo').css('top', 50 - (-0.1 * x * (x - 50)));
}, 20);

Categories