I'm trying to figure out if my code is at fault or the current HTML5 File API implementation.
The code below works. The bug appears when repeating the process after already loading the image once.
The second time a file is selected the image blob loads, then there's a flicker and the image disappears. Subsequent selections after that usually work fine (sometimes there's erratic behavior if the file is big). Repeating the process for the same file selection generally works (as a fix).
Any help would be greatly appreciated.
JavaScript libraries used
JQuery 1.7.1
JQuery Tools 1.2.6
JCrop 0.9.9
Steps - Summary
The user selects a file via the traditional <input type="file" />
dialog.
An onchange handler executes for the input and checks if a file was selected, if so, then verifies that the MIME type is image/jpeg or image/png and that the selected file size is smaller than 250KB. If this validation fails it resets the selection.
At this point the file (image) to be uploaded is valid. Next it
checks if the browser supports the File API CreateObjectURL via if
(typeof window.URL == 'undefined') return;
(if it doesn't, skips
the next steps)
It loads the image blob into the current picture preview (one used to display the current image) and also into a dynamically generated image element which is added into a jquery tools' overlay with jcrop cropping functionality.
The user then crops the image via jcrop and closes the overlay, seeing the cropped preview of the image to be uploaded (only if the browser supports CreateObjectURL and the user cropped the image)
The Code
The HTML
<div style="height: 100px; overflow: hidden; width: 100px; margin-bottom: 5px;">
<img id="PhotoPreview" alt="Photo" src="/Content/no-photo.png" />
</div>
<input type="file" id="Photo" name="Photo" onchange="photoChanged(this.files)" />
<input type="hidden" id="PhotoCrop" name="PhotoCrop" value="" />
The JavaScript window.URL = window.URL || window.webkitURL;
function photoChanged(files) {
if (!files.length) return;
var file = files[0];
if (file.type != 'image/jpeg' && file.type != 'image/png') {
alert('The photo must be in PNG or JPEG format');
$("#Photo").val('');
return;
}
var fileSizeLimit = 250;
var fileSize = file.size / 1024;
if (fileSize > fileSizeLimit) {
var fileSizeString = fileSize > 1024 ? (fileSize / 1024).toFixed(2) + "MB" : (fileSize).toFixed(2) + "KB";
alert("The photo file size is too large, maximum size is " + fileSizeLimit
+ "KB. This photos' file size is: " + fileSizeString);
$("#Photo").val('');
return;
}
if (typeof window.URL == 'undefined') return;
var preview = document.getElementById('PhotoPreview');
$(preview).removeClass('profile-photo');
preview.src = window.URL.createObjectURL(file);
preview.onload = function () {
var img = new Image();
$("#PhotoOverlay div").empty().append(img);
window.URL.revokeObjectURL(this.src);
img.src = window.URL.createObjectURL(file);
img.onload = function () {
window.URL.revokeObjectURL(this.src);
$(this).Jcrop({
onSelect: onImageCropSelect,
aspectRatio:
310 / 240,
minSize: [100, 100],
setSelect: [0, 0, 100, 100]
});
$("#PhotoOverlay")
.css({ width: this.width + 'px', : 'auto'})
.overlay()
.load();
};
};
}
function onImageCropSelect(e) {
$("#PhotoCrop").val(e.x + ',' + e.y + ',' + .x2 + ',' + e.y2);
var rx = 100 / e.w;
var ry = 100 / e.h;
var height = $("#PhotoOverlay div img").height();
var width = $("#PhotoOverlay div img").width();
jQuery('#PhotoPreview').css({
width: Math.round(rx * width) + 'px',
height: Math.round(ry * height) + 'px',
marginLeft: '-' + Math.round(rx * e.x) + 'px',
marginTop: '-' + Math.round(ry * e.y) + 'px'
});
}
$(function () {
$("#PhotoOverlay").overlay({
mask: {
color: '#ebecff',
loadSpeed: 200,
opacity: 0.9
}
});
});
Feel free to use the code (my contribution for any help received here).
Update
I have come across another question on stackoverflow regarding a similar issue (image loading then disappearing) in regards to the use of JCrop. I'm currently betting on JCrop being the culprit.
I've also read that when img.onload executes the image isn't always 'ready', hence the strange behavior and additional checks to .actualWidth/.actualHeight with a setTimeout might solve the issue. I'll investigate it.
Update
I have a working proof of concept using a FileReader and readAsDataUrl instead of using the CreateObjectURL and using a CANVAS to draw a preview instead of a margin based overflow:hidden solution. Needs some refinement then I'll post the code here.
often erratic behavior occurs when you set img.src
before declare img.onload
function imageLoadHandler() { ... }
var img = new Image();
img.onload = function() { imageLoadHandler() }
img.src = window.URL.createObjectURL(file);
if(img.complete) { imageLoadHandler() } //fix for browsers that don't trigger .load() event with image in cache
hope this helps
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With