How to limit random image location to inside of another image - javascript

so I have an HTML button and a JavaScript randomizer that randomizes a location for an image to 'spawn' when you click the said button. I also have a grass field image (which has a randomizer to choose from 12 different fields upon reloading the page and display one of them). Currently, it can 'spawn' an image anywhere on the page, however, I want it to only be able to spawn it onto the field.
my javascript:
function show_image(src, width, height, alt) {
var img = document.createElement("img");
img.src = src;
img.width = width;
img.height = height;
img.alt = alt;
// set the position
img.style.position = 'absolute';
img.style.top = document.body.clientHeight * Math.random() + 'px';
img.style.left = document.body.clientWidth * Math.random() + 'px';
document.body.appendChild(img);
}
document.getElementById('foo').addEventListener('click', () =>
show_image("cow-sprites/cowtest.png", 56, 54, 'foo')
);
my html:
<button id="foo">
get a cow
</button>
my css:
.cow-spawning {
z-index: 1;
}
.field-image {
position: fixed;
left: 28.25%;
top: 16%;
z-index: -1;
-webkit-transform: scale(0.85);
-moz-transform: scale(0.85);
-ms-transform: scale(0.85);
-o-transform: scale(0.85);
transform: scale(0.85);
}
what i've tried:
replacing
img.style.top = document.body.clientHeight * Math.random() + 'px';
img.style.left = document.body.clientWidth * Math.random() + 'px';
with
img.style.top = meadows.naturalHeight * Math.random() + meadows_box.top + 'px';
img.style.top = meadows.naturalHeight * Math.random() + meadows_box.left + 'px';
and creating a variable to hold the field image (meadows)

You have the coordinates for a container (the field) and another box (the image). Each has a left (x) and top (y) position, as well as a width (x) and height (y).
You want to randomize the coordinates for the image so that all 4 corners of that image are inside the outer containers bounds. It can be difficult to think about positioning all 4 corners at once, but we can simplify the problem.
Let's only think of the left,top (x,y) coordinate. If the image stays within the container, then the lowest the top can be is the same as the bottom position of the container plus the height of the image. Similarly, the furthest right the image can go is one image width from the right side of the container.
For example, let's assume the outer container left,top is 0,0, and its width,height are 100,100. The inner image width,height is 10,10. The inner image can be at 0,0 inside the container extending to 10,10, which is the leftmost and topmost position. If the image is in the furthest bottom, right corner, then the bottom right coordinate is at 100,100. Since the image is 10,10 then the top,left corner is at 90,90.
In the end, this just tells us to calculate the random position using a reduced range. Instead of being able to put the top or left coordinate anywhere from 0-100, we need to restrict them to 0-90, allowing for the image height and width padding it on the right and bottom.
img.style.top = (containerHeight - imageHeight) * Math.random() + 'px';
img.style.left = (containerWidth - imageWidth) * Math.random() + 'px';
Where containerHeight and containerWidth are the outer container height at width, assuming that the containerTop and containerLeft are both 0. If you have to account for the offset position of the outer container, then we can account for that as well.
img.style.top = ((containerHeight - containerTop - imageHeight) * Math.random() + containerTop) + 'px';
img.style.left = ((containerWidth - containerLeft - imageWidth) * Math.random() + containerLeft) + 'px';
For example:
containerLeft: 100
containerTop: 200
containerWidth: 100
containerHeight: 200
imageWidth: 10
imageHeight: 20
Randomized left/X positions can range from 100 to 190 and the new top/Y position ranges can be from 200 to 280.

Related

Center Object based on rotation

