Wrong width and height when reusing FileReader - javascript

I am currently developing a simple interface allowing users to upload a picture, have a preview of it, and then be able to crop it as a square.
I'm using FileReader to get the preview of the image, and jCrop to crop it.
Here is the code I'm using (a part of it):
var jcrop = null;
var reader = new FileReader();
reader.onload = function (e) {
$('#upload-picture-sample').attr('src', e.target.result).load(function(){
var img = this;
var imageWidth = img.width;
var imageHeight = img.height;
console.log(img.width+'-- WIDTH --'+$(this).width());
console.log(img.height+'-- HEIGHT --'+$(this).height());
if(imageWidth != imageHeight){
var selectArray = [];
if(imageWidth > imageHeight){
selectArray = [(imageWidth-imageHeight)/2, 0, (imageWidth-imageHeight)/2 + imageHeight, imageHeight];
}else{
selectArray = [0, (imageHeight - imageWidth)/2, imageWidth, (imageHeight - imageWidth)/2 + imageWidth];
}
if(!jcrop){
$('#upload-picture-sample').Jcrop({
aspectRatio: 1,
keySupport: false,
addClass: 'jcrop-centered',
setSelect: selectArray
}, function(){
jcrop = this;
});
}else{
jcrop.setImage(e.target.result);
jcrop.setSelect(selectArray);
}
}
$(this).unbind('load');
});
}
$('#upload-picture-file').change(function(){
$('#upload-picture-send').removeClass('disabled');
$('#upload-picture-error').hide();
console.log('changed!');
reader.readAsDataURL(this.files[0]);
})
I think the only part that really matters is the part when I calculate the width and height.
To illustrate the problem, I'm uploading:
1/ Picture1 (600x450)
2/ Picture2 (94x125)
3/ Picture1 again (600x450)
The first upload is working fine, the second is working fine as well, but I guess it's more luck than something else, since the height is incorrectly calculated as 0.
The third upload is not working (the size is not correctly set).
It means that the cropzone is not correctly displayed.
Regarding css, I have this:
#upload-picture-sample{
display:block;
margin-left: auto;
margin-right: auto;
margin-bottom: 30px;
max-height:125px;
height:auto;
width:auto;
max-width:300px;
}
Do you have any idea of how I could solve my problem?
UPDATE: After having added setTimeout as recommended by #initialxy
So it's much better, but still, the first image doesn't have the same dimensions than the last (and it should, since it's exactly the same image)
Thanks.

All of your numbers look inconsistent. In first image, width end up being 167 with img.width but 167 with jQuery. One thing to note is that jQuery gets computed style while img.width and img.height are DOM properties that sets desired width and height. (FYI there's also naturalWidth and naturalHeight, which are read-only DOM properties that gets you the original dimensions of img.)
it's more luck than something else
If your code depends on luck, then we got a problem.
It looks like browser emitted load event slightly too early, such that layout engine hasn't finished updating the DOM. load just means data is loaded, doesn't necessarily mean layout engine has to be completed. So try to queue your code block inside your load function later in the task queue. By which, I mean wrap all that in a setTimeout(..., 0); Watch out for this, as it has changed.
eg.
$('#upload-picture-sample').attr('src', e.target.result).load(function(){
var img = this;
setTimeout(function() {
var imageWidth = img.width;
var imageHeight = img.height;
console.log(img.width+'-- WIDTH --'+$(img).width());
console.log(img.height+'-- HEIGHT --'+$(img).height());
// TODO: Crop
...
}, 0);
$(this).unbind('load');
});
EDIT: In response to #Vico's update. Looks like layout engine has settled this time. Let's make some observations on these numbers. The first image's dimensions were originally 600x450, but it ended up being 167x125. This makes sense, because it was resized by max-height: 125px; CSS property. The second image has both of its dimensions less than max-width and max-height so it didn't get resized. The third image has dimensions of 600x450, and it ended up having these dimensions, so at this point max-width and max-height is no longer taking effect. Why is that? Perhaps jcrop screwed around with style and overridden your style. Fire up your Chrome debugger, inspect your img element and use its JavaScript console to play around with it. (To give you a short answer, yes, jcrop does screw around with style and applies inline styles to the element, which overrides your style. But hey, it's more fun to play with debugger.) Also, I'm not sure why your dimensions on the right all ended up with 1440x514. You can find out by screwing around in debugger.

For more information, I suggest all the potential readers to have a look at this issue:
https://github.com/tapmodo/Jcrop/issues/46
The workarounds work fine.

Related

Wait for image added to CSS to load - JQuery

I have an image and I want to draw on it. To do that, I use jQuery to hide the image:
$("img").hide();
And then I create a canvas and put it in the same div with id drawing in the html. I then set the background of the canvas to be the same as the img src for the image I hid. This makes it look like an image but now it is actually a canvas with the image as it's background. I do this by:
$('#drawing > canvas').css('background-image','url('+$(".image img").attr('src')+')');
context.canvas.width = $("img").width();
context.canvas.height = $("img").height();
The issue I am having is that sometimes, the image isn't displayed in the canvas and the canvas is not the size of the image. I think it's probably because of some loading issue. How can I wait for the canvas to have the image displayed in the background for sure? Thank you
Edit: Note that in the DOM, the canvas always has the right src. It just doesn't display it
Edit 2: Here's the JSfiddle. Here, everything seems fine but I have a lot more going on in my code including fetching stuff from the server so it's slower there. Hope this helps you guys to understand the problem: http://jsfiddle.net/wL3ezLke/2/
Thanks again
You need to use:
$(function(){
// Code executed once the DOM is loaded.
});
Official documentation:
https://api.jquery.com/ready/
If I understand correctly your problem is knowing when the image loaded (from what you describe it could be a lot of other problems though).
To test if an image has loaded it's pretty simple.
var $img = $('#hiddenImg');
if ($img[0].complete) doCanvasStuff();
else {
$img.on('load', function(e) {
var $canvas = $('#drawCanvas');
$canvas.css({width: $img.width(), height: $img.height()});
//you can go ahead with the background image, but this is preferable
var ctx = $canvas[0].getContext("2d");
ctx.drawImage(img,0,0);
}
}
This should make sure you canvas loads an image only after it was loaded, or do canvas stuff right away if image was loaded, fiddle
Change
$('#drawing > canvas').css('background-image','url('+$(".image img").attr('src')+')');
context.canvas.width = $("img").width();
context.canvas.height = $("img").height();
to
context.canvas.width = $("img").width();
context.canvas.height = $("img").height();
$('#drawing > canvas').css('background-image','url('+$(".image img").attr('src')+')');
so the height and width are set before the image goes into the background.

Modify CSS classes using Javascript

I was wondering if there is an easy way to change the CSS classes in JavaScript.
I have gone through all other similar questions here and I couldn't find an straight-forward and simple solution.
what I'm trying to do is to set the width and height of a <div> to match an image that I have on my site (upon loading). I already know the picture dimensions and I can set my CSS to that - but I want my script to figure this out on its own.
After hours of r&d (I'm a beginner), this is what I came up with:
var myImg = new Image();
myImg.src = "img/default.jpg";
myImg.onload = function(){
var imgWidth = this.width;
var imgHeight = this.height;
document.getElementById("myBg").setAttribute('style', "height :"+ imgHeight + "px");
document.getElementById("myBg").setAttribute('style', "width :"+ imgWidth + "px");
};
However, this only sets the width of the element with id "myBg". If I reverse the order of the height and width, then it only sets the height to the image's height.
It seems like first it sets the height of the element to the image height but right after it moves to the next statement to set the width, the height value goes back to what it what defined originally in css.
I did further research online and seems like changing the css (inserting new attributes, removing, etc.) using JavaScript is not an easy task. It is done through
document.styleSheets[i].cssRules[i] or document.styleSheets[i].addRule
type of commands, but all the tutorials online and here on stackoverflow were confusing and complicated.
I was wondering if anyone familiar with document.styleSheets can explain this to me simply?
Imagine I have this class in my separate css file:
.container
{
height: 600px;
width: 500px;
}
I want the height and width to change to the dimension of the picture upon loading. How do I do this?
I don't want to define a new "style" element in my html file, I want to change the css file.
I'm not supposed to know the image dimension before it loads to the page.
no jquery please, I want to do this using only standard JavaScript.
Thank you.
The reason only one or the other works is because in your second line of code, you destroy the whole style attribute, and recreate it. Note that setAttribute() overwrites the whole attribute.
A better solution would be to use the element.style property, not the attribute;
var bg = document.getElementById("myBg");
bg.style.width = imgWidth + "px";
bg.style.height = imgHeight + "px";
You can grab all elements with class container and apply it to each of them like this:
var elements = document.querySelectorAll('.container');
for(var i=0; i<elements.length; i++){
elements[i].style.width = imgWidth + "px";
elements[i].style.height = imgHeight + "px";
}
Note querySelectorAll isn't supported by IE7 or lower, if you need those then there are shims for getElementsByClassName() here on SO.
If your rules start incrementing you could extract your css to a new class and switch classes:
CSS:
.container-1{
/* A set of rules */
}
.container-2{
/* A set of rules */
}
JavaScript:
element.className = element.className.replace(/container-1/, 'container-2')
var object = document.createElement('container');
object.style.width= "500px";
object.style.height= "600px";
You can also add values to this if you hold the dimensions in variables
var height = 600;
var width = 500;
You can increment when needed
height += 5;
Here is something you might find useful. It may offer you some insight on how you can solve a problem with many different approaches, seeing as though you are new to js.

iPad canvas rotation

I created a canvas element and I'm using javascript to make it as big as possible in the viewport, while maintaining the aspect ratio.
When you rotate the iPad, the device first rotates the page and only after that launches the resize event.
The problem is, if you come from landscape (about 800px wide) to portrait (about 400px), a part of the body is not being displayed, because the resize didn't happen yet at that moment. After this, the resize event resizes the canvas to the correct size, but then a part of the canvas (& document) is still outside the viewport.
So basically the problem exists because the resize event only launches after the iPad already cut off a part of the body on both sides because the body is too wide.
I could fix this by setting a margin to the canvas to compensate, but it's a dirty solution... does anyone have a better suggestion?
This could also be considered a dirty solution, but in the past I have used a setTimeout to deal with this issue, and it seems to always be reliable.
window.onorientationchange = function() {
canvasResize();
}
function canvasResize() {
window.setTimeout(function () {
//Your code here based on new size
}, 100);
}
Or if you are looking for a slightly less dirty solution, you should be able to just keep track of the viewport width yourself, and then wait until that changes to do your updates. Add to your script somewhere:
var viewportWidth = window.innerWidth;
And change canvasResize:
function canvasResize() {
if(window.innerWidth != viewportWidth) {//Dimensions have changed for sure
viewportWidth = window.innerWidth; //Update viewportWidth for future use
//Your code here based on new size
}
else {
//Delay and try again
window.setTimeout(function () {
canvasResize();
}, 100);
}
}
Both of are pretty quick and dirty, and I'd be interested if there's a more elegant solution, but I haven't found one yet.

jQuery image resizing - over a variable and directly gives different results

I've got couple of lines of JavaScript using jQuery to resize images to thumbnails.
var thumb = $(this);
thumb.load(function() {
var ratio = thumb.height() / config.maxHeight;
var newWidth = Math.ceil(thumb.width() / ratio);
thumb.height(config.maxHeight);
// this line matters
thumb.width(newWidth);
});
Fotunately this works fine. But if I replace the last line with:
thumb.width(Math.ceil(thumb.width() / ratio));
It changes width of images that hasn't got explicitly defined dimensions badly (too narrow). To me, it seems like totally equivalent ways - via a variable or directly - but obviously they're not.
I tried casting the ceil() result to a Number or Integer and it behaved opposite way - images with undefined dimension were OK but the rest was too wide (width of original image).
Although I the first solution works I guess there's something fundamental I'm missing. So I want to avoid it in the future.
Thank you!
I would guess that the <img> element you are manipulating does not have declared height or width attributes. If that is the case, then the issue is how browsers intelligently resize images given only one constraint.
If you have an image that is 1000px wide, and 1000px tall, and you write an IMG tag like this:
<img src="big_image.gif" width="10" />
Modern browsers will render the huge image resized down to 10 by 10px.
So, on the line where you alter the height:
thumb.height(config.maxHeight);
the browser goes ahead an also alters the width. If you subsequently read the width (i.e. thumb.width(Math.ceil(thumb.width() / ratio))), you are going to be reading the new width, not the width it had before being given a new height.
var someImg = new Image();
someImg.src = <theURLofDesiredImage>
alert(someImg.width + " : " + someImg.height);
This is not Jquery but its vanilla JS and its a true way to determine "an unloaded" (not cached!) image. Add a query string to the URL url + "?asdasdasdadads" will allow you to circumvent the browser caching the image. This will result in a longer "image load time" but you will ALWAYS and more importantly, PREDICTABLY, resolve the dynamically loaded image.

Programmatically Clip/Cut image using Javascript

Are there any documents/tutorials on how to clip or cut a large image so that the user only sees a small portion of this image? Let's say the source image is 10 frames of animation, stacked end-on-end so that it's really wide. What could I do with Javascript to only display 1 arbitrary frame of animation at a time?
I've looked into this "CSS Spriting" technique but I don't think I can use that here. The source image is produced dynamically from the server; I won't know the total length, or the size of each frame, until it comes back from the server. I'm hoping that I can do something like:
var image = getElementByID('some-id');
image.src = pathToReallyLongImage;
// Any way to do this?!
image.width = cellWidth;
image.offset = cellWidth * imageNumber;
This can be done by enclosing your image in a "viewport" div. Set a width and height on the div (according to your needs), then set position: relative and overflow: hidden on it. Absolutely position your image inside of it and change the position to change which portions are displayed.
To display a 30x40 section of an image starting at (10,20):
<style type="text/css">
div.viewport {
overflow: hidden;
position: relative;
}
img.clipped {
display: block;
position: absolute;
}
</style>
<script type="text/javascript">
function setViewport(img, x, y, width, height) {
img.style.left = "-" + x + "px";
img.style.top = "-" + y + "px";
if (width !== undefined) {
img.parentNode.style.width = width + "px";
img.parentNode.style.height = height + "px";
}
}
setViewport(document.getElementsByTagName("img")[0], 10, 20, 30, 40);
</script>
<div class="viewport">
<img class="clipped" src="/images/clipped.png" alt="Clipped image"/>
</div>
The common CSS properties are associated with classes so that you can have multiple viewports / clipped images on your page. The setViewport(…) function can be called at any time to change what part of the image is displayed.
In answer to :
Alas, JavaScript simply isn't capable of extracting the properties of the image you'd require to do something like this. However, there may be salvation in the form of the HTML element combined with a bit of server-side scripting.
...
< ? (open php)
$large_image = 'path/to/large_image';
$full_w = imagesx($large_image);
$full_h = imagesy($large_image);
(close php) ? >
This can be done in Javascript, just google a bit :
var newimage = new Image();
newimage.src = document.getElementById('background').src;
var height = newimage.height;
var width = newimage.width;
This generates a new image from an existing one and captures this way in java script the original height and width properties of the original image (not the one id'ed as background.
In answer to :
The width/height properties of the document's image object are read only. If you could change them, however, you would only squish the frames, not cut the frames up like you desire. The kind of image manipulation you want can not be done with client-side javascript. I suggest cutting the images up on the server, or overlay a div on the image to hide the parts you do not wish to display.
...
var newimage = new Image();
newimage.src = document.getElementById('background').src;
var height = newimage.height;
var width = newimage.width;
newimage.style.height = '200px';
newimage.style.width = '200px';
newimage.height = '200px';
newimage.width = '200px';
and if wanted :
newimage.setAttribute('height','200px');
The doubled newimage.style.height and newimage.height is needed in certain circumstances in order to make sure that a IE will understand in time that the image is resized (you are going to render the thing immediately after, and the internal IE processing is too slow for that.)
Thanks for the above script I altered and implemented on http://morethanvoice.net/m1/reader13.php (right click menu... mouseover zoom lent) correct even in IE , but as you will notice the on mousemove image processing is too fast for the old styled IE, renders the position but only once the image. In any case any good idea is welcome.
Thanks to all for your attention, hope that the above codes can help someone...
Claudio Klemp
http://morethanvoice.net/m1/reader13.php
CSS also defines a style for clipping. See the clip property in the CSS specs.
The width/height properties of the document's image object are read only. If you could change them, however, you would only squish the frames, not cut the frames up like you desire. The kind of image manipulation you want can not be done with client-side javascript. I suggest cutting the images up on the server, or overlay a div on the image to hide the parts you do not wish to display.
What spriting does is essentially position a absolutely-positioned DIV inside another DIV that has overflow:hidden. You can do the same, all you need to do is resize the outer DIV depending on the size of each frame of the larger image. You can do that in code easily.
You can just set the inner DIV's style:
left: (your x-position = 0 or a negative integer * frame width)px
Most JavaScript Frameworks make this quite easy.
Alas, JavaScript simply isn't capable of extracting the properties of the image you'd require to do something like this. However, there may be salvation in the form of the HTML <canvas> element combined with a bit of server-side scripting.
PHP code to go about extracting the width and height of the really large image:
<?php
$large_image = 'path/to/large_image';
$full_w = imagesx($large_image);
$full_h = imagesy($large_image);
?>
From here, you'd then load the image into a <canvas> element, an example of which is documented here. Now, my theory was that you may be able to extract pixel data from a <canvas> element; assuming that you can, you would simply make sure to have some form of definite divider between the frames of the large image and then search for it within the canvas. Let's say you found the divider 110 pixels from the left of the image; you would then know that each "frame" was 110 pixels wide, and you've already got the full width stored in a PHP variable, so deciphering how much image you're working with would be a breeze.
The only speculative aspect to this method is whether or not JavaScript is capable of extracting color data from a specified location within an image loaded into a <canvas> element; if this is possible, then what you're trying to accomplish is entirely feasible.
I suppose you want to take a thumbnail for your image. You can use ImageThumbnail.js that created from prototype library in this way:
<script type="text/javascript" src="prototype.js"></script>
<script type="text/javascript" src="ImageThumbnail.js"></script>
<input type="file" id="photo">
<img src="empty.gif" id="thumbnail" width="80" height="0">
<script type="text/javascript">
<!--
new Image.Thumbnail('thumbnail', 'photo');
//-->
</script>
for more information
try use haxcv library haxcv js by simple functions
go to https://docs.haxcv.org/Methods/cutImage to read more about his library
var Pixels = _("img").cutImage (x , y , width , height );
_("img").src (Pixels.src);
// return cut image
but try to include library first

Categories

Resources