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
Related
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.
Lately I am fiddling a bit in React and I have encountered the following problem, which is not necessarily React related but more Javascript / CSS in general related.
I have created a bar which contains a certain amount of cells. These cells all have the same width and height properties but will adjust its width/height properties to the viewport.
When I click one of these cells a div get's appended to the container of the cells, this div is an overlay with a different color (so you know which cell has been clicked).
The problem is that the bar and it's containing cells have their width attribute set in percentage, this has been done so I can calculate the width needed based on the total cell count when adding more cells to the containing div.
The overlay div however, which is added when a cell is being clicked, has it's width defined in px. This is because the left and top position of the overlay div is being determined with the pageY and pageX coordinates and the height and width of the cell element is being fetched with offsetHeight and offsetWidth.
The result of the above is that the actual width of the cell is a decimal value (it is percentage) but the calculated width is a rounded px value, this results in the following:
The normal Cell width:
The Overlay div width:
as you can see this is a minor difference in width, to show it properly I had to zoom in quite a bit. In the second picture you see that how further in left position the overlay div is being added, the bigger the difference (which is logical).
The code:
This is the normal Cell which is being rendered
_onRenderCell = (item, index) => {
this.myCell = React.createRef();
return (
<div
className="ms-ListGridExample-tile"
data-is-focusable={false}
style={{
width: 100 / this._columnCount + '%',
height: this._rowHeight * 1.5,
float: 'left'
}}
>
... rest of the rendering omitted ...
You can see here that the width is determined based on the column count and returned as a percentage: width: 100 / this._columnCount + '%'
The onMouseDown event to add the overlay div:
_onMouseDown (e){
const containerNode = this.rsvnRef.current;
const cellNode = this.myCell.current;
// Determine current position
let posY = e.pageY - containerNode.getBoundingClientRect().top;
let posX = e.pageX - containerNode.getBoundingClientRect().left;
// Determine width and height of cell
let height = cellNode.offsetHeight;
let width = cellNode.offsetWidth;
// Determine top and left positions
let top = posY - (posY % height);
let left = posX - (posX % width);
var reservationClasses = document.getElementsByClassName('reservation');
var collisions = Array.prototype.filter.call(reservationClasses, function(rsv) {
return rsv.offsetTop == top && rsv.offsetLeft == left;
});
if (collisions.length == 0){
// Creating the overlay div
const newRsvn = document.createElement("div");
newRsvn.className = "reservation reservation-creating";
newRsvn.style.top = top + 'px';
newRsvn.style.left = left + 'px';
newRsvn.style.height = height + 'px';
newRsvn.style.width = width + 'px';
// Apending overlay div to container
containerNode.append(newRsvn);
}
}
My question is (as you probably expected); how can I make the overlaying div have the exact same width as the cell div? Can you return the offsetWidth with decimal places, or can I in some way calculate the offsetWidth in percentage?
After some more research, and thanks to this post: Converting width from percentage to pixels I managed to get it done.
However, I don't know if it is the best / right solution so please do give feedback if you think there is a better way!
I decided to just set the width of the cell in pixels instead of a percentage. The rendering of a cell is being done dynamically anyway (each time the screen size changes) so calculating the right amount of pixels in the cell rendering and rounding that to a whole number works fine since now both the cell and overlaying div are using the same width.
The code:
_onRenderCell = (item, index) => {
this.myCell = React.createRef();
const containerNode = this.rsvnRef.current;
let percents = parseInt(100 / this._columnCount);
let parentWidth = parseInt(containerNode.offsetWidth);
let pixels = parseInt(parentWidth*(percents/100));
return (
<div
className="ms-ListGridExample-tile"
data-is-focusable={false}
style={{
// width: 100 / this._columnCount + '%',
width: pixels + 'px',
height: this._rowHeight * 1.5,
float: 'left'
}}
>
... rest of the rendering omitted ...
When i run my application it gives me several errors:
the first one is:
SyntaxError: class is a reserved identifier in the class thumbnail
code:
const MAXHEIGHT = 170;
const MAXWIDTH = 320;
import {Subcategory} from './subcategory'
//import {Category} from './category'
import {bootstrap} from 'angular2/platform/browser'
class Thumbnail {
width: number;
height: number;
constructor(element: HTMLImageElement) {
this.height = element.height;
this.width = element.width;
}
resize(oImage: HTMLImageElement) {
var maxWidth = 100; // Max width for the image
var maxHeight = 100; // Max height for the image
var ratio = 0; // Used for aspect ratio
var width = oImage.width; // Current image width
var height = oImage.height; // Current image height
// Check if the current width is larger than the max
if (oImage.width > MAXWIDTH) {
ratio = MAXWIDTH / width; // get ratio for scaling image
oImage.width=MAXWIDTH; // Set new width
oImage.height=height * ratio; // Scale height based on ratio
height = height * ratio; // Reset height to match scaled image
width = width * ratio; // Reset width to match scaled image
}
// Check if current height is larger than max
if (height > MAXHEIGHT) {
ratio = MAXHEIGHT / height; // get ratio for scaling image
oImage.height= MAXHEIGHT; // Set new height
oImage.width= width * ratio; // Scale width based on ratio
width = width * ratio; // Reset width to match scaled image
height = height * ratio; // Reset height to match scaled image
}
// Check if current height is smaller than max
if (height < MAXHEIGHT) {
ratio = MAXHEIGHT / height; // get ratio for scaling image
oImage.height = MAXHEIGHT; // Set new height
oImage.width = width * ratio; // Scale width based on ratio
width = width * ratio; // Reset width to match scaled image
height = height * ratio; // Reset height to match scaled image
}
// Check if the current width is smaller than the max
if (oImage.width < MAXWIDTH) {
ratio = MAXWIDTH / width; // get ratio for scaling image
oImage.width = MAXWIDTH; // Set new width
oImage.height = height * ratio; // Scale height based on ratio
height = height * ratio; // Reset height to match scaled image
width = width * ratio; // Reset width to match scaled image
}
document.body.appendChild(oImage);
console.log(oImage.height);
console.log(oImage.width);
}
};
window.onload = () => {
var oImage = new Image();
var oImage2 = new Image();
// var category = new Category(1, "string");
var subcategory = new Subcategory("teste",1,2);
oImage.src = 'AngularJS.png';
oImage.setAttribute('class','img-responsive');
oImage2.src = 'ts.jpg';
var thumbnail = new Thumbnail(oImage);
var thumbnail2 = new Thumbnail(oImage2);
thumbnail.resize(oImage);
thumbnail2.resize(oImage2);
console.log(oImage.height);
console.log(oImage.width);
};
The second error is:
GET http://localhost:51580/app
301 Moved Permanently
The configuration of angular is:
<script>
System.config({
packages: {
TypeScriptHTMLApp1: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app')
.then(null, console.error.bind(console));
</script>
I already tried to create a folder named app and moved the file for that folder but the error persists. I need help with this, I lost several hours and can't resolve anything. I attach one image with the file structure and another one with the firebug errors. Thanks in advance.
as suggested by #Eric in comment
Classes aren't supported in Firefox version < 45 according to this article
. instead try same use case in the chrome.
posted as answer may helpful to someone.
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.
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