How can I implement a FPS view with WebGL inside a browser? - javascript

I'm using Copperlicht, and I want to create a usable FPS. The demo controls shows why the browser environment makes this a pain.
In order to implement FPS camera control, you need to track the relative mouse position - in other words, its motion, not its absolute screen coordinates. The mouse can leave the browser at any time (rightfully so) and can't be tracked, unless the user initiates a drag event inside the page. Click events change focus and prevent the application from using mouse data as input.
The mouse position can be tracked during drag, but this requires the user to hold down their left mouse button. This isn't good since left clicking is usually used for other things. Holding the button down is also tiring and cumbersome.
The only thing I can think of is automating the middle mouse button. A middle mouse button press keeps focus in the browser, and keeps left/right click events outside the browser window in the browser's focus. Is it possible to cause middle mouse button to stay pressed using JavaScript?
If not, is there a "pure" solution to this ? I'd rather not go to flash or Java or a plugin as an answer.

This thread is a nice reading on this topic. It seems like prototypes for this functionality are at least suggested for Firefox and Chrome.

How about making the window fullscreen and then pausing the game if the cursor moves out of the window? I know this doesn't really solve the problem, but it's the best I can think of, without using a plugin of some sort.

It's kind of cheating, but going to about:flags in Chrome and enabling "FPS counter" works for me, :) (but it's not doing it for all browsers nor inside your WebGL app).

I found this example code at
http://bitdaddys.com/javascript/example3run.html
<html>
<head>
<title>JavaScript Example of Mouse Position Tracking</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body>
<form name=thisform>
<table border=0>
<tr><td colspan=2>Position Of Cursor</td></tr>
<tr><td>X <input type=text name=x value=""></td>
<td>Y <input type=text name=y value=""></td>
</tr>
</table>
</form>
<script type=text/javascript>
var isIE = document.all?true:false;
if (!isIE) document.captureEvents(Event.MOUSEMOVE);
document.onmousemove = getMousePosition;
function getMousePosition(mp) {
var _x;
var _y;
if (!isIE) {
_x = mp.pageX;
_y = mp.pageY;
}
if (isIE) {
_x = event.clientX + document.body.scrollLeft;
_y = event.clientY + document.body.scrollTop;
}
document.thisform.x.value=_x;
document.thisform.y.value=_y;
return true;
}
</script>
</body>
</html>

We need the window to be able to capture the mouse, as it is seen with some browser plugins, maybe in Java. Flash doesn't have this ability, AFAIK.
As a sidenote, when captured to "get the mouse back" you have to press ESC, and this can be annoying when the app doesn't inform the user properly.

