Related
Hi please help me to find out how trully responsive game can be created with Phaser3.
Respnsiveness is critical because game (representation layer of Blockly workspace) should be able to be exapanded to larger portion on screen and shrinked back many times during the session.
The question is How I can change dimentions of the game in runtime?
--- edited ---
It turns out there is pure css solution, canvas can be ajusted with css zoom property. In browser works well (no noticeable effect on performance), in cordova app (android) works too.
Here is Richard Davey's answer if css zoom can break things:
I've never actually tried it to be honest. Give it a go and see what
happens! It might break input, or it may carry on working. That (and
possibly the Scale Manager) are the only things it would break,
though, nothing else is likely to care much.
// is size of html element size that needed to fit
let props = { w: 1195, h: 612, elementId: 'myGame' };
// need to know game fixed size
let gameW = 1000, gameH = 750;
// detect zoom ratio from width or height
let isScaleWidth = gameW / gameH > props.w / props.h ? true : false;
// find zoom ratio
let zoom = isScaleWidth ? props.w / gameW : props.h / gameH;
// get DOM element, props.elementId is parent prop from Phaser game config
let el = document.getElementById(props.elementId);
// set css zoom of canvas element
el.style.zoom = zoom;
Resize the renderer as you're doing, but you also need to update the world bounds, as well as possibly the camera's bounds.
// the x,y, width and height of the games world
// the true, true, true, true, is setting which side of the world bounding box
// to check for collisions
this.physics.world.setBounds(x, y, width, height, true, true, true, true);
// setting the camera bound will set where the camera can scroll to
// I use the 'main' camera here fro simplicity, use whichever camera you use
this.cameras.main.setBounds(0, 0, width, height);
and that's how you can set the world boundary dynamically.
window.addEventListener('resize', () => {
game.resize(window.innerWidth, window.innerHeight);
});
There is some builtin support for resizing that can be configured in the Game Config. Check out the ScaleManager options. You have a number of options you can specify in the scale property, based on your needs.
I ended up using the following:
var config = {
type: Phaser.AUTO,
parent: 'game',
width: 1280, // initial width that determines the scaled size
height: 690,
scale: {
mode: Phaser.Scale.WIDTH_CONTROLS_HEIGHT ,
autoCenter: Phaser.Scale.CENTER_BOTH
},
physics: {
default: 'arcade',
arcade: {
gravity: {y: 0, x: 0},
debug: true
}
},
scene: {
key: 'main',
preload: preload,
create: this.create,
update: this.update
},
banner: false,
};
Just in case anybody else still has this problem, I found that just resizing the game didn't work for me and physics didn't do anything in my case.
To get it to work I needed to resize the game and also the scene's viewport (you can get the scene via the scenemanager which is a property of the game):
game.resize(width,height)
scene.cameras.main.setViewport(0,0,width,height)
For Phaser 3 the resize of the game now live inside the scale like this:
window.addEventListener('resize', () => {
game.scale.resize(window.innerWidth, window.innerHeight);
});
But if you need the entire game scale up and down only need this config:
const gameConfig: Phaser.Types.Core.GameConfig = {
...
scale: {
mode: Phaser.Scale.WIDTH_CONTROLS_HEIGHT,
},
...
};
export const game = new Phaser.Game(gameConfig);
Take a look at this article.
It explains how to dynamically resize the canvas while maintaining game ratio.
Note: All the code below is from the link above. I did not right any of this, it is sourced from the article linked above, but I am posting it here in case the link breaks in the future.
It uses CSS to center the canvas:
canvas{
display:block;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
It listens to the window 'resize' event and calls a function to resize the game.
Listening to the event:
window.onload = function(){
var gameConfig = {
//config here
};
var game = new Phaser.Game(gameConfig);
resize();
window.addEventListener("resize", resize, false);
}
Resizing the game:
function resize() {
var canvas = document.querySelector("canvas");
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
var windowRatio = windowWidth / windowHeight;
var gameRatio = game.config.width / game.config.height;
if(windowRatio < gameRatio){
canvas.style.width = windowWidth + "px";
canvas.style.height = (windowWidth / gameRatio) + "px";
}
else{
canvas.style.width = (windowHeight * gameRatio) + "px";
canvas.style.height = windowHeight + "px";
}
}
I have used and modified this code in my phaser 3 project and it works great.
If you want to see other ways to resize dynamically check here
Put this in your create function:
const resize = ()=>{
game.scale.resize(window.innerWidth, window.innerHeight)
}
window.addEventListener("resize", resize, false);
Important! Make sure you have phaser 3.16+
Or else this won't work!!!
Use the game.resize function:
// when the page is loaded, create our game instance
window.onload = () => {
var game = new Game(config);
game.resize(400,700);
};
Note this only changes the canvas size, nothing else.
I am using ZingChart for a standard bar graph. I have the selected state for individual bars working as I would like but for one thing. Is there a way to show the value box (set to visible:false globally) to show just for the selected node when it is clicked/selected? I was able to make the value box for every node show in a click event I added to call an outside function using the modifyplot method but I don't see a similar method for nodes such as modifynode. If this is not an option, is there any way to insert a "fake" value box the markup of which would be created on the fly during the click event and have that element show above the selected node? Below is my render code for the chart in question. Thanks for your time!
zingchart.render({
id: "vsSelfChartDiv",
width: '100%',
height: '100%',
output: 'svg',
data: myChartVsSelf,
events:{
node_click:function(p){
zingchart.exec('vsSelfChartDiv', 'modifyplot', {
graphid : 0,
plotindex : p.plotindex,
nodeindex : p.nodeindex,
data : {
"value-box":{
"visible":true
}
}
});
var indexThis = p.nodeindex;
var indexDateVal = $('#vsSelfChartDiv-graph-id0-scale_x-item_'+indexThis).find('tspan').html();
updateTop(indexDateVal);
}
}
});
You'd probably be better off using a label instead of a value-box. I've put together a demo here.
I'm on the ZingChart team. Feel free to hit me up if you have any more questions.
// Set up your data
var myChart = {
"type":"line",
"title":{
"text":"Average Metric"
},
// The label below will be your 'value-box'
"labels":[
{
// This id allows you to access it via the API
"id":"label1",
"text":"",
// The hook describes where it attaches
"hook":"node:plot=0;index=2",
"border-width":1,
"background-color":"white",
"callout":1,
"offset-y":"-30%",
// Hide it to start
"visible":false,
"font-size":"14px",
"padding":"5px"
}
],
// Tooltips are turned off so we don't have
// hover info boxes and click info boxes
"tooltip":{
"visible":false
},
"series":[
{
"values":[69,68,54,48,70,74,98,70,72,68,49,69]
}
]
};
// Render the chart
zingchart.render({
id:"myChart",
data:myChart
});
// Bind your events
// Shows label and sets it to the plotindex and nodeindex
// of the clicked node
zingchart.bind("myChart","node_click",function(p){
zingchart.exec("myChart","updateobject", {
"type":"label",
"data":{
"id":"label1",
"text":p.value,
"hook":"node:plot="+p.plotindex+";index="+p.nodeindex,
"visible":true
}
});
});
// Hides callout label when click is not on a node
zingchart.bind("myChart","click",function(p){
if (p.target != 'node') {
zingchart.exec("myChart","updateobject", {
"type":"label",
"data":{
"id":"label1",
"visible":false
}
});
}
});
<script src='http://cdn.zingchart.com/zingchart.min.js'></script>
<div id="myChart" style="width:100%;height:300px;"></div>
How to set non pixels position?
I try this
var stack = { "dir1": "down", "dir2": "right", "firstpos1": 50, "firstpos2": 50 };
But I this it is bad because of different screen resolution.
Necroposting, but may help to other searchers. My solution without js recalculations:
js:
new PNotify({
...
addclass: 'pnotify-center'
});
css:
.pnotify-center {
right: calc(50% - 150px) !important;
}
there's a similar question with an answer here. As per the first example in the stacks documentation, you can center the initial position of the notification by setting the top/left css propreties in before_open. You also need to reposition the notification everytime the window is resized.
function get_center_pos(width, top) {
// top is empty when creating a new notification and is set when recentering
if (!top) {
top = 30;
// this part is needed to avoid notification stacking on top of each other
$('.ui-pnotify').each(function() {
top += $(this).outerHeight() + 20;
});
}
return {
"top": top,
"left": ($(window).width() / 2) - (width / 2)
}
}
$(document).ready(function() {
new PNotify({
title: "this is center",
text: "blablabla",
opacity: 0.90,
type: "info",
width: "390px",
before_open: function(PNotify) {
PNotify.get().css(get_center_pos(PNotify.get().width()));
}
});
$(window).resize(function() {
$(".ui-pnotify").each(function() {
$(this).css(get_center_pos($(this).width(), $(this).position().top))
});
});
});
I am testing the <webview> element in Chrome and I have gone through the documentation but I cannot figure out why the Webview is not resizing when the parent window does.
Index.Html
<body>
<webview src="http://website.com" style="width:1010px; height:700px" minwidth="1010" minheight="700" autosize="on""></webview>
<script src="index.js"></script>
background.js
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('index.html', {
'bounds': {
'width': 1010,
'height': 700
}});});
Index.js
$(function() {
function resizeWebView() {
$('webview').get(0).width = $(window).width();
$('webview').get(0).height = $(window).height();
}
$(window).resize(resizeWebView);
resizeWebView();
});
I tried also removing the autosize=on as recommended but it does not work.
Another question how to disable the main window (Embedder) from resizing.
Thanks
You should use the chrome.app.window.onBoundsChanged API on the window object returned by chrome.app.window.create. Simple implementation:
background.js
var appWin = null;
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create(
'index.html',
{'bounds': {'width': 1010, 'height': 700}},
onWindowCreated
);
});
function onWindowCreated(win) {
appWin = win;
// Resize the webview when the window resizes.
appWin.onBoundsChanged.addListener(onBoundsChanged);
// Initialize the webview size once on launch.
onBoundsChanged();
}
function onBoundsChanged() {
var webview = document.querySelector('webview');
var bounds = appWin.getBounds();
webview.style.height = bounds.height + 'px';
webview.style.width = bounds.width + 'px';
}
index.js
// Nothing here
See a much more elaborate object-oriented example with multiple windows and persisting/reusing the last window size set by the user here: https://github.com/GoogleChrome/chrome-app-samples/tree/master/url-handler.
As to your second question, it would be good if you asked it separately, but nevertheless: just set the resizable parameter of chrome.app.window.create to false:
...
chrome.app.window.create(
'index.html',
{
'bounds': {'width': 1010, 'height': 700},
'resizable': false
},
...
I set out to achieve something similar and the following approach is how I implemented it.
index.html
<webview src="http://website.com" style="width:1010px; height:700px"></webview>
background.js
The code you have here looks correct.
index.js
I approached this in a slightly different way by using the window.onresize event to handle the resize. Please keep in mind that resize event is fired after the screen has been resized and may seem to create a slightly laggy feel.
window.onresize = setWebview;
function setWebview() {
var webview = document.querySelector('webview');
var webviewWidth = document.documentElement.clientWidth;
var webviewHeight = document.documentElement.clientHeight;
webview.style.width = webviewWidth + 'px';
webview.style.height = webviewHeight + 'px';
}
I also call the setWebview(); function in the onLoad or something of the sorts for the initial setting of the webview.
So that is my approach, looking at your index.js I think you may be having an issue in that you are trying to set the width and height of the <webview> when in fact you need to be setting the style width and height, so maybe the following would have worked;
$('webview').get(0).style.width = $(window).width();
$('webview').get(0).style.height = $(window).height();
Notice that I added in .style before .width and .height
Hope that helps..
I don't know if this is a new thing but I did exactly this with CSS:
webview {
height: 100%;
width: 100%;
}
Easy as pie!
I am using select2 plugin inside a jquery dialog but in does not work. When dropping down, the focus moves to the input control but immediately get out from it,not allowing me to type anything.
This is the HTML:
<div id="asignar_servicio" title="Asignar servicios a usuarios">
<input type="hidden" class="bigdrop" id="a_per_id" />
</div>
And this is the javascript code:
$( "#asignar_servicio" ).dialog({
autoOpen: false,
height: 500,
width: 450,
modal: true,
buttons: {
"Cancelar": function () {
$('#asignar_servicio').dialog('close');
}
}
});
$("#a_per_id").select2({
placeholder: "Busque un funcionario",
width: 400,
minimumInputLength: 4,
ajax: {
url: "#Url.Action("Search", "Personal")",
dataType: 'json',
data: function (term, page) {
return {
q: term,
page_limit: 10,
};
},
results: function (data, page) {
return { results: data.results };
}
}
}).on("change", function (e) {
var texto = $('lista_personal_text').val().replace(/ /g, '');
if (texto != '')
texto += ',';
texto += e.added.text;
var ids = $('lista_personal_id').val().replace(/ /g, '');
if (ids != '')
ids += ',';
ids += e.added.id;
});
I have this same code in other page and it works.
Any help will be appreciated,
thanks
Jaime
jstuardo's link is good, but there's a lot to sift through on that page. Here's the code you need:
$.ui.dialog.prototype._allowInteraction = function(e) {
return !!$(e.target).closest('.ui-dialog, .ui-datepicker, .select2-drop').length;
};
Just add it next to wherever you are setting the select2 drop down.
An easy way:
$.ui.dialog.prototype._allowInteraction = function (e) {
return true;
};
add this after whereever you set select2
Or try this from:
Select2 doesn't work when embedded in a bootstrap modal
Remove tabindex="-1" from the modal div
I have found this workaround. https://github.com/ivaynberg/select2/issues/1246
Cheers
Jame
There's a new version of the fix for select2 4.0 from the github issue thread about this problem:
if ($.ui && $.ui.dialog && $.ui.dialog.prototype._allowInteraction) {
var ui_dialog_interaction = $.ui.dialog.prototype._allowInteraction;
$.ui.dialog.prototype._allowInteraction = function(e) {
if ($(e.target).closest('.select2-dropdown').length) return true;
return ui_dialog_interaction.apply(this, arguments);
};
}
Just run this before any modal dialogs that will have select2 in them are created.
JSFiddle of this fix in action
The best solution I found was just making the dialog not be a modal dialog by removing modal:true. Once you do this the page will function as desired.
After a while of battling with this I found another option that allows you to keep the dialog as a modal. If you modify the css for select2 to something like the following:
.select2-drop {
z-index: 1013;
}
.select2-results {
z-index: 999;
}
.select2-result {
z-index: 1010;
}
keep in mind that this works however if you open a lot of dialogs on the same page it will eventually exceed the z-index specified, however in my use case these numbers got the job done.
Not enough reputation to comment on a previous post, but I wanted to add this bit of code:
$('#dialogDiv').dialog({
title: "Create Dialog",
height: 410,
width: 530,
resizable: false,
draggable: false,
closeOnEscape: false,
//in order for select2 search to work "modal: true" cannot be present.
//modal: true,
position: "center",
open: function () { },
close: function () { $(this).dialog("distroy").remove(); }
});
$("#displaySelectTwo")select2();
Updating to the newer version of JQuery and Select2 is not an option in our application at this time. (using JQueryUI v1.8 and Select2 v1)
Add this after your select2() declaration.
$.ui.dialog.prototype._allowInteraction = function (e) {
return !!$(e.target).closest('.ui-dialog, .ui-datepicker, .select2-dropdown').length;
};
I've used the following fix with success:
$.fn.modal.Constructor.prototype.enforceFocus = function () {
var that = this;
$(document).on('focusin.modal', function (e) {
if ($(e.target).hasClass('select2-input')) {
return true;
}
if (that.$element[0] !== e.target && !that.$element.has(e.target).length) {
that.$element.focus();
}
});
}
I could fix this by removing the option: 'modal: true' from the dialog options.
It worked fine.
For anyone stumpling upon this with Select2 v4.0.12
I was using the Select2 option dropdownParent
i set the dropDownParent value, and still had the issue.
dropdownParent: $("#ReportFilterDialog")
What fixed it for me, was setting the value to, to select the outer layer of the modal dialog:
dropdownParent: $("#ReportFilterDialog").parent()