I am using node/npm blessed to create a screen to monitor a particular application and I want the monitor tool to look/act like linux top. The problem I have, which I think is relatively simple (but I cant find the answer anywhere), is to have blessed clear the screen at startup as well as at particular points based upon user input.
Right now I can use blessed to paint the screen properly (sample program below), but the problem is I need the screen to be cleared at startup (right now the first line of output just appends right after you start up the program):
var blessed = require('blessed');
var screen = blessed.screen(),
body = blessed.box({
top: 0,
left: 0,
width: '100%',
height: '100%',
tags: true
});
var items = [];
screen.append(body);
screen.key(['escape', 'q', 'C-c'], function(ch, key) {
return process.exit(0);
});
function log(text) {
items.push(text);
var MAX = 10;
if( items.length > MAX+1 ){
items.shift();
}
for (var i=1; i<MAX; i++){
body.setLine(i, items[i]);
}
screen.render();
}
function status(text) {
body.setLine(0, text );
screen.render();
}
var counter = 1;
setInterval(function() {
status((new Date()).toISOString());
log('Line number: ' + (counter++));
}, 1000);
For completeness, the solution to this question is provided below, and is very simple. To clear the screen, issue the following code at the start of the program:
screen.render();
The sample program that I referenced in the original post can be modified as follows and now works:
var blessed = require('blessed');
var screen = blessed.screen(),
body = blessed.box({
top: 0,
left: 0,
width: '100%',
height: '100%',
tags: true
});
// The following line clears the screen
screen.render();
:
:
Related
In my Phaser 3 game I'm using Phaser.GameObjects.Image 's as things that a user can click on. When a user's mouse hovers over an image I would like a tooltip with text to fade in and appear. When the mouse moves off the image, I'd like the tooltip to disappear.
How can I implement this behavior in Phaser? I'm new to Phaser and I don't see a ToolTip class in the framework.
You could:
use the pointer events for detecting, that the pointer is over an object
and animate/tween the alpha property on the over event
you can alter the speed with the tween duration
and hide the toolTip on the out event
Here the docs to the Input events: https://photonstorm.github.io/phaser3-docs/Phaser.Input.Events.html
Here a mini example:
var config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 800,
height: 600,
scene: {
create: create
}
};
var game = new Phaser.Game(config);
var toolTip;
var toolTipText;
function create ()
{
let objectWithToolTip = this.add.rectangle( 100, 100, 100, 100, 0xffffff).setInteractive();
toolTip = this.add.rectangle( 0, 0, 250, 50, 0xff0000).setOrigin(0);
toolTipText = this.add.text( 0, 0, 'This is a white rectangle', { fontFamily: 'Arial', color: '#000' }).setOrigin(0);
toolTip.alpha = 0;
this.input.setPollOnMove();
this.input.on('gameobjectover', function (pointer, gameObject) {
this.tweens.add({
targets: [toolTip, toolTipText],
alpha: {from:0, to:1},
repeat: 0,
duration: 500
});
}, this);
this.input.on('gameobjectout', function (pointer, gameObject) {
toolTip.alpha = 0;
toolTipText.alpha = 0;
});
objectWithToolTip.on('pointermove', function (pointer) {
toolTip.x = pointer.x;
toolTip.y = pointer.y;
toolTipText.x = pointer.x + 5;
toolTipText.y = pointer.y + 5;
});
}
<script src="//cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.min.js"></script>
Info: if you want to dig deeper into the phaser events you can checkout some examples on the offical home page: https://phaser.io/examples/v3/category/input/mouse are really good, and explore may use cases.
Extra Info: If you don't want to re-invent the wheel, there are several plugins, that can be loaded into phaser(that add special functionality to phaser).
It is always good to check this page(https://phaserplugins.com/), before implementing so common feature/function. There is even on specially for tooltips https://github.com/netgfx/Phaser-tooltip
fabricjs_2.3.6
I am working on an editor that will allow the user to create textboxes images and rectangles dynamically and that portion of the project works great. All objects on the canvas will pre-existing before creating groups therefore I do not want to hard-code any fabrics. I also want to preserve the positional relation between the grouped items.
I am having a problem with creating groups by selecting 2 or more existing objects on the canvas to create a group. My code works with one exception, if the selected items are not in the upper left corner of the canvas when I create the group the grouped objects remain in the original location as desired but the move handle is in the upper left corner of the canvas. If you click the move cursor handle ghost and then click a blank area on the canvas the problem goes away and everything works fine after that.
I have had no luck searching for a solution possibly because I'm not sure what controls the move cursor's location on the canvas or if it is actually called the "move cursor".
Here is a picture from my jsfiddle after clicking the group button:
Here is a jsfiddle link to demonstrate the problem: https://jsfiddle.net/Larry_Robertson/xfrd278a/
Here is my code:
HTML
<span> Test 1: select both the line and the text objects with the mouse then click group, works great.</span>
<br/>
<span> Test 2: select both the line and the text objects with the mouse, move the selction to the center of the canvas then click group. This creates the problem where a ghost move handle is left behind in the upper left corner of the canvas. The move handle did not update to the correct position when the group was created. If you hover the mouse in the upper left of canvas you will see the move cursor. Click on the move cursor then click any blank part of the canvas then you can reselect the group and it moves properly. What am I doing wrong???</span>
<br/>
<button id="group">group</button>
<canvas id="c" height="300" width="500"></canvas>
JS
var canvas = new fabric.Canvas('c');
var text = new fabric.Text('hello world', {
fontSize: 30,
originX: 'left',
originY: 'top',
left: 0,
top: 0
});
var line = new fabric.Line([10, 10, 100, 100], {
stroke: 'green',
strokeWidth: 2
});
canvas.add(line);
canvas.add(text);
canvas.renderAll();
$('#group').on(("click"), function(el) {
var activeObject = canvas.getActiveObject();
var selectionTop = activeObject.get('top');
var selectionLeft = activeObject.get('left');
var selectionHeight = activeObject.get('height');
var selectionWidth = activeObject.get('width');
if (activeObject.type === 'activeSelection') {
var group = new fabric.Group([activeObject], {
left: 0,
top: 0,
originX: 'center',
originY: 'center'
});
canvas.add(group);
deleteSelectedObjectsFromCanvas();
canvas.setActiveObject(group);
group = canvas.getActiveObject();
group.set('top', selectionTop + (selectionHeight / 2));
group.set('left', selectionLeft + (selectionWidth / 2));
group.set('originX', 'center');
group.set('originY', 'center');
canvas.renderAll();
}
});
function deleteSelectedObjectsFromCanvas() {
var selection = canvas.getActiveObject();
var editModeDected = false;
if (selection.type === 'activeSelection') {
selection.forEachObject(function(element) {
console.log(element);
if (element.type == 'textbox') {
if (element.isEditing == true) {
//alert('At least one textbox is currently being edited. No objects were deleted');
editModeDected = true;
}
}
});
if (editModeDected == true) {
return false;
}
// Its okay to delete all selected objects
selection.forEachObject(function(element) {
console.log('removing: ' + element.type);
//element.set('originX',null);
//element.set('originY',null);
canvas.remove(element);
canvas.discardActiveObject();
canvas.requestRenderAll();
});
} else {
if (selection.isEditing == true && selection.type == 'textbox') {
//alert('Textbox is currently being edited. No objects were deleted');
} else {
canvas.remove(selection);
canvas.discardActiveObject();
canvas.requestRenderAll();
}
}
}
CSS
#c {
margin: 10px;
padding: 10px;
border: 1px solid black;
}
After making changes on code always do a setCoords on the object(s) that got changed.
Here is a one liner you can add after the renderAll to fix your issue:
...
group.set('originY', 'center');
canvas.renderAll();
canvas.forEachObject(function(o) {o.setCoords()});
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.
Long story short: I want to build a CLI for a development daemon. Daemon outputs different kind of information to stdout and I want to relay that information to user in an area of screen, in scrollable fashion.
I am struggling with getting stdout to blessed. Simple prototype below, which buffers stdout, so information is never complete.
var blessed = require('blessed');
var screen = blessed.screen(),
body = blessed.box({
top: 1,
left: 0,
width: '100%',
height: '99%'
}),
statusbar = blessed.box({
top: 0,
left: 0,
width: '100%',
height: 1,
style: {
fg: 'white',
bg: 'blue'
}
});
screen.append(statusbar);
screen.append(body);
screen.key(['escape', 'q', 'C-c'], function(ch, key) {
return process.exit(0);
});
function status(text) { statusbar.setContent(text); screen.render(); }
function log(text) { body.insertLine(0, text); screen.render(); }
status('TEST');
var spawn = require('child_process').spawn;
yes = spawn('yes', ['it is']);
ls.stdout.on('data', function (data) {
log(data.toString());
});
ls.stderr.on('data', function (data) {
log(data.toString());
});
The line
var screen = blessed.screen()
Takes parameters in order to change where the input comes from, and where the output goes too. Logically, they're named input and output:
var screen = blessed.screen({ input: some_input_stream, output: some_output_stream })
So, you just chuck a writeable (or duplex) stream into the output parameter. Then, you can pipe the output anywhere you wish.
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))
});
});
});