I'd like to have a canvas on my html page, with width = 100%. I'd like to then get the width in pixels of the canvas at runtime. Something like:
Canvas c = Canvas.createIfSupported();
c.setWidth("100%");
c.setHeight("100%");
// let's draw a rectangle to fill the whole canvas:
c.getContext2d().rect(0, 0, ?, ?); // <-- what's its actual width/height?
Thanks
It's (surprisingly)
c.getContext2d().rect(0, 0, 299, 149);
... no matter what the actual pixel size of the canvas is. The reason is, that 300x150 is the default coordinate space size, see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#the-canvas-element
You can change this by using canvas.setCoordinateSpaceWidth() and canvas.setCoordinateSpaceHeight() to anything you want.
Often, people want to draw independently of the actual size of the canvas, so they just set the coordinate space to something like 100x100, and the drawn image gets scaled automatically.
However, you may want to find out about the actual canvas pixel size:
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
#Override
public void execute() {
final int clientWidth = canvas.getElement().getClientWidth();
final int clientHeight = canvas.getElement().getClientHeight();
setupCoordinateSpace(canvas, clientWidth, clientHeight);
drawMyImage(canvas);
}
});
Note, that this must be done in scheduleDeferred, because the client size will only be known after the browser had a chance to perform the layout.
You can use this information to adjust the aspect ratio to match the canvas (which I would recommend, because otherwise you get distorted images)
void setupCoordinateSpace(Canvas canvas, int clientWidth, int clientHeight) {
final double aspect = (double) clientWidth / (double) clientHeight;
canvas.setCoordinateSpaceHeight(
(int) (myCoordinateSpaceWidth / aspect));
}
Or, alternatively, you can also set the coordinate space to match the pixel size, so you can perform pixel-exact drawing on an HTML5 canvas:
void setupCoordinateSpace(Canvas canvas, int clientWidth, int clientHeight) {
canvas.setCoordinateSpaceWidth(clientWidth);
canvas.setCoordinateSpaceHeight(clientHeight);
}
After the coordinate space has been set up, you can call drawMyImage()
use canvas width and height methods
var width = c.width;
var height = c.height;
Related
I hate to ask such a generic question but I'm really stuck and super hoping someone can help me along the way. Here is the situation:
I'm making the GUI for a mobile app, portrait mode. I'm using canvas scalers to scale my canvasses with a reference width of 1080. This means I effectively don't know the height of my screen space.
What I want to create is a menu with a variable amount of items. The menu must be anchored to the bottom (with an offset margin) and grow upwards. So far I've been able to manage this using VerticalLayoutGroup and anchoring the rect transform to the bottom.
But my last requirement is that if the content would grow too big, a scrollbar would appear. The definition of the content being too big is: if it would extend the (unknown) screen height ( minus the offset margin of course). I hope the following image illustrates this much clearer:
I have a unity project here: https://ufile.io/v31br
Did you give a try to scrollView? here it is: https://unity3d.com/learn/tutorials/topics/user-interface-ui/scroll-view
You can use your vertical layout inside it and you will probably want to deactivate horizontal scroll and delete the horizontal slider.
Via script you can check its rectTransform height and compare it to your container's size, when reached maxHeight you can start managing your item's sizes
I assume you use the ScrollRect component as it is the right component to use in your case.
You can check the screen height with the Screen.height property.
Once you know the screen height you can compare it with your rect height and toggle the scrollbar with the ScrollRect.vertical property. You may have to change the ScrollRect.verticalScrollbarVisibility to permanent in order to make it work for you.
The answer Dave posted was close, but the problem is that the scrollview doesn't expand. I fixed it eventually by stretching the scrollview and resizing the parent manually as items are added. I set the anchors to the maximum size and adjust the sizeDelta.
public class MenuScript : MonoBehaviour
{
public int MenuItemCount;
public GameObject MenuItemPrefab;
public Transform MenuItemParent;
private RectTransform _rectTransform;
void Start()
{
_rectTransform = GetComponent<RectTransform>();
for (var i = 0; i <= MenuItemCount; i++)
{
GameObject instance = Instantiate(MenuItemPrefab, MenuItemParent, false);
instance.GetComponent<Text>().text = instance.name = "Item " + i;
float size = instance.transform.GetComponent<RectTransform>().sizeDelta.y;
TryExpandBy(size + 10);
}
}
private void TryExpandBy(float size)
{
var deltaY = _rectTransform.sizeDelta.y + size;
if (deltaY > 0) deltaY = 0;
_rectTransform.sizeDelta = new Vector2(_rectTransform.sizeDelta.x, deltaY);
}
}
I'm trying to create an InputField (TextMesh Pro) with a dynamic (its text content may vary) prefix.
This spectacular image should explain the goal.
https://imgur.com/a/qx1eXOa
So I set a TextMeshPro text to use as Prefix, and by script I was trying to "move" the TextArea accordingly.
The fact is, TextArea is a RectTransform, and I'm operating in a ScreenSpace render mode.
I was trying like this:
private TextMeshProGUI prefix;
private RecTransform textArea;
public void ChangePrefixTo(string newPrefix)
{
float oldWidth = prefix.preferredWidth;
prefix.text = newPrefix;
float newWidth = prefix.preferredWidth;
Vector2 newPos = new Vector2();
newPos.x = textArea.position.x + (newWidth - oldWidth);
newPos.y = textArea.position.y;
textArea.position = newPos;
}
, but the textArea gets shot into the stars.
How can I map the RectTransform position according to the size of a TextMeshPro text?
Thanks for the help and long live the whales
Instead of using a script to resize your prefix, you can group both of your elements in a Horizontal Layout Group, check only Width for Child Controls Size.
Add a Layout Element and set your Preferred Width to define the size of your TextArea.
The Prefix will scale according to his content, and it will push your text area as it grows.
I am trying to use de Image Button on LIBGDX to create a button based on two images.
Using add to second image, works fine, but have one problem.
The images are of different sizes.
Note: I am testing with the same picture to see the result
Is there a way to correct this? Using some scale to the images?
levelsTexture = new Texture(Gdx.files.internal("level1.png"));
levels = new TextureRegion(levelsTexture).split(TILE_WIDTH, TILE_HEIGHT);
ImageButton levels_image = new ImageButton(new TextureRegionDrawable(new
TextureRegion(levels[0][0])));
levels_image.add(new Image (levels[0][0]));
stage.addActor(levels_image);
levels_image.setScale(2f);
The problem:
ImageButton is an extension of the Table class and typically the ImageButton images are set as the background. Using the "add" method for the second image like you did might work kind of, but it behaves differently than setting the background and it also might not be what you want if you want the the second image to also change when you click the button.
The easiest way to add two images to a single ImageButton would be to simply combine the two images in Photoshop (or equivalent) and use that single image on the ImageButton.
The more advanced (and more flexible) method would be to combine the two images programmatically and use this as the background for your ImageButton. This can be done by creating a custom class which extends BaseDrawable and have it take two Images in the constructor. If you want your images stacked on top of each other, set the minHeight of your custom drawable class to be the combined height of your two images. Then override the draw method and draw your two images on top of each other like this:
#Override
public void draw(Batch batch, float x, float y, float width, float height){
img1.getDrawable().draw(batch, x, y, img1.getWidth(), img1.getHeight());
img2.getDrawable().draw(batch, x, y+img1.getHeight(), img2.getWidth(), img2.getHeight());
}
}
The ImageButton takes a Drawable in its constructor, so you can pass this object right into the button when you create it and both of your Images should appear in the button and they will be treated as one.
I've done something similar to make a background for a table using multiple Images and this method works great.
I wrote my own SpriteButton class in which I implement this method for scaling my textures.
private Dimension getScaledDimension(Dimension imgSize, Dimension boundary) {
int original_width = imgSize.width;
int original_height = imgSize.height;
int bound_width = boundary.width;
int bound_height = boundary.height;
int new_width = original_width;
int new_height = original_height;
// first check if we need to scale width
if (original_width > bound_width) {
//scale width to fit
new_width = bound_width;
//scale height to maintain aspect ratio
new_height = (new_width * original_height) / original_width;
}
// then check if we need to scale even with the new height
if (new_height > bound_height) {
//scale height to fit instead
new_height = bound_height;
//scale width to maintain aspect ratio
new_width = (new_height * original_width) / original_height;
}
return new Dimension(new_width, new_height);
}
Then finally you draw the texture with the output dimension as as size to draw.
sb.begin();
sb.draw(this.texture2, this.x, this.y, dim_size_new.width, dim_size_new.height);
sb.end();
So you could technically draw the second image to the size of the the first image, if the first image size is correct.
Your implementation will look different from mine but you should be able to figure it out form here on out.
I would like to display a quadratic GridPane inside a window. The window can have every possible size and normally width and height are not equal. Anyway I would like to display the GridPane as square centered like this:
respectively
Ideally there is a configurable padding around the square. The pictures are taken from a canvas approach, but I want to switch to standard controls. Can anyone give me some hints how to achieve this?
One way to achieve an squared layout for your grid for any resolution of your window is rescaling it to use the maximum size of the window (after some padding), while keeping the squared size.
Instead of a GridPane, a simple Group is more flexible for moving its children, though it requires manual layouting of them.
This simple snippet is based on the JavaFX implementation of the 2048 game you can find here. It uses styleable rectangles to create a grid, and over them the 'tiles' are added. To find out more about styling, layouting or tile movement, go to the refered link.
private static final int NUM_CELLS = 15;
private static final int CELL_SIZE = 50;
private static final int TILE_SIZE = CELL_SIZE-15;
private final static int MARGIN = 20;
#Override
public void start(Stage primaryStage) {
// create background square grid
Group gridGroup = new Group();
IntStream.range(0,NUM_CELLS).boxed().forEach(i->
IntStream.range(0,NUM_CELLS).boxed().forEach(j->{
Rectangle cell = new Rectangle(i * CELL_SIZE, j * CELL_SIZE, CELL_SIZE, CELL_SIZE);
cell.setFill(Color.WHEAT);
cell.setStroke(Color.GREY);
gridGroup.getChildren().add(cell);
})
);
// Add grid to board
Group board = new Group(gridGroup);
// add random tiles to board
IntStream.range(0,NUM_CELLS).boxed().forEach(i->
IntStream.range(0,NUM_CELLS).boxed().forEach(j->{
if(i==j || NUM_CELLS-i-1==j){
Label label = new Label();
label.setMinSize(TILE_SIZE, TILE_SIZE);
label.setPrefSize(TILE_SIZE, TILE_SIZE);
label.setMaxSize(TILE_SIZE, TILE_SIZE);
label.setStyle("-fx-background-color: red; -fx-background-radius: 50");
label.setLayoutX((i+0.5)*CELL_SIZE-TILE_SIZE/2);
label.setLayoutY((j+0.5)*CELL_SIZE-TILE_SIZE/2);
board.getChildren().add(label);
}
})
);
Bounds gameBounds = board.getLayoutBounds();
StackPane root = new StackPane(board);
// Listener to rescale and center the board
ChangeListener<Number> resize = (ov, v, v1) -> {
double scale = Math.min((root.getWidth() - MARGIN) / gameBounds.getWidth(),
(root.getHeight() - MARGIN) / gameBounds.getHeight());
board.setScaleX(scale);
board.setScaleY(scale);
board.setLayoutX((root.getWidth() - gameBounds.getWidth()) / 2d);
board.setLayoutY((root.getHeight() - gameBounds.getHeight()) / 2d);
};
root.widthProperty().addListener(resize);
root.heightProperty().addListener(resize);
Scene scene = new Scene(root);
// Maximum size of window
Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
double factor = Math.min(visualBounds.getWidth() / (gameBounds.getWidth() + MARGIN),
visualBounds.getHeight() / (gameBounds.getHeight() + MARGIN));
primaryStage.setScene(scene);
primaryStage.setWidth((gameBounds.getWidth() + MARGIN) * factor);
primaryStage.setHeight((gameBounds.getHeight() + MARGIN) * factor);
primaryStage.show();
}
And this is how it will look like after some window resizing:
I'm wondering about the behavior of {Shape}.attr("fill","url({image.path})").
when applying a fill image to a shape:
public class AppMapCanvas extends Raphael {
public AppMapCanvas(int width, int height) {
super(width, height);
this.hCenter = width / 2;
this.vCenter = height / 2;
...
Rect rect = this.new Rect(hCenter, vCenter, 144, 40, 4);
rect.attr("fill", "url('../images/app-module-1-bg.png')"); // <--
...
}
}
The background image seem to teal accross the canvas behind the shape, thus gets weird positioning (an illustration snapshot is enclosed - i marked the original image borders in red).
This seem to resolve itself in the presence of an animation along a path (a mere path.M(0,0) is sufficiant).
How can i position the fill-image properly in the first place?
The proper way to do this from what I can understand would be to use an SVG pattern declaration to specify the portion and position of the image you would want to use. Then you would use that pattern to fill the rectangle element. Unfortunately, the Raphael javascript library doesn't have support for patterns... so there's no direct way to use an image to fill a rectangle.