(this is the only solution i've seen so far could work for my game project, doing an FPS too)
Implement a Plugin for each browser you intend to support. AFAIK, this is the way they solved the problem with "Quake Live".
Chrome / Chromium -> PPAPI
Firefox & Opera -> NPAPI
IE -> ActiveX
Safari -> Safari plugin development
Btw, the link Daniel Baulig gave you has a nice pointer and solves this problem (on the long run).

At this point in time (Oct 2011) the only way to get real First Person Shooter-style controls is via a browser plugin. Depending on your needs you might also be able to get away with a simple click-and-drag scheme like I'm currently using in my Quake 3 demo, but if you are building an actual game and not a pretty tech demo this is probably not sufficient.
(Note: That's actually what Google did for their GWT port of Quake 2. You have to use the CTRL key to fire, since clicking is used to move your view.)
In the near future, however, we should be receiving a "MouseLock" API that is basically custom-built for this purpose. You can read up on it's progress at Seth Ladd's Blog. Once that comes out we'll have a lot more options for game controls available to us. (Would also help with things like RTS games)

Right here right now :
I make one with push/pop matrix with glmatrix 0.9 also version 2.0 webgl & glmatrix .
Secret - Must translate to zero , rotate and then translate to map position. You have all parameters for first person controler.
See:opengles 1.1. / webgl 1.0 / glmatrix 0.9
or see this example with full colizion.
WebGl 2 / glmatrix 2 Example's (also First Person controller):
Download from bitBucket
Live example
Out of context :
////////////////////////////////////////////////////////
// Somewhere in draw function ....
////////////////////////////////////////////////////////
mat4.identity(object.mvMatrix);
this.mvPushMatrix(object.mvMatrix,this.mvMatrixStack);
////////////////////////////////////////////////////////
if (App.camera.FirstPersonController==true){camera.setCamera(object)}
////////////////////////////////////////////////////////
mat4.translate(object.mvMatrix, object.mvMatrix, object.position.worldLocation );
mat4.rotate(object.mvMatrix, object.mvMatrix, degToRad(object.rotValue), object.rotDirection.RotationVector );
....
End of Draw function
Content of SetCamera :
var camera = new Object();
/* Set defaults */
camera.pitch = 0;
camera.pitchRate = 0;
camera.yaw = 0;
camera.yawRate = 0;
camera.xPos = 0;
camera.yPos = 0;
camera.zPos = 0;
camera.speed = 0;
camera.yawAmp = 0.05;
camera.pitchAmp = 0.007;
keyboardPress = defineKeyBoardObject();
camera.setCamera = function(object) {
/* Left Key or A */
if (keyboardPress.getKeyStatus(37) || keyboardPress.getKeyStatus(65) || App.camera.leftEdge == true) {
camera.yawRate = 20;
if (App.camera.leftEdge == true) camera.yawRate = 10;
}
/* Right Key or D */
else if (keyboardPress.getKeyStatus(39) || keyboardPress.getKeyStatus(68) || App.camera.rightEdge == true) {
camera.yawRate = -20;
if (App.camera.rightEdge == true) camera.yawRate = -10;
}
else {
// camera.yawRate = 0;
}
/* Up Key or W */
if (keyboardPress.getKeyStatus(38) || keyboardPress.getKeyStatus(87)) {
camera.speed = 0.03;
}
/* Down Key or S */
else if (keyboardPress.getKeyStatus(40) || keyboardPress.getKeyStatus(83)) {
camera.speed = -0.03;
}
else {
camera.speed = 0;
}
/* Page Up
if (keyboardPress.getKeyStatus(33)) {
camera.pitchRate = 100;
}
/* Page Down
else if (keyboardPress.getKeyStatus(34)) {
camera.pitchRate = -100;
}
else {
camera.pitchRate = 0;
}
*/
/* Calculate yaw, pitch and roll(x,y,z) */
if (camera.speed != 0) {
camera.xPos -= Math.sin(degToRad(camera.yaw)) * camera.speed;
camera.yPos = 0;
camera.zPos -= Math.cos(degToRad(camera.yaw)) * camera.speed;
}
camera.yaw += camera.yawRate * camera.yawAmp ;
camera.pitch += camera.pitchRate * camera.pitchAmp ;
mat4.rotate(object.mvMatrix, object.mvMatrix, degToRad(-camera.pitch), [1, 0, 0]);
mat4.rotate(object.mvMatrix, object.mvMatrix, degToRad(-camera.yaw), [0, 1, 0]);
// mat4.translate(object.mvMatrix, object.mvMatrix, [camera.yaw, -camera.pitch, 0]);
mat4.translate(object.mvMatrix, object.mvMatrix, [-camera.xPos , -camera.yPos , -camera.zPos ]);
camera.yawRate = 0;
camera.pitchRate = 0;
};
This code allows you to draw 3D objects and folders easily
and quickly.Under the principle of one object one line.
webgl 3d wourld engine framework zlatnaspirala
First person web site look.
Used lib :
High performance matrix and vector operations for WebGL

Related

Paper.js scale transform continues outside the onFrame event

I'm running into a bit of an odd issue with Paper.js - I'm using the library to scale the "petals" of a randomly generated flower while audio plays.
The issue crops up if the flower is "growing" and the user navigates to a different tab in the browser. Even though it appears that the onFrame event is not firing when the window is out of view, whichever petal is currently scaling at the time will continue to scale indefinitely.
I even tried using a special js library to determine if the window is in view and still wasn't able to get the petals to stop scaling.
You can view a demo here, as I was not even able to replicate this in a Paper sketch: https://demos2.paperbeatsscissors.com/
Also including my onFrame code here in case the problem is obvious to someone:
view.onFrame = function(event) {
// See if something is playing
if (playing > -1) {
// Get the active flower
var activeFlower = garden[playing],
activeData = activeFlower.data;
// Active layer and petal
var activeLayer = getEl(activeFlower, activeData.lIndex),
activePetal = getEl(activeLayer, activeData.pIndex);
// Variables
var time = activeData.audio.seek(),
scaleAmount = (1 / (activeData.timing / event.delta.toFixed(3))) * 2;
// Petal progression
if (!activeData.completed) {
if (activePetal.scaling.x < 1 && activePetal.scaling.y < 1) {
activePetal.pivot = {x: 0, y: activePetal.height / 2};
activePetal.scaling.x = activePetal.scaling.x + scaleAmount;
activePetal.scaling.y = activePetal.scaling.y + scaleAmount;
} else {
if (activeData.pIndex < (activeLayer.children.length - 1)) {
// If the petal is scaled, jump to a new petal
activeData.pIndex += 1;
} else {
if (activeData.lIndex > 0) {
// When all petals are bloomed, jump to a new layer
activeData.pIndex = 0;
activeData.lIndex -= 1;
} else {
// Set the flower as completed
activeData.completed = true;
}
}
}
}
activeFlower.rotate(.125, activeData.center);
// Reset the playing variable if the audio clip is complete and the flower has completed
if (!activeData.audio.playing() && time === 0 && activeData.completed) {
playing = -1;
}
}
}
Really stumped on this one so any help is greatly appreciated!
I think that your problem is coming from the fact that you base your scaling calculation on event.delta which represents the time elapsed since the last event fired.
The thing is that, if I'm not mistaken, under the hood, Paper.js onFrame event relies on requestAnimationFrame which does not fire when the tab if inactive.
So when you switch tab, wait for a while and get back to your tab event.delta value is big and your scaling value too, hence the size of your petals. This basic sketch showcase this behavior.
So in my opinion, you should simply check event.delta value and limit it if it's too high.

EaselJS SpriteSheet isn't responding to gotoAndPlay

I'm developing a Fez-based HTML5 Canvas game using EaselJS and I've found a problem, that I can't solve myself, while trying to implement SpriteSheets to the hero.
The problem is that I've defined three animations to my hero ("stand", "jump" and "run") and when I try to change from one animation to another using hero.gotoAndPlay("run") or hero.gotoAndStop("stand") the animations don't change, change but show the first frame only or change to the wrong animation.
Can someone help me? What I'm doing wrong and how can I fix it? Thanks.
Here's the relevant JavaScript code I'm using to create the hero Sprite:
var data = {
images: ["http://i.imgur.com/M0GmFnz.png"],
frames: {width:34, height:46},
animations: {
stand:0,
run:[0,12,true,0.25],
jump:13
}
};
var spriteSheet = new createjs.SpriteSheet(data);
var hero = new createjs.Sprite(spriteSheet, "stand");
hero.offset = 4 + 1; // "+1" is important, change the first number only.
hero.regX = hero.offset + 2;
hero.regY = hero.offset;
hero.width = 34 - (hero.offset*2) - 12;
hero.height = 46 - (hero.offset*2);
hero.x = 0;
hero.y = 0;
hero.jumping = true;
stage.addChild(hero);
And the code I'm using to change the animation:
if(!left && !right){ // User isn't pressing left arrow or right arrow
hero.gotoAndStop("stand");
}else{
hero.gotoAndPlay("run");
}
JSFiddle
Official Site
If you are calling gotoAndStop or gotoAndPlay in a tick (or similar) then it will constantly reset to the first frame. You have to ensure you only call these functions one time when the animation changes.
A good way to set this up is to store the current animation, store it, and only change it up if the animation changes. Something like:
var animation;
if(!left && !right){ // User isn't pressing left arrow or right arrow
animation = "stand";
}else{
animation = "run";
}
if (currentAnimation != animation) {
hero.gotoAndStop(animation);
}
This is just an example, but should give you an idea. You should only call these methods one time to kick off an animation.

html5/javascript game running slow in ie8

We have made a game in html/javascript. There's unfortunately a lot of secrecy around the project, so I can't disclose much code.
However the essence is that it runs extremely well on all browsers, except ie. through using the <meta http-equiv="X-UA-Compatible" content="IE=9"> tag; now the only problem is ie8, which the game NEEDS to be compatible with. I know it has little html5 integration, but the game actually works, except it's horribly slow.
Keep in mind, ie9 and ie10 were very slow before using the tag as well. The game is in n sense advanced or anything. So I was wondering if anyone knew what could cause issues like these?
I'll try to put some obfuscated code here, keep in mind though that it's not the entire code, just the main loop:
// Global constants:
var PLAYGROUND_WIDTH = 1000;
var PLAYGROUND_HEIGHT = 1000;
var REFRESH_RATE = 30;
//Constants for the gameplay
var smallStarSpeed = 1; //pixels per frame
var mediumStarSpeed = 3; //pixels per frame
var bigStarSpeed = 5; //pixels per frame
var percent = 1;
// --------------------------------------------------------------------
// -- the main declaration: --
// --------------------------------------------------------------------
$(function(){
//Calculate playground width and height:
PLAYGROUND_WIDTH = $(window).width() - 20;
PLAYGROUND_HEIGHT = $(window).height() - 20;
//Calculate Layour for responsive Design.
//Calculate Area:
// Animations declaration:
// The background:
var DM = new DeckManager;
var IM = new ImageManager;
IM.Create("Image1");
//DEBUG: Loading images for demo, this should be done using the image manager in the actual game.
var background1 = new $.gameQuery.Animation({
imageURL: "http://gamequeryjs.com/demos/3/background1.png"});
var background2 = new $.gameQuery.Animation({
imageURL: "http://gamequeryjs.com/demos/3/background2.png"});
var background3 = new $.gameQuery.Animation({
imageURL: "http://gamequeryjs.com/demos/3/background3.png"});
var background4 = new $.gameQuery.Animation({
imageURL: "http://gamequeryjs.com/demos/3/background4.png"});
var background5 = new $.gameQuery.Animation({
imageURL: "http://gamequeryjs.com/demos/3/background5.png"});
var background6 = new $.gameQuery.Animation({
imageURL: "http://gamequeryjs.com/demos/3/background6.png"});
var Face = new Array();
IM.Load("image2");
IM.Load("image3");
IM.Load("image4");
IM.Load("image5");
var resizeTimer;
//Event to handle resizing
//This event should fire under any circimstance, except when safari is NOT maximized, and windows resolution is changed (WTF?)
$(window).resize(function ()
{
clearTimeout(resizeTimer);
resizeTimer = setTimeout(Resized, 100);
});
//Actual Resizing Event
function Resized()
{
//Your function goes here
//Calculate playground width and height:
PLAYGROUND_WIDTH = $(window).width() - 20;
PLAYGROUND_HEIGHT = $(window).height() - 20;
//Calculate Layout for responsive Design.
};
// Initialize the game:
$("#playground").playground({
height: PLAYGROUND_HEIGHT,
width: PLAYGROUND_WIDTH,
keyTracker: true,
mouseTracker: true});
// Initialize the background
$.playground()
.addGroup("background", {width: PLAYGROUND_WIDTH,
height: PLAYGROUND_HEIGHT})
.addSprite("background1", {animation: background1,
width: PLAYGROUND_WIDTH,
height: PLAYGROUND_HEIGHT})
.addSprite("background2", {animation: background2,
width: PLAYGROUND_WIDTH,
height: PLAYGROUND_HEIGHT,
posx: PLAYGROUND_WIDTH})
.addSprite("background3", {animation: background3,
width: PLAYGROUND_WIDTH,
height: PLAYGROUND_HEIGHT})
.addSprite("background4", {animation: background4,
width: PLAYGROUND_WIDTH,
height: PLAYGROUND_HEIGHT,
posx: PLAYGROUND_WIDTH})
.addSprite("background5", {animation: background5,
width: PLAYGROUND_WIDTH,
height: PLAYGROUND_HEIGHT})
.addSprite("background6", {animation: background6,
width: PLAYGROUND_WIDTH,
height: PLAYGROUND_HEIGHT,
posx: PLAYGROUND_WIDTH})
$("#background").addSound(bgmusic);
//Setup obects so they can be reached randomly
var Vals = new Array();
var Vals2 = new Array();
var Turned = 0;
var TurnedMax = 2;
//This will ensure that two cards of each are added to the deck
//This function will be handled by the imagemanager at later stages.
for (var i = 0; i < NumberOfCards; ++i)
{
Vals[i] = Math.floor(i/2);
}
Vals2[0]=3;
DM.Create(Vals, Vals2, NumberOfCards, 1);
//Generate the actual object
$.playground()
.addGroup("object", {width: PLAYGROUND_WIDTH,
height: PLAYGROUND_HEIGHT})
for (var i = 0; i < NumberOfCards+1; ++i)
{
//Generate unique ID for the card
var name = "object"+i;
val = DM.Pushobject();
//Add the actual card to the playground, we spawn them in a responsive awy based on the resolution of the game.
$("#object").addSprite(name, {animation: IM.GetBack(), width: 208, height: 303, posx: (i%(Math.ceil(noc*Ratio))) *SpaceX + SpaceX - 104 + LastYOff * ( i>= (NumberOfCards + NumberOfCardsBonus) - ((NumberOfCards + NumberOfCardsBonus)%(Math.ceil(noc*Ratio))) ) , posy: Math.floor( i / (Math.ceil(noc*Ratio)) ) * SpaceY + SpaceY - 152 });//
//Add a class to the object, this, does nothing except make us able to search for objects with the same class later in the code.
$("#"+name).addClass("object");
//Create the actual class for the object, this will add logic to the object.
$("#"+name)[0].Cards = new Cards($("#"+name));
$("#"+name)[0].Cards.Create(val, IM.GetImage(val), IM.GetImage(3), IM.GetBack(), DM.LastBonus(), Scale);
//Add a mousedown event for the card, this mousedown will be run in the main
//environment rather than the class environment to make sure that we have access
//to all the data we need access to.
$("#"+name).mousedown(function(e)
{
var Ready = 0;
$(".object").each(function()
{
if (this.object.visible && (this.object.Turning==true || this.object.FlippedV==true))
{
Ready++;
}
});
//Find all the objects with the tag/class card.
$(".object").each(function()
{
//Check if the mouse clicked the object, if it's still part of the game, and if it has not been flipped.
if (e.clickedobject
&& Ready<TurnedMax)
{
//Run the clicked event for the card, this will start events etc.
this.object.Clicked();
//Increase the turned counter, if we have turned the correct amount of objectto be compared
//then compare them.
if (this.Cards.Bonus == false)
{
Turned++;
if (Turned==TurnedMax)
{
//We have turned the amount of object needed
//Find out which value the first object has, and use this as a base to compare if cards match.
//Also instantiate a counter for the amount of cards actually matching.
//It's done this way if you want a variable number of object needed for a match.
var Correct = this.object.value;
var CorrectAmount = 0;
$(".object").each(function()
{
//For each card, if they are flipped, are not going into hiding/deletion, and has the
//Correct value, increase the counter for the number of objects matching.
if (this.object.Flipped == true && this.object.Hiding==0 && this.object.value == Correct)
CorrectAmount++;
});
//If we have a correct match
if (CorrectAmount==TurnedMax)
{
$(".object").each(function()
{
//Foreach object that is flipped and not in hiding, delete them (aka. yay, you got a match).
if (this.object.Flipped==true && this.object.Hiding==0)
this.object.SetVisible(false);
});
}
$(".object").each(function()
{
//Foreach objectthat was not in hiding and was not part of the match, unflip them again.
if (this.object.Flipped==true && this.object.Hiding==0)
this.object.Hide();
});
Turned=0;
}
}
}
});
});
}
// this sets the id of the loading bar (NOT USED YET):
$.loadCallback(function(percent){
$("#loadingBar").width(400*percent);
});
//initialize the start button
$("#startbutton").click(function(){
$.playground().startGame(function(){
$("#welcomeScreen").remove();
});
})
//This is for the background animation (DEBUG)
$("#playground").registerCallback(function(){
$("#background1").x(($("#background1").x() + smallStarSpeed +PLAYGROUND_WIDTH) % (2 * PLAYGROUND_WIDTH) - PLAYGROUND_WIDTH);
$("#background2").x(($("#background2").x() + smallStarSpeed +PLAYGROUND_WIDTH) % (2 * PLAYGROUND_WIDTH) - PLAYGROUND_WIDTH);
$("#background3").x(($("#background3").x() + mediumStarSpeed +PLAYGROUND_WIDTH) % (2 * PLAYGROUND_WIDTH) - PLAYGROUND_WIDTH);
$("#background4").x(($("#background4").x() + mediumStarSpeed +PLAYGROUND_WIDTH) % (2 * PLAYGROUND_WIDTH) - PLAYGROUND_WIDTH);
$("#background5").x(($("#background5").x() + bigStarSpeed +PLAYGROUND_WIDTH) % (2 * PLAYGROUND_WIDTH) - PLAYGROUND_WIDTH);
$("#background6").x(($("#background6").x() + bigStarSpeed +PLAYGROUND_WIDTH) % (2 * PLAYGROUND_WIDTH) - PLAYGROUND_WIDTH);
//Basic Game Engine!!
$(".object").each(function()
{
//For each card, perform their step event.
this.object.Step();
});
}, REFRESH_RATE);
});
You say you're using <meta http-equiv="X-UA-Compatible" content="IE=9">.
That's is unlikely to be helpful in any way at all.
It will have no effect at all in IE9 or IE8.
It will be having an effect in IE10 and IE11, but that effect will be to pull the browser down into backward-compatibility mode, which basically means that you're telling IE10 and higher to switch off some of their newer features and pretend to be IE9.
There's really no point in that at all; in fact, it's probably doing more harm than good, so I recommend removing that line of code entirely.
If you must have an X-UA-Compatible flag, the only sensible value for it is IE=edge. This will force all IE versions to use the best mode they have available. That's the default anyway in most cases, but it might help in the few cases where the default is something else.
But the real problem you're complaining about here is performance, and I can tell you categorically that this is entirely unrelated to the X-UA-Compatible flag -- as I say, the way you have it set won't have any effect on IE8 at all anyway, so it's not that which is slowing things down.
Ultimately, your problem with IE8 may boil down to the simple fact that it is just a much much slower browser than later versions. It is possible that you may simply not be able to get IE8 to perform well enough to achieve what you want.
IE9 and IE10 are much better, so you should be able to get reasonable performance from them with a bit of tweaking, so your best hope would probably be to do some performance tuning.
And the best bet for performance testing is to upgrade to IE11, which has much better dev tools than IE10, including some really good tools for analysing your site's performance.
Performance tuning is big topic in itself (out of scope for this answer), but the ultimate goal is to work out what is causing your biggest bottlenecks, and fix those. In most cases there's generally just one or two major issues that hold everything up, and dealing with them is usually sufficient to make the difference.
So my advice is: Download IE11 and try out the new performance testing features in the new dev tools. Hopfully that'll be enough to get you started, but if you still can't find the problem, try asking a more specific question about whay a particular bit of your code is slow.
One other thing I would suggest is maybe to try reducing your use of libraries like jQuery.
jQuery is incredibly useful, but it does have a performance impact. Try switching to native Javascript where possible.
And where you can't, try reducing the number of times you make the same call. For example:
$("#background1").x(($("#background1").x() + smallStarSpeed +PLAYGROUND_WIDTH) % (2 * PLAYGROUND_WIDTH) - PLAYGROUND_WIDTH);
This calls $("#background1") twice. Do this instead:
var $bg1 = $("#background1");
$bg1.x(($bg1.x() + smallStarSpeed +PLAYGROUND_WIDTH) % (2 * PLAYGROUND_WIDTH) - PLAYGROUND_WIDTH);
Better yet, if you can do it with document.getElementByTagName rather than jQuery, you'll save yourself even more performance.
I also notice that you're repeating some calculations in that code. Obviously, if you can tidy that up to reduce the repeats, it will also help.
I hope that's given you some directions to think.
Put <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9" >
in your html head tag and try. Switching the compatibility view on turns IE into IE8 rendering.
http://html5test.com/compare/browser/ie08/chrome28/ff22.html

Proper way to center pop window with Javascript and pass html

Goal: quick and dirty app (client side only) to grab some arguments from one page and put results onto a new page, which can be printed and then closed. Arguments on the original page then can be changed and new page popped.
Used this as a starting point:
https://developer.mozilla.org/en/DOM/window.open
http://www.yourhtmlsource.com/javascript/popupwindows.html
Proof of concept(final version will have about 10 inputs/args)
HTML fragment
<input type="text" id="x">
<form>
<input type=button value="Calculate" onClick="javascript:genResults()">
</form>
JS
function dirtypop(arg)
{
var popwin=window.open('','name','height=300,width=400,status=1,center=1');
popwin.document.write('<html><head><title>Square</title>');
popwin.document.write('</head><body>');
popwin.document.write('<h1>Squared plus one is: '+arg+'</h1>');
popwin.document.write('<p>Close this window</p>');
popwin.document.write('</body></html>');
popwin.document.close();
};
function genResults()
{
x = document.getElementById('x').value;
if (x == parseFloat(x))
{
dirtypop(x*x+1);
}
};
This works(tested on FF3.5 and Chrome), except new window does not pop into center. How to center it? Mozzila says needs chrome=yes and talks about UniversalPrivilege scripts, what kind of beasts are those?
Anything else that can be improved?
Here's one of my custom cross-browser scripts that can be reused dynamically to center any popped window of any size on the screen:
// here's the script
function popWindow(url,winName,w,h) {
if (window.open) {
if (poppedWindow) { poppedWindow = ''; }
//GET SIZE OF WINDOW/SCREEN
windowW = w;
windowH = h;
var windowX = (screen.width/2)-(windowW/2);
var windowY = (screen.height/2)-(windowH/2);
var myExtra = "status=no,menubar=no,resizable=yes,toolbar=no,scrollbars=yes,addressbar=no";
var poppedWindow = window.open(url,winName,'width='+w+',height='+h+',top='+windowY+',left=' + windowX + ',' + myExtra + '');
setTimeout(refreshThis,3000);
}
else {
alert('Your security settings are not allowing our popup windows to function. Please make sure your security software allows popup windows to be opened by this web application.');
}
}
// and you would call it like this:
popWindow('http://www.myurl.com/','myPoppedWindowName','500','400');
// With this example call you would pop a window with a url of http://www.myurl.com/
// which is given the name of myPoppedWindowName
// and a width of 500px along with height of 400px
// which gets centered on the screen according to these size parameters
This will do the trick considering it truly is a cross-browser implementation, including reverse compatibility to browsers in place back in 2001. It also contains a check to make sure the end-user has popup windows enabled.
You'll need to set the top and left properties instead of center=1.
var left = (screen.width - windowWidth) / 2;
var top = (screen.height - windowHeight) / 2;

Catch browser's "zoom" event in JavaScript

Is it possible to detect, using JavaScript, when the user changes the zoom in a page?
I simply want to catch a "zoom" event and respond to it (similar to window.onresize event).
Thanks.
There's no way to actively detect if there's a zoom. I found a good entry here on how you can attempt to implement it.
I’ve found two ways of detecting the
zoom level. One way to detect zoom
level changes relies on the fact that
percentage values are not zoomed. A
percentage value is relative to the
viewport width, and thus unaffected by
page zoom. If you insert two elements,
one with a position in percentages,
and one with the same position in
pixels, they’ll move apart when the
page is zoomed. Find the ratio between
the positions of both elements and
you’ve got the zoom level. See test
case.
http://web.archive.org/web/20080723161031/http://novemberborn.net/javascript/page-zoom-ff3
You could also do it using the tools of the above post. The problem is you're more or less making educated guesses on whether or not the page has zoomed. This will work better in some browsers than other.
There's no way to tell if the page is zoomed if they load your page while zoomed.
Lets define px_ratio as below:
px ratio = ratio of physical pixel to css px.
if any one zoom The Page, the viewport pxes (px is different from pixel ) reduces and should be fit to The screen so the ratio (physical pixel / CSS_px ) must get bigger.
but in window Resizing, screen size reduces as well as pxes. so the ratio will maintain.
zooming: trigger windows.resize event --> and change px_ratio
but
resizing: trigger windows.resize event --> doesn’t change px_ratio
//for zoom detection
px_ratio = window.devicePixelRatio || window.screen.availWidth / document.documentElement.clientWidth;
$(window).resize(function(){isZooming();});
function isZooming(){
var newPx_ratio = window.devicePixelRatio || window.screen.availWidth / document.documentElement.clientWidth;
if(newPx_ratio != px_ratio){
px_ratio = newPx_ratio;
console.log("zooming");
return true;
}else{
console.log("just resizing");
return false;
}
}
The key point is difference between CSS PX and Physical Pixel.
https://gist.github.com/abilogos/66aba96bb0fb27ab3ed4a13245817d1e
Good news everyone some people! Newer browsers will trigger a window resize event when the zoom is changed.
I'm using this piece of JavaScript to react to Zoom "events".
It polls the window width.
(As somewhat suggested on this page (which Ian Elliott linked to): http://novemberborn.net/javascript/page-zoom-ff3 [archive])
Tested with Chrome, Firefox 3.6 and Opera, not IE.
Regards, Magnus
var zoomListeners = [];
(function(){
// Poll the pixel width of the window; invoke zoom listeners
// if the width has been changed.
var lastWidth = 0;
function pollZoomFireEvent() {
var widthNow = jQuery(window).width();
if (lastWidth == widthNow) return;
lastWidth = widthNow;
// Length changed, user must have zoomed, invoke listeners.
for (i = zoomListeners.length - 1; i >= 0; --i) {
zoomListeners[i]();
}
}
setInterval(pollZoomFireEvent, 100);
})();
This works for me:
var deviceXDPI = screen.deviceXDPI;
setInterval(function(){
if(screen.deviceXDPI != deviceXDPI){
deviceXDPI = screen.deviceXDPI;
... there was a resize ...
}
}, 500);
It's only needed on IE8. All the other browsers naturally generate a resize event.
There is a nifty plugin built from yonran that can do the detection. Here is his previously answered question on StackOverflow. It works for most of the browsers. Application is as simple as this:
window.onresize = function onresize() {
var r = DetectZoom.ratios();
zoomLevel.innerHTML =
"Zoom level: " + r.zoom +
(r.zoom !== r.devicePxPerCssPx
? "; device to CSS pixel ratio: " + r.devicePxPerCssPx
: "");
}
Demo
Although this is a 9 yr old question, the problem persists!
I have been detecting resize while excluding zoom in a project, so I edited my code to make it work to detect both resize and zoom exclusive from one another. It works most of the time, so if most is good enough for your project, then this should be helpful! It detects zooming 100% of the time in what I've tested so far. The only issue is that if the user gets crazy (ie. spastically resizing the window) or the window lags it may fire as a zoom instead of a window resize.
It works by detecting a change in window.outerWidth or window.outerHeight as window resizing while detecting a change in window.innerWidth or window.innerHeight independent from window resizing as a zoom.
//init object to store window properties
var windowSize = {
w: window.outerWidth,
h: window.outerHeight,
iw: window.innerWidth,
ih: window.innerHeight
};
window.addEventListener("resize", function() {
//if window resizes
if (window.outerWidth !== windowSize.w || window.outerHeight !== windowSize.h) {
windowSize.w = window.outerWidth; // update object with current window properties
windowSize.h = window.outerHeight;
windowSize.iw = window.innerWidth;
windowSize.ih = window.innerHeight;
console.log("you're resizing"); //output
}
//if the window doesn't resize but the content inside does by + or - 5%
else if (window.innerWidth + window.innerWidth * .05 < windowSize.iw ||
window.innerWidth - window.innerWidth * .05 > windowSize.iw) {
console.log("you're zooming")
windowSize.iw = window.innerWidth;
}
}, false);
Note: My solution is like KajMagnus's, but this has worked better for me.
⬤ The resize event works on modern browsers by attaching the event on window, and then reading values of thebody, or other element with for example (.getBoundingClientRect()).
In some earlier browsers it was possible to register resize event
handlers on any HTML element. It is still possible to set onresize
attributes or use addEventListener() to set a handler on any element.
However, resize events are only fired on the window object (i.e.
returned by document.defaultView). Only handlers registered on the
window object will receive resize events.
⚠️ Do resize your tab, or zoom, to trigger this snippet:
window.addEventListener("resize", getSizes, false)
function getSizes(){
let body = document.body
body.width = window.innerWidth
body.height = window.innerHeight
console.log(body.width +"px x "+ body.height + "px")
}
getSizes()
⬤ An other modern alternative: the ResizeObserver API
Depending your layout, you can watch for resizing on a particular element.
This works well on «responsive» layouts, because the container box get resized when zooming.
function watchBoxchange(e){
info.textContent = e[0].contentBoxSize[0].inlineSize+" x "+e[0].contentBoxSize[0].blockSize + "px"
}
new ResizeObserver(watchBoxchange).observe(fluid)
#fluid {
width: 200px;
height:100px;
overflow: auto;
resize: both;
border: 3px black solid;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 8vh
}
<div id="fluid">
<info id="info"></info>
</div>
💡 Be careful to not overload javascript tasks from user gestures events. Use requestAnimationFrame whenever you needs redraws.
I'd like to suggest an improvement to previous solution with tracking changes to window width. Instead of keeping your own array of event listeners you can use existing javascript event system and trigger your own event upon width change, and bind event handlers to it.
$(window).bind('myZoomEvent', function() { ... });
function pollZoomFireEvent()
{
if ( ... width changed ... ) {
$(window).trigger('myZoomEvent');
}
}
Throttle/debounce can help with reducing the rate of calls of your handler.
According to MDN, "matchMedia" is the proper way to do this https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#Monitoring_screen_resolution_or_zoom_level_changes
it's a bit finicky because each instance can only watch one MQ at a time, so if you're interested in any zoom level change you need to make a bunch of matchers.. but since the browser is in charge to emitting the events it's probably still more performant than polling, and you could throttle or debounce the callback or pin it to an animation frame or something - here's an implementation that seems pretty snappy, feel free to swap in _throttle or whatever if you're already depending on that.
Run the code snippet and zoom in and out in your browser, note the updated value in the markup - I only tested this in Firefox! lemme know if you see any issues.
const el = document.querySelector('#dppx')
if ('matchMedia' in window) {
function observeZoom(cb, opts) {
opts = {
// first pass for defaults - range and granularity to capture all the zoom levels in desktop firefox
ceiling: 3,
floor: 0.3,
granularity: 0.05,
...opts
}
const precision = `${opts.granularity}`.split('.')[1].length
let val = opts.floor
const vals = []
while (val <= opts.ceiling) {
vals.push(val)
val = parseFloat((val + opts.granularity).toFixed(precision))
}
// construct a number of mediamatchers and assign CB to all of them
const mqls = vals.map(v => matchMedia(`(min-resolution: ${v}dppx)`))
// poor person's throttle
const throttle = 3
let last = performance.now()
mqls.forEach(mql => mql.addListener(function() {
console.debug(this, arguments)
const now = performance.now()
if (now - last > throttle) {
cb()
last = now
}
}))
}
observeZoom(function() {
el.innerText = window.devicePixelRatio
})
} else {
el.innerText = 'unable to observe zoom level changes, matchMedia is not supported'
}
<div id='dppx'>--</div>
You can also get the text resize events, and the zoom factor by injecting a div containing at least a non-breakable space (possibly, hidden), and regularly checking its height. If the height changes, the text size has changed, (and you know how much - this also fires, incidentally, if the window gets zoomed in full-page mode, and you still will get the correct zoom factor, with the same height / height ratio).
<script>
var zoomv = function() {
if(topRightqs.style.width=='200px){
alert ("zoom");
}
};
zoomv();
</script>
On iOS 10 it is possible to add an event listener to the touchmove event and to detect, if the page is zoomed with the current event.
var prevZoomFactorX;
var prevZoomFactorY;
element.addEventListener("touchmove", (ev) => {
let zoomFactorX = document.documentElement.clientWidth / window.innerWidth;
let zoomFactorY = document.documentElement.clientHeight / window.innerHeight;
let pageHasZoom = !(zoomFactorX === 1 && zoomFactorY === 1);
if(pageHasZoom) {
// page is zoomed
if(zoomFactorX !== prevZoomFactorX || zoomFactorY !== prevZoomFactorY) {
// page is zoomed with this event
}
}
prevZoomFactorX = zoomFactorX;
prevZoomFactorY = zoomFactorY;
});
Here is a clean solution:
// polyfill window.devicePixelRatio for IE
if(!window.devicePixelRatio){
Object.defineProperty(window,'devicePixelRatio',{
enumerable: true,
configurable: true,
get:function(){
return screen.deviceXDPI/screen.logicalXDPI;
}
});
}
var oldValue=window.devicePixelRatio;
window.addEventListener('resize',function(e){
var newValue=window.devicePixelRatio;
if(newValue!==oldValue){
// TODO polyfill CustomEvent for IE
var event=new CustomEvent('devicepixelratiochange');
event.oldValue=oldValue;
event.newValue=newValue;
oldValue=newValue;
window.dispatchEvent(event);
}
});
window.addEventListener('devicepixelratiochange',function(e){
console.log('devicePixelRatio changed from '+e.oldValue+' to '+e.newValue);
});
Here is a native way (major frameworks cannot zoom in Chrome, because they dont supports passive event behaviour)
//For Google Chrome
document.addEventListener("mousewheel", event => {
console.log(`wheel`);
if(event.ctrlKey == true)
{
event.preventDefault();
if(event.deltaY > 0) {
console.log('Down');
}else {
console.log('Up');
}
}
}, { passive: false });
// For Mozilla Firefox
document.addEventListener("DOMMouseScroll", event => {
console.log(`wheel`);
if(event.ctrlKey == true)
{
event.preventDefault();
if(event.detail > 0) {
console.log('Down');
}else {
console.log('Up');
}
}
}, { passive: false });
I'am replying to a 3 year old link but I guess here's a more acceptable answer,
Create .css file as,
#media screen and (max-width: 1000px)
{
// things you want to trigger when the screen is zoomed
}
EG:-
#media screen and (max-width: 1000px)
{
.classname
{
font-size:10px;
}
}
The above code makes the size of the font '10px' when the screen is zoomed to approximately 125%. You can check for different zoom level by changing the value of '1000px'.

Categories