I need to center an object in a canvas based on its rotation. I can't figure out the maths.
What information do I have?
x, and y coordinates of the top left corner (see red circle of image)
width and height of the object
the rotation value in degrees
What have i tried so far?
// center horizontally
if (curretElement === null) return;
curretElement.x((canvas.width() / 2) - ((curretElement.width() * curretElement.scaleX()) / 2));
canvas.draw();
// center vertically
curretElement.y((canvas.height() / 2) - ((curretElement.height() * curretElement.scaleY()) / 2));
canvas.draw();
This centers the image when it's not rotated.
currentElement is the selected object.
canvas is the room where the object should be centered in.
You can calculate the coordinates this way:
imagine that you have your object centered on the canvas
calculate the coordinates of the top left corner relative to the center of the canvas
rotate the object around the center of the canvas and calculate where the top left corner ends up relative to the center of the canvas
translate the relative coordinates of the top left corner back to absolute coordinates
Here is a function that does the calculation:
function calculateXY(canvasWidth, canvasHeight, width, height, angle) {
//calculate where the top left corner of the object would be relative to center of the canvas
//if the object had no rotation and was centered
const x = -width / 2;
const y = -height / 2;
//rotate relative x and y coordinates by angle degrees
const sinA = Math.sin(angle * Math.PI / 180);
const cosA = Math.cos(angle * Math.PI / 180);
const xRotated = x * cosA - y * sinA;
const yRotated = x * sinA + y * cosA;
//translate relative coordinates back to absolute
const canvasCenterX = canvasWidth / 2;
const canvasCenterY = canvasHeight / 2;
const finalX = xRotated + canvasCenterX;
const finalY = yRotated + canvasCenterY;
return { x: finalX, y: finalY };
}
UPDATE :first try is really bad so i update code. which actually work and keep your left corner in center of container, just fill angle input
i want to comment and ask more but i can't and this thing(bounty) is tempting
Not heavy depend on java script. may be it what you need second attempt.
Corner is container and content in before. Rotate but it remains there. does it help? comment it.
<!DOCTYPE html>
<html lang="en">
<body>
<style>
body{
margin:0;
}
#container{
display :flex;
position: absolute;
width:100%;
height:100%;
}
#ram{
display:flex;
background-color:black;
position:absolute;
margin:auto;
top:50%;
right:50%;
}
#ram::before{
content: "";
position:absolute;
height:40px;
width:400px;
background-color: #000;
}
</style>
<input type="number" id="a" onchange = "forf()">
<div id = "container">
<div id = "ram">
</div>
</div>
<script>
function forf(){
var a = document.getElementById("a").value;
document.getElementById("ram").style.transform = "rotate(" + a + "deg)";
}
</script>
</body>
</html>

Max-height and max-width for divs with dynamic backgrounds?

I have a code responsible for cropping image and saving its cropped areas in a list of divs. Each of these divs represents each of the cropped images. But the problem is - I dont want them to be so huge, I want them to have fixed height and width, e.g. max 100x100px.
Codesandbox with working image cropping: https://codesandbox.io/s/fast-frost-0b5tt
Code revelant to cropping logic:
const width = pos.w + "px";
const height = pos.h + "px";
const marginLeft = - pos.x + "px";
const marginTop = - pos.y + "px";
return (
<div
key={i}
style={{
width: width,
height: height,
backgroundSize: "400px 300px",
backgroundPosition: `top ${marginTop} left ${marginLeft}`,
backgroundImage: "url('https://boygeniusreport.files.wordpress.com/2016/11/puppy-dog.jpg?quality=98&strip=all&w=400')"
}}
/>
);
You will see that image cropping works great, however newly created images have dynamic height and width.
Question: How to make these newly created divs to have fixed width and height, but without destroying it? Thanks!
Edit: New link added (the old one was missing styles)
The goal is to make the children (cropped images) to have static height and width, but keep the background image in correct position. But seems like im too stopid to do it by myself
The following change within your render function will create a 100px square centered on the selection's center point, or as close as possible keeping within the image limits (I'm not sure how to reference the original image's width & height from here).
...
const previews = crops.map(({ pos }, i) => {
const width = 100 + "px";
const height = 100 + "px";
let margx = (pos.w / 2) - 50 + pos.x;
let margy = (pos.h / 2) - 50 + pos.y;
if(margx < 0) margx = 0;
if(margy < 0) margy = 0;
// this needs origional image width & height (400,300) to get max values
const maxx = 400-100;
const maxy = 300-100;
if(margx > maxx) margx = maxx;
if(margy > maxy) margy = maxy;
const marginLeft = - margx + "px";
const marginTop = - margy + "px";
return (
<div
...
If you fix both height and width, the previews will look distorted. So I would recommend
only fixing the height.
const fixedHeight = 100;
const zoom = fixedHeight / pos.h;
const backgroundWidth = 400 * zoom;
const backgroundHeight = 300 * zoom;
const width = pos.w * zoom;
const height = fixedHeight;
const marginLeft = -pos.x * zoom;
const marginTop = -pos.y * zoom;
See results in this codesandbox demo.
I can propose something in between solutions proposed by #MunimMunna and #ArleighHix improving both solutions.
See result
// setup base image size
const imageBaseWidth = 400;
const imageBaseHeight = 300;
// choose thumbnail size and aspect ratio
const thumbHeight = 100;
const thumbWidth = 200;
// we check which axis needs to be filled to border
const zoomX = thumbWidth / pos.w;
const zoomY = thumbHeight / pos.h;
// you can improve it further by defining max zoom in level so that thumbnails don't show ugly pixels
// just use additional zoom = Math.min(zoom, maxZoom) and some more logic for handling min max margin offset so it wont go outside image bounds
const zoom = Math.max(zoomX, zoomY);
// scaling base image to best fit available space
const backgroundWidth = imageBaseWidth * zoom;
const backgroundHeight = imageBaseHeight * zoom;
// calculate offset to top left corner of biggest rect in selected region that keeps target aspect ratio
const marginLeft = thumbWidth / 2 - (pos.w / 2 + pos.x) * zoom;
const marginTop = thumbHeight / 2 - (pos.h / 2 + pos.y) * zoom;
return (
<div
className="preview"
key={i}
style={{
width: thumbWidth + "px",
height: thumbHeight + "px",
backgroundSize: `${backgroundWidth}px ${backgroundHeight}px`,
backgroundPosition: `top ${marginTop}px left ${marginLeft}px`,
backgroundImage: "url('https://boygeniusreport.files.wordpress.com/2016/11/puppy-dog.jpg?quality=98&strip=all&w=400')"
}}
/>
);
It chooses bigest posible region inside selection that keeps aspect ratio of targeted thumbnails and zooms in on it if required. You can setup target thumbnails size with fixedHeight, fixedWidth
I'd try a rewrite without the jcrop library, to have better access to all the props, size, positioning etc.
https://stackblitz.com/edit/react-7ubxaa

Scale images with canvas without blurring it

I have a class named Engine.Renderer. It just creates a new canvas and give me the possibility to easily update and render the active canvas' scene. When a new canvas is created, I apply those settings to its context:
this.context.imageSmoothingEnabled = false;
this.context.mozImageSmoothingEnabled = false;
this.context.webkitImageSmoothingEnabled = false;
In CSS, I've added those lines:
main canvas {
position: absolute;
top: 0;
image-rendering: optimizeSpeed;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-optimize-contrast;
image-rendering: optimize-contrast;
-ms-interpolation-mode: nearest-neighbor
}
I have also write a function that adjusts the canvas to the window:
[...]
resize: function () {
var width = window.innerWidth;
var height = width / 16 * 9;
if ( height > window.innerHeight ) {
height = window.innerHeight;
width = height * 16 / 9;
}
if ( width > window.innerWidth ) {
width = window.innerWidth;
height = width / 16 * 9;
}
width = Number( width ).toFixed();
height = Number( height ).toFixed();
this.canvas.style.width = width + "px";
this.canvas.style.height = height + "px";
this.container.style.width = width + "px";
this.container.style.height = height + "px";
this.container.style.left = ( ( window.innerWidth - width ) / 2 ) + "px";
// the new scale
this.scale = ( width / this.canvas.width ).toFixed( 2 );
}
[...]
Now, I have a class named Character. This class is able to create and render a new character on the given canvas. The render part looks like this:
context.drawImage(
this.outfit,
this.sprite * this.dimension.width,
this.direction * this.dimension.height,
this.dimension.width,
this.dimension.height,
this.position.x,
this.position.y,
// set the character sizes to normal size * scale
this.dimension.width * this.renderer.scale,
this.dimension.height * this.renderer.scale
);
I have two problems with it:
the game performance is even worse than before (~9 FPS when rendering a single character on ~1400x800px canvas);
character' image is not so much blurred as before, but I still can see a little blur;
How can I solve those problems?
Try using integer values for positions and sizes:
context.drawImage(
this.outfit,
this.sprite * this.dimension.width,
this.direction * this.dimension.height,
this.dimension.width,
this.dimension.height,
this.position.x|0, // make these integer
this.position.y|0,
// set the character sizes to normal size * scale
(this.dimension.width * this.renderer.scale)|0,
(this.dimension.height * this.renderer.scale)|0
);
Also, setting canvas size with CSS/style will affect interpolation. From my own tests the CSS settings for interpolation does not seem to affect canvas content any longer.
It's better, if you need a fixed small size scaled up, to set the canvas size properly and instead use scale transform (or scaled values) to draw the content:
this.canvas.width = width;
this.canvas.height = height;
Update: Based on the comments -
When changing the size of the canvas element the state is reset as well meaning the image smoothing settings need to be reapplied.
When image smoothing is disabled the browser will use nearest-neighbor which means best result is obtained when scaling 2^n (2x, 4x, 8x or 0.5x, 0.25x etc.) or otherwise "clunkyness" may show.
A modified fiddle here.

How to keep track of a position no matter what screen size is?

I'm trying to make a widget that selects an area in the page using JavaScript but the issue is that when it saves the X/Y and Width/Height, it's actually only relevant to that screen size, so if we try to draw that selection on another user's computer, it'll go off the correct position.
On what to rely and how to keep track of an x and y position no matter what the user's screen size is?
obj.offsetLeft and obj.offsetTop will always be relative to the top/left corner which is 0,0.
Center x = window.innerWidth / 2;
Center y = window.innerHeight / 2;
Object width = elementNode.offsetWidth;
Object height = elementNode.offsetHeight;
Position the object in the middle of the screen:
el = document.getElementById('my_div');
el.style.position = 'absolute';
el.style.left = Math.floor((window.innerWidth / 2) - (el.offsetWidth / 2)) + 'px';
el.style.top = Math.floor((window.innerHeight / 2) - (el.offsetHeight / 2)) + 'px';
Working demo: http://jsfiddle.net/4aenr/

How do I effectively calculate zoom scale?

I have a draggeable image contained within a box. You can zoom in and zoom out on the image in the box which will make the image larger or smaller but the box size remains the same. The box's height and width will vary as the browser is resized. The top and left values for the image will change as it is dragged around.
I'm trying to keep whatever the point the box was centered on in the image, in the center. Kind of like how zoom on Google Maps works or the zoom on Mac OS X zooms.
What I'm doing right now is calculating the center of the box (x = w/2, y = h/2) and then using the top and left values for the image to calculate the position of the image in the center of the box. (x -= left, y -= top).
Then I zoom the image by growing or shrinking it and I use the scale change to adjust the coordinates (x = (x * (old_width/new_width), y = (y * (old_height/new_height)).
I then reposition the image so that its center is what it was before zoom by grabbing the coordinates it is currently centered on (that changed with the resize) and adding the difference between the old center values and the new values to the top and left values (new_left = post_zoom_left + (old_center_x - new_center_x), new_top = post_zoom_top + (old_center_y - new_center_y).
This works ok for zoom in, but zoom out seems to be somewhat off.
Any suggestions?
My code is below:
app.Puzzle_Viewer.prototype.set_view_dimensions = function () {
var width, height, new_width, new_height, coordinates, x_scale,
y_scale;
coordinates = this.get_center_position();
width = +this.container.width();
height = +this.container.height();
//code to figure out new width and height
//snip ...
x_scale = width/new_width;
y_scale = height/new_height;
coordinates.x = Math.round(coordinates.x * x_scale);
coordinates.y = Math.round(coordinates.y * y_scale);
//resize image to new_width & new_height
this.center_on_position(coordinates);
};
app.Puzzle_Viewer.prototype.get_center_position = function () {
var top, left, bottom, right, x, y, container;
right = +this.node.width();
bottom = +this.node.height();
x = Math.round(right/2);
y = Math.round(bottom/2);
container = this.container.get(0);
left = container.style.left;
top = container.style.top;
left = left ? parseInt(left, 10) : 0;
top = top ? parseInt(top, 10) : 0;
x -= left;
y -= top;
return {x: x, y: y, left: left, top: top};
};
app.Puzzle_Viewer.prototype.center_on_position = function (coordinates) {
var current_center, x, y, container;
current_center = this.get_center_position();
x = current_center.left + coordinates.x - current_center.x;
y = current_center.top + coordinates.y - current_center.y;
container = this.container.get(0);
container.style.left = x + "px";
container.style.top = y + "px";
};
[Working demo]
Data
Resize by: R
Canvas size: Cw, Ch
Resized image size: Iw, Ih
Resized image position: Ix, Iy
Click position on canvas: Pcx, Pcy
Click position on original image: Pox, Poy
Click position on resized image: Prx, Pry
Method
Click event position on canvas -> position on image: Pox = Pcx - Ix, Poy = Pcy - Iy
Position on image -> Pos on resized image: Prx = Pox * R, Pry = Poy * R
top = (Ch / 2) - Pry, left = (Cw / 2) - Prx
ctx.drawImage(img, left, top, img.width, img.height)
Implementation
// resize image
I.w *= R;
I.h *= R;
// canvas pos -> image pos
Po.x = Pc.x - I.left;
Po.y = Pc.y - I.top;
// old img pos -> resized img pos
Pr.x = Po.x * R;
Pr.y = Po.y * R;
// center the point
I.left = (C.w / 2) - Pr.x;
I.top = (C.h / 2) - Pr.y;
// draw image
ctx.drawImage(img, I.left, I.top, I.w, I.h);
This is a general formula that works for zooming in or out, and can handle any point as the new center. To make it specific to your problem:
Pcx = Cw / 2, Pcy = Ch / 2 (alway use the center)
R < 1 for zooming out, and R > 1 for zooming in

Categories