Three.JS 3D Model has different scale on different screens - javascript

I want that the scale of my 3D Model appears the same on every screen. It should depend on the screen-width, screen-height and the resolutuion. On mobile screens it should appear a bit smaller.
Here is the Code i currently have:
//add moon model
let moonObject = new URL("/assets/Moon.glb", import.meta.url).href;
let moon;
const glftLoader = new GLTFLoader();
glftLoader.load(moonObject, (gltfScene) => {
moon = gltfScene;
let scale = Math.min(window.innerWidth, window.innerHeight) / 6000 - 0.13;
if (window.innerWidth < 600) {
scale = 0.018
}
moon.scene.scale.set(scale, scale, scale);
moon.scene.position.set(1, 1, 0);
moon.scene.rotation.y = 180;
scene.scene.add(gltfScene.scene);
});
This is what it tried it looks good on some screens but on others it appears too small and on some too big:
let scale = Math.min(window.innerWidth, window.innerHeight) / 6000 - 0.13;

Related

Scale entire div except (most) text spans

I'm fitting a game container to the device screen with an optimal scale ratio:
class Main {
// ...
private handleResize() {
let scale = this.getFittingScale();
let container = document.getElementById('gameContainer');
container!.style.transform = `scale(${scale})`;
container!.style.left = `${window.innerWidth / 2 - baseResolution.width / 2}px`;
container!.style.top = `${window.innerHeight / 2 - baseResolution.height / 2}px`;
}
private getFittingScale(): number {
let ratio = baseResolution.width / baseResolution.height;
let windowRatio = window.innerWidth / window.innerHeight;
// choose optimal scale ratio
if (windowRatio > ratio) {
return window.innerHeight / baseResolution.height;
} else return window.innerWidth / baseResolution.width;
}
// ...
}
The issue I've with this is that text fonts scale together inside the <div/>. Is there a way to prevent this text font scaling except for a few specific elements?

Can I reduce the file size of all the image tags in my SVGs as a factor of its width and height using Javascript?

I am working with importing large SVG photos into my image editor. Some of the SVG images contain <image> tags who in turn contain large PNG strings. Some of my files exceed 25MB so that making edits are very slow.
I tried manually scaling / shrink objects it down in the image editor and suddenly the edits were fast, and then I could scale them back up to its original size, seemingly still maintaining the latest edit and without losing any quality (this feels like black magic, could someone explain how this works?)
So this made me think if it is possible to let the system shrink all the images upon loading, but just before showing the new image (most of them are 4500 x 3000 px anyways, so shrinking them by a factor of 4 wouldn't affect its display) and when the user clicks save, we automatically rescale everything to its original size, with the hope of not losing quality, would that even work?
I have tried all kinds of madness to make this work, but nothing bites it seems. Here is my latest attempt. This code is me trying to reduce the pixel size and thus the file size of all the objects.
new Promise(resolve => fabric.loadSVGFromURL(imageUrl, (objects, options) => {
const group = new fabric.Group(objects)
objects.forEach(obj => {
console.log(obj.height)
var scaleX = obj.scaleX;
var scaleY = obj.scaleY;
// Scale to 10% of its original size
var tempScaleX = scaleX * 0.1;
var tempScaleY = scaleY * 0.1;
obj.scaleX = tempScaleX;
obj.scaleY = tempScaleY;
obj.height = obj.height * 0.1
obj.width = obj.width * 0.1
console.log(obj.height)
})
group.height = group.height * 0.1
group.width = group.width * 0.1
resolve(getRatio(group, canvas))
}))
.then(({ ratio, width, height }) => {
fabric.loadSVGFromURL(imageUrl, (objects, options) => {
try {
objects.forEach(obj => {
obj.set({
left: (obj.left * ratio) + ((canvas.width / 2) - ((width * ratio) / 2)),
top: (obj.top * ratio) + ((canvas.height / 2) - ((height * ratio) / 2)),
})
obj.scale(ratio)
canvas.add(obj)
})
canvas.renderAll()
onCanvasModified(canvas)
} catch(err) {
console.log('Could not retrieve that image');
}
})
})

How to fix HTML canvas element and its coordinates across different screen sizes?

I've spent the past two weeks making a game with a few friends using the HTML canvas tag and JavaScript. None of us have any prior experience with a project of this scale, so considerations of browser/screen-size compatibility wasn't on our minds. The game runs and looks fine on our laptops (all have similar screen sizes), but it looked bad when we sent a link to another friend whose screen size differs greatly from what we had in mind.
To discuss the layout of the game in greater detail, it's set up with the canvas element acting as the actual game with a series of divs sitting below the canvas to represent things like a dialogue box or the pause menu (only one of these divs is shown at a time with the others being hidden).
The game is gridbased in that every object, from the wall tiles to enemies, has a size relative to some constant blockWidth (defined below) which itself is relative to the desired amount of squares on-screen, numSquares (also defined below).
Changing the canvas's height and width properties in JavaScript did successfully fix a ratio of the canvas size and ensure that the wall and floor textures loaded in their proper place. The player and other NPCs, however, appear at odd places onscreen, sometimes not showing up onload at all.
I'm not quite sure what to attribute this problem to, but I think it has something to do with canvas' coordinate system not mixing well with the admittedly poorly executed block system we put in place.
//some relevant misc variables
var numSquares = 30;
const blockWidth = 1132 / numSquares * 0.75;
screen.width = 1132;
screen.height = 600;
//some relevant player variables
stats.x = 678;
stats.y = 600;
stats.width = blockWidth * 3 * 0.37;
stats.height = blockWidth * 3;
Again, I suspect that the problem has something to do with the fact that the tiles that render correctly (i.e. wall and floor textures) have their coordinates in terms of blockWidth whereas the tiles that render incorrectly (i.e. the player) have their coordinates as regular numbers.
Is there a way to go about adjusting our game for different monitor sizes other than revamping the entire coordinate system?
try this meta which will solve cross platform screen (laptop, Computer, Tab, Mobile)problem:
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
Your main problem is using hard coded values for your variables. If stats.x = 678;and your screen is 480px wide (for example), the stats will fall out the screen.
Also: screen.width and screen.height is a hardcoded number. Probably what you need is something like: screen.width = window.innerWidth and screen.height = window.innerHeight
What I'm missing from your relevant misc variables: In your code you have this:
var numSquares = 30;
const blockWidth = 1132 / numSquares * 0.75;
Where 1132 is in fact the screen.width. This means that you don't have a grid as you say. This means you have only 1 (one) row of squares. Alternatively you have 30 squares per row, and in this case you have a grid.
Also, it would be nice to see how you draw your grid. I would do it like this:
for (let y = 0; y < ch; y += cw / numSquares) {
for (let x = 0; x < cw; x += cw / numSquares) {
let o = { x: x, y: y, w: blockWidth, h: blockWidth };
drawRect(o);
}
}
Where drawRect is a custom function to draw a rect. Since your blockWidth = screen.width / numSquares * 0.75; I'm assuming you let a gap between rects, but this is assuming.
As I've commented before you can't give your stats hard coded values. You will need to calculate these values in function of your grid cells. For example your stats.x may be the x of the 5'th column of your grid and the stats.y may be the y of the 3-rd row. But this is again assuming and I may be wrong.
Next comes a code example. Please take a look and let me know if this is what you need.
const screen = document.getElementById("canvas");
const ctx = screen.getContext("2d");
let cw = (screen.width = window.innerWidth);
let ch = (screen.height = window.innerHeight);
let stats = {};
let numSquares = 30;
let blockWidth;
generateGrid();
function Init() {
// a function to re-generate the grid and re-draw the stats when screen.width and/or screen.height changed
cw = screen.width = window.innerWidth;
ch = screen.height = window.innerHeight;
generateGrid();
drawStats(5, 3);
}
// recalculate everything on resize
setTimeout(function() {
Init();
addEventListener("resize", Init, false);
}, 15);
// some useful functions
function drawRect(o) {
ctx.beginPath();
ctx.fillStyle = "rgba(0,0,0,.05)";
ctx.fillRect(o.x, o.y, o.w, o.h);
}
function drawStats(x, y) {
stats.x = x * cw / numSquares;
stats.y = y * cw / numSquares;
stats.width = blockWidth * 3 * 0.37;
stats.height = blockWidth * 3;
ctx.fillStyle = "red";
ctx.beginPath();
ctx.fillRect(stats.x, stats.y, stats.width, stats.height);
}
function generateGrid() {
blockWidth = cw / numSquares * 0.75;
for (let y = 0; y < ch; y += cw / numSquares) {
for (let x = 0; x < cw; x += cw / numSquares) {
let o = { x: x, y: y, w: blockWidth, h: blockWidth };
drawRect(o);
}
}
}
*{margin:0;padding:0}
<canvas id="canvas"></canvas>
If this is not what you need, please update your question, and add more explanations and more code.

Graphical representation of speed on canvas

I have the following code:
https://jsfiddle.net/8o3sn9mh/21/
var
canvas = document.getElementById('c'),
ctx = canvas.getContext('2d');
width = window.innerWidth;
height = window.innerHeight;
canvas.width = width;
canvas.height = height;
thrust = height * 0.000001;
maxVelocity = height * 0.00067;
velocity = height * 0.00019;
velocityInterval = setInterval(function(){
velocity+= thrust;
if(velocity > maxVelocity) velocity = maxVelocity;
ctx.fillRect(1, height / 2, width * (velocity / maxVelocity), height / 30);
}, 1);
explanation: I have a canvas which adapts to the user's window. I am trying to represent speed progress upon the canvas with the following starting parameters:
velocity, Max velocity(speed limit) and thrust which accelerates the velocity every millisecond.
the bar starts accelerating from a certain velocity(0.00019) and when you reach full speed the bar's width is exactly the canvas' width. it works fine but as you can see the graph bar starts at a certain x point which is not 0x, because i decided that the starting velocity shall be quite fast.
how can I start the bar at 0x and still be accurate with the speed progress?
here is how it should look like(of course the logic i was talking about is not included here):
https://jsfiddle.net/8o3sn9mh/33/
You can re-map the value between 0 and max.
Javascript has no native build in function for that.
But you can take one from p5 library for example which is:
Math.map = function (value, istart, istop, ostart, ostop) {
return ostart + (ostop - ostart) * ((value - istart) / (istop - istart));
}
Or follow this discussion on SO.
This function re-maps a value to a new range.
So in your case you'd define
var mapStart = width* ((height * 0.00019)/maxVelocity);
and map the (velocity,width) calculation to be between 0 and max. width
var mappedValue = Math.map((width * (velocity / maxVelocity)), mapStart, width, 0, width);
ctx.fillRect(1, height / 2, mappedValue, height / 30);
The fiddle.

JavaScript canvas, scale between two points on chart/graph?

So I've built a small graph application with JavaScript to help me practice using the canvas. I've spent the last 10 hours trying to scale between two points on the X-Axis and can't for the life of me figure it out. I've learned that to scale you need to translate > scale > translate. This works fine when I scale to the far left/right using the following type code.
let x = 0;
let y = this.getCanvasHeight() / 2;
this.getCanvasContext().clearRect(0, 0, this.getCanvas().width, this.getCanvas().height);
this.setCanvas();
ctx.translate(x, y);
ctx.scale(scale, 1);
ctx.translate(-x, -y);
this.resetCanvasLines();
this.renderGraph(this.state.points, scale);
This piece of code simply allows me to zoom into the far left of the graph. So now I'm trying to pick two points on this graph and zoom in on top of them, so that they fit evenly on the screen. The Y-Axis will always be the same.
My thinking was to get the midpoint between the two points and zoom in on that location, which I feel should work but I just can't get it working. My graph width is 3010px and split into 5 segments of 602px. I want to zoom let's say from x1 = 602 and x2 = 1806, which has the midpoint of 1204. Is there a technique to properly calculating the scale amount?
rangeFinder(from, to) {
let points = this.state.points;
if (points.length === 0) {
return;
}
let ctx = this.getCanvasContext();
let canvasWidth = this.getCanvasWidth();
let canvasHeight = this.getCanvasHeight() / 2;
let seconds = this.state.seconds;
let second = canvasWidth / seconds;
let scale = 1;
// My graph starts from zero, goes up to 5 and the values are to represent seconds.
// This gets the pixel value for the fromX value.
let fromX = from * second;
to = isNaN(to) ? 5 : to;
// Get the pixel value for the to location.
let toX = parseInt(to) * second;
let y = canvasHeight / 2;
// get the midpoint between the two points.
let midpoint = fromX + ((toX - fromX) / 2);
// This is where I really go wrong. I'm trying to calculate the scale amount
let zoom = canvasWidth - (toX - fromX);
let zoomPixel = (zoom / 10) / 1000;
let scaleAmount = scale + ((zoom / (canvasWidth / 100)) / 100) + zoomPixel;
ctx.clearRect(0, 0, this.getCanvas().width, this.getCanvas().height);
this.setCanvas();
// translate and scale.
ctx.translate(midpoint, y);
ctx.scale(scaleAmount, 1);
ctx.translate(-midpoint, -y);
this.resetCanvasLines();
this.renderGraph(points);
}
Any help would be great, thanks.
Scale = 5/3 = total width / part width.
After scale, x = 602 should have moved to 602 * 5/3 ~ 1000. Translate the new image by -1000. There is no need to find mid-point.

Categories