I'm a beginner to javascript. I'm trying to resize images on client side before uploading them to my server for storage I have found a way to do it using HTML5 Canvas: http://hacks.mozilla.org/2011/01/how-to-develop-a-html5-image-uploader/
My problem: It kept on uploading blank image. I think the reason for the blank image is that canvas is not done drawing when I grabbed the image from canvas. When I set a 3 second windows.timeout before grabbing image from canvas everything works fine. Is there a more reliable method than a timeout? Or is there any other method to do client-side image resizing?
The reason I'm doing client-side image resizing is because I'm dealing with people who uploads unnecessary big files and I think it is most efficient to do a client side image resizing.
Thank you for any hints/answers!
//resize image
Resizeimage.resize(file);
//***if i wrap this part with timeout it works
var canvas = document.getElementById('canvas');
var data = canvas.toDataURL(file.type);
// convert base64/URLEncoded data component to raw binary data held in a string
var bytestring;
if (data.split(',')[0].indexOf('base64') >= 0)
bytestring = atob(data.split(',')[1]);
else
bytestring = escape(data.split(',')[1]);
//get imagetype
var mimeString = data.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to a typed array
var ia = new Uint8Array(bytestring.length);
for (var i = 0; i < bytestring.length; i++) {
ia[i] = bytestring.charCodeAt(i);
}
// create a blob from array
var blob = new Blob([ia], {
type: mimeString
});
//upload files
$scope.upload = $upload.upload({
url: 'articles/imageUpload',
method: 'POST',
file: blob
//update progress bar
}).progress(function(event) {
$scope.uploadInProgress = true;
$scope.uploadProgress = Math.floor(event.loaded / event.total * 100);
//file upload success
}).success(function(data, status, headers, config) {
$scope.uploadInProgress = false;
$scope.uploadedImage = JSON.parse(data);
$scope.isDisabled = false;
//file upload fail
}).error(function(err) {
$scope.uploadInProgress = false;
console.log('Error uploading file: ' + err.message || err);
});
//***
angular.module('articles').factory('Resizeimage', [
function() {
var imgSelectorStr = null;
// Resizeimage service logic
function render(src) {
var image = new Image();
image.onload = function() {
var canvas = document.getElementById('canvas');
//check the greater out of width and height
if (image.height > image.width) {
image.width *= 945 / image.height;
image.height = 945;
} else {
image.height *= 945 / image.width;
image.width = 945;
}
//draw (&resize) image to canvas
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0, image.width, image.height);
};
image.src = src;
}
function loadImage(src) {
// Create fileReader and run the result through the render
var reader = new FileReader();
reader.onload = function(e) {
render(e.target.result);
};
reader.readAsDataURL(src);
}
// Public API
return {
resize: function(src) {
loadImage(src);
return true;
}
};
}
]);
Your problem is quite a simple one.
JavaScript will run one statement after another (and you need this because you want all statements executed when grabbing the image) as long as you dont attach to any events.
If you attach your code to an event (image.onload
for example) this part of your code will wait for the event and the rest of your code will continue asynchronous.
So you are right - the image isn't drawn when you try to grab this.
Its bad style to solve it by a timeout (most inefficient/insecure) better solve this by wrapping your image-grabbing code in a function and run this function after your image re-size is done. It's like a callback you use to synchronize the asynchronous event.
Example
/* setTimeout will behave like an event */
setTimeout(
function(){
$('console').append("<div>SetTimeout</div>");
SynchCodeAfterTimeout();
}, 1000);
AsynchCodeAfterTimeout();
function AsynchCodeAfterTimeout(){
$('console').append("<div>AsynchCodeAfterTimeout</div>");
}
function SynchCodeAfterTimeout(){
$('console').append("<div>SynchCodeAfterTimeout</div>");
}
console{
font-family: Consolas, monaco, monospace;
}
<script type="text/javascript" src="https://code.jquery.com/jquery-latest.min.js"></script>
<console></console>
Example with your code
render();
// Resizeimage service logic
function render() {
var image = new Image();
image.onload = function() {
var canvas = document.getElementById('canvas');
//check the greater out of width and height
if (image.height > image.width) {
image.width *= 945 / image.height;
image.height = 945;
} else {
image.height *= 945 / image.width;
image.width = 945;
}
//draw (&resize) image to canvas
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0, image.width, image.height);
/*timeout not needed its just there so the browser renders the canvas contents so you can see them when the alert pops up*/
setTimeout(function() {
alert('yey all the drawing is done (this is synchronous)');
});
};
image.src = 'https://loremflickr.com/320/240';
}
<canvas id="canvas"></canvas>
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