I need to modify existing code with new requirement:
Image dimensions has to be validated before upload from browser.
If height or width of image is less than 500 px it has to be increased to 500px.
Here is the code that currently used in our app to upload images.
var fileInputElem = document.getElementById('P3_FILE');
var fileIndex = 0;
var deferredObject = $.Deferred();
var s$=apex.widget.waitPopup;
// builds a js array from long string
function clob2Array(clob, size, array) {
loopCount = Math.floor(clob.length / size) + 1;
for (var i = 0; i < loopCount; i++) {
array.push(clob.slice(size * i, size * (i + 1)));
}
return array;
}
// converts binaryArray to base64 string
function binaryArray2base64(int8Array) {
var data = "";
var bytes = new Uint8Array(int8Array);
var length = bytes.byteLength;
for (var i = 0; i < length; i++) {
data += String.fromCharCode(bytes[i]);
}
return btoa(data);
}
// a recursive function that calls itself to upload multiple files synchronously
function uploadFile(pFileIndex) {
var fileCount = 0;
var file = fileInputElem.files[pFileIndex];
var reader = new FileReader();
var uploadTarget = apex.item("P3_UPLOAD_TARGET").getValue();
reader.onload = (function(pFile) {
return function(e) {
if (pFile) {
var base64 = binaryArray2base64(e.target.result);
var f01Array = [];
f01Array = clob2Array(base64, 30000, f01Array);
apex.server.process(
'UPLOAD_FILE',
{
x01: file.name,
x02: file.type,
x03: uploadTarget,
x04: apex.item("P3_FILE_TYPE").getValue(),
x05: parent.apex.item('P2_SCREEN_TYPE').getValue(),
f01: f01Array
},
{
dataType: 'json',
success: function(data) {
if (data.j_retn_status == 'SUCCESS') {
if (fileIndex === 0) {
apex.item('P3_PRIMARY_ID').setValue(data.j_primary_id);
}
fileIndex++;
if (fileIndex < fileInputElem.files.length) {
// start uploading the next file
var d = fileIndex - 1;
uploadFile(fileIndex);
} else {
// all files have been uploaded at this point
apex.item('P3_FILES_COUNT').setValue(fileIndex);
fileInputElem.value = '';
deferredObject.resolve('done');
}
} else {
//alert('Oops! Something went terribly wrong. Please try again or contact your application administrator.' + data.j_retn_status);
$('#FILEDISP'+pFileIndex).html($('#FILEDISP'+pFileIndex).text() + '<p class="fa-window-close" aria-hidden="true"></p>' ) ;
}
}
}
);
}
}
})(file);
$('#FILEDISP'+pFileIndex).html($('#FILEDISP'+pFileIndex).text() + '<p class="fa fa-check" aria-hidden="true"></p>' ) ;
reader.readAsArrayBuffer(file);
return deferredObject.promise();
}
How can we modify it to validate image dimensions and increase image width or height before upload please?
Thank you!
You could probably do something like below, although I'd look into using a library for doing something like this in case there are gotchas for certain situations.
const MIN_WIDTH = 500;
const MIN_HEIGHT = 500;
const imageInputEl = document.getElementById('example-image');
imageInputEl.addEventListener("change", handleFiles, false);
function handleFiles() {
const fileList = this.files;
const file = fileList[0];
if ( /\.(jpe?g|png)$/i.test(file.name) ) {
var reader = new FileReader();
reader.addEventListener("load", function () {
var image = new Image();
image.src = this.result;
document.getElementById('input-image').src = this.result;
image.onload = () => {
const width = image.naturalWidth;
const height = image.naturalHeight;
let canvasWidth = MIN_WIDTH;
let canvasHeight = MIN_HEIGHT;
if (width >= MIN_WIDTH) canvasWidth = width;
if (height >= MIN_HEIGHT) canvasHeight = height;
const canvas = document.createElement("canvas");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
const ctx = canvas.getContext("2d");
ctx.drawImage(document.getElementById('input-image'), 0, 0, canvasWidth, canvasHeight);
const resizedImage = canvas.toDataURL("image/png");
document.getElementById('resized-image').src = resizedImage;
};
});
reader.readAsDataURL(file);
} else {
throw new Error('Not a valid image file.');
}
}
.container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
<div class="container">
<input type="file"id="example-image" accept="image/png, image/jpeg">
<div>
Input Image:
</div>
<img id="input-image">
<div>
Resized Image:
</div>
<img id="resized-image">
</div>
Can I crop an image without a canvas? And how to crop an image without resizing the image? Like this real picture to be 133px x 133px
Large Image to be Small Image
Large Image To be Small Image
And thiis my code https://jsfiddle.net/w26La1u6/2/
$(document).on('change', '#files', function(event) {
var files = event.target.files; // FileList object
var output = document.getElementById("list");
for (var i = 0, f; f = files[i]; i++) {
if (f.type.match('image.*')) {
if (this.files[0].size < 12097152) {
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
var span = document.createElement('span');
span.innerHTML = ['<img class="thumbnail" src="', e.target.result,
'" title="', escape(theFile.name), '"/>'
].join('');
output.insertBefore(span, null);
};
})(f);
$('#clear, #list').show();
reader.readAsDataURL(f);
} else {
alert("Image Size is too big. Minimum size is 2MB.");
$(this).val("");
}
}
}
})
But if there is no other way to crop without canvas, tell me how to crop with canvas
EDIT
<input id="uploadImage" type="file" accept="image/*" capture="camera" />
<img id="imgDisplay" src="http://placehold.it/300x200" alt="Not a kitten" />
var Resample = (function (canvas) {
// (C) WebReflection Mit Style License
function Resample(img, width, height, onresample) {
var load = typeof img == "string",
i = load || img;
if (load) {
i = new Image;
// with propers callbacks
i.onload = onload;
i.onerror = onerror;
}
i._onresample = onresample;
i._width = width;
i._height = height;
load ? (i.src = img) : onload.call(img);
}
function onerror() {
throw ("not found: " + this.src);
}
function onload() {
var
img = this,
width = img._width,
height = img._height,
onresample = img._onresample
;
// Altered section - crop prior to resizing
var imgRatio = img.width / img.height;
var desiredRatio = width / height;
var cropWidth, cropHeight;
if (desiredRatio < imgRatio) {
cropHeight = img.height;
cropWidth = img.height * desiredRatio;
} else {
cropWidth = img.width;
cropHeight = img.width / desiredRatio;
}
delete img._onresample;
delete img._width;
delete img._height;
canvas.width = width;
canvas.height = height;
context.drawImage(
// original image
img,
// starting x point
0,
// starting y point
0,
// crop width
cropWidth,
// crop height
cropHeight,
// destination x point
0,
// destination y point
0,
// destination width
width,
// destination height
height
);
onresample(canvas.toDataURL("image/png"));
}
var context = canvas.getContext("2d"),
round = Math.round;
return Resample;
}(
this.document.createElement("canvas"))
);
var newCropWidth = 133;
var newCropHeight = 133;
function loadImage(data) {
document.querySelector('#imgDisplay').src = data;
}
function handleFileSelect(evt) {
if (evt.target.files.length === 1) {
var picFile = evt.target.files[0];
if (picFile.type.match('image.*')) {
var fileTracker = new FileReader;
fileTracker.onload = function() {
Resample(
this.result,
newCropWidth,
newCropHeight,
loadImage
);
}
fileTracker.readAsDataURL(picFile);
}
}
}
document.querySelector('#uploadImage').addEventListener('change', handleFileSelect, false);
There are ways to do it using jQuery UI but you can just utilize a plugin that someone else already made such as Cropper
The code below allows to click on a canvas with a background image and draw a point. I would like to give the change to the user to change the default image, so I have added an input file element. So go to the snippet and follow this steps:
Step 1: click on the first default image "Ziiweb". The coordinate is
shown in the textarea.
Step 2: load/preview a new image using the Browse button, and click on it. As you can see the coordinates of the first image are kept, why?
Hint: after previewing a new image, and click over it, the function mousedown() is called twice.
$.fn.canvasAreaDraw = function (options) {
this.each(function (index, element) {
init.apply(element, [index, element, options]);
});
}
var init = function (index, input, options) {
var points, activePoint, settings;
var $reset, $canvas, ctx, image;
var draw, mousedown, stopdrag, move, resize, reset, resot, rightclick, record, previewImage;
mousedown = function (e) {
console.log('mousedown');
console.log(points);
console.log(points.length);
var x, y, dis, lineDis, insertAt = points.length;
e.preventDefault();
if (!e.offsetX) {
e.offsetX = (e.pageX - $(e.target).offset().left);
e.offsetY = (e.pageY - $(e.target).offset().top);
}
x = e.offsetX;
y = e.offsetY;
points.splice(insertAt, 0, Math.round(x), Math.round(y));
activePoint = insertAt;
$(this).on('mousemove', move);
record();
return false;
};
record = function () {
$(input).val(points.join(','));
};
settings = $.extend({
imageUrl: $(this).attr('data-image-url')
}, options);
points = []; //I expected this to reset the points list!!!, but no....
if (!$(this).is('canvas')) {
$canvas = $('<canvas>');
} else {
$canvas = $(this);
}
ctx = $canvas[0].getContext('2d');
image = new Image();
$(this).prev().prev().val('');
resize = function () {
$canvas.attr('height', image.height).attr('width', image.width);
draw();
};
if (settings.imageUrl) {
image.src = settings.imageUrl;
} else {
image.src = options;
}
$canvas.css({
background: 'url(' + image.src + ')'
});
$(input).after('<br>', $canvas);
$(document).ready(function () {
$canvas.on('mousedown', mousedown);
});
};
//LOAD IMAGE
previewImage = function () {
var aux = $(this).prev();
if (this.files && this.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
aux.canvasAreaDraw(e.target.result);
}
reader.readAsDataURL(this.files[0]);
}
}
$(document).ready(function () {
$('.canvas-area').canvasAreaDraw();
$('.imgInp').on('change', previewImage);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea class="canvas-area" data-image-url="http://www.ziiweb.com/images/logo.png"></textarea>
<input type='file' class="imgInp" id="jander" />
Because each time you attach a new image, the document.readyevent fires and attaches a new mousedown handler to the canvas.
$.fn.canvasAreaDraw = function (options) {
this.each(function (index, element) {
init.apply(element, [index, element, options]);
});
}
var init = function (index, input, options) {
var points, activePoint, settings;
var $reset, $canvas, ctx, image;
var draw, mousedown, stopdrag, move, resize, reset, resot, rightclick, record, previewImage;
mousedown = function (e) {
console.log('mousedown');
console.log(points);
console.log(points.length);
var x, y, dis, lineDis, insertAt = points.length;
e.preventDefault();
if (!e.offsetX) {
e.offsetX = (e.pageX - $(e.target).offset().left);
e.offsetY = (e.pageY - $(e.target).offset().top);
}
x = e.offsetX;
y = e.offsetY;
points.splice(insertAt, 0, Math.round(x), Math.round(y));
activePoint = insertAt;
$(this).on('mousemove', move);
record();
return false;
};
record = function () {
$(input).val(points.join(','));
};
settings = $.extend({
imageUrl: $(this).attr('data-image-url')
}, options);
points = []; //I expected this to reset the points list!!!, but no....
if (!$(this).is('canvas')) {
$canvas = $('<canvas>');
} else {
$canvas = $(this);
}
ctx = $canvas[0].getContext('2d');
image = new Image();
$(this).prev().prev().val('');
resize = function () {
$canvas.attr('height', image.height).attr('width', image.width);
draw();
};
if (settings.imageUrl) {
image.src = settings.imageUrl;
} else {
image.src = options;
}
$canvas.css({
background: 'url(' + image.src + ')'
});
$(input).after('<br>', $canvas);
$(document).ready(function () {
alert('attaching a new mousedown event!');
$canvas.on('mousedown', mousedown);
});
};
//LOAD IMAGE
previewImage = function () {
var aux = $(this).prev();
if (this.files && this.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
aux.canvasAreaDraw(e.target.result);
}
reader.readAsDataURL(this.files[0]);
}
}
$(document).ready(function () {
$('.canvas-area').canvasAreaDraw();
$('.imgInp').on('change', previewImage);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea class="canvas-area" data-image-url="http://www.ziiweb.com/images/logo.png"></textarea>
<input type='file' class="imgInp" id="jander" />
The solution being to attach the event somewhere else, or to use a flag.
$.fn.canvasAreaDraw = function (options) {
this.each(function (index, element) {
init.apply(element, [index, element, options]);
});
}
var init = function (index, input, options) {
var points, activePoint, settings;
var $reset, $canvas, ctx, image;
var draw, mousedown, stopdrag, move, resize, reset, resot, rightclick, record, previewImage;
mousedown = function (e) {
console.log('mousedown');
console.log(points);
console.log(points.length);
var x, y, dis, lineDis, insertAt = points.length;
e.preventDefault();
if (!e.offsetX) {
e.offsetX = (e.pageX - $(e.target).offset().left);
e.offsetY = (e.pageY - $(e.target).offset().top);
}
x = e.offsetX;
y = e.offsetY;
points.splice(insertAt, 0, Math.round(x), Math.round(y));
activePoint = insertAt;
$(this).on('mousemove', move);
record();
return false;
};
record = function () {
$(input).val(points.join(','));
};
settings = $.extend({
imageUrl: $(this).attr('data-image-url')
}, options);
points = []; //I expected this to reset the points list!!!, but no....
if (!$(this).is('canvas')) {
$canvas = $('<canvas>');
} else {
$canvas = $(this);
}
ctx = $canvas[0].getContext('2d');
image = new Image();
$(this).prev().prev().val('');
resize = function () {
$canvas.attr('height', image.height).attr('width', image.width);
draw();
};
if (settings.imageUrl) {
image.src = settings.imageUrl;
} else {
image.src = options;
}
$canvas.css({
background: 'url(' + image.src + ')'
});
$(input).after('<br>', $canvas);
$(document).ready(function () {
if(first){
alert('attaching a new mousedown event!');
first= false;
$canvas.on('mousedown', mousedown);
}
});
};
var first = true;
//LOAD IMAGE
previewImage = function () {
var aux = $(this).prev();
if (this.files && this.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
aux.canvasAreaDraw(e.target.result);
}
reader.readAsDataURL(this.files[0]);
}
}
$(document).ready(function () {
$('.canvas-area').canvasAreaDraw();
$('.imgInp').on('change', previewImage);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea class="canvas-area" data-image-url="http://www.ziiweb.com/images/logo.png"></textarea>
<input type='file' class="imgInp" id="jander" />
I have a Javascript that allows to resize multiple images and uploads them. So far it works until you choose more than about 5 files. From about 5 to 25 selected files Chrome browser (or others) just gets really slow, if you choose even more the browser crashes.
It seems to take a lot of memory.
Do you have any suggestions what could avoid the freezing of the browser/computer or the crash?
Thank you very much for your help!
// Once files have been selected
document.querySelector('form input[type=file]').addEventListener('change', function(event){
$( "#load" ).html( "<img src=\"http://www.maxk.at/image_uploader/ajax-loader.gif\"> Upload läuft" );
// Read files
var files = event.target.files;
// Iterate through files
var j=1;
var k=files.length;
for (var i = 0; i < files.length; i++) {
// Ensure it's an image
if (files[i].type.match(/image.*/)) {
// Load image
var reader = new FileReader();
reader.onload = function (readerEvent) {
var image = new Image();
image.onload = function (imageEvent) {
// Add elemnt to page
var imageElement = document.createElement('div');
imageElement.classList.add('uploading');
imageElement.innerHTML = '<span class="progress"><span></span></span>';
var progressElement = imageElement.querySelector('span.progress span');
progressElement.style.width = 0;
document.querySelector('form div.photos').appendChild(imageElement);
// Resize image
var canvas = document.createElement('canvas'),
max_size = 1200,
width = image.width,
height = image.height;
if (width > height) {
if (width > max_size) {
height *= max_size / width;
width = max_size;
}
} else {
if (height > max_size) {
width *= max_size / height;
height = max_size;
}
}
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
// Upload image
var xhr = new XMLHttpRequest();
if (xhr.upload) {
// Update progress
xhr.upload.addEventListener('progress', function(event) {
var percent = parseInt(event.loaded / event.total * 100);
progressElement.style.width = percent+'%';
}, false);
// File uploaded / failed
xhr.onreadystatechange = function(event) {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
imageElement.classList.remove('uploading');
imageElement.classList.add('uploaded');
imageElement.style.backgroundImage = 'url('+xhr.responseText+')';
console.log('Image uploaded: '+j+k+'Abstand'+xhr.responseText);
j++;
if (j==k){$("#load").html("Upload beendet");}
if (k==1){$("#load").html("Upload beendet");}
} else {
imageElement.parentNode.removeChild(imageElement);
}
}
}
// Start upload
xhr.open('post', 'process.php', true);
xhr.send(canvas.toDataURL('image/jpeg'));
}
}
image.src = readerEvent.target.result;
}
reader.readAsDataURL(files[i]);
}
}
// Clear files
event.target.value = '';
});
I have been tracking down the bug in my project, works on iOS and desktop browsers, fails silently on android.
I think it might have to do with my FileReader?
any insights are appreciated!
function handleImage(e) {
var reader = new FileReader();
reader.onload = function(event) {
var img = new Image();
img.onload = function() {
alert("alert 2");
images.length = 0;
images.push(img);
imageLoaded = true;
$('#changePhoto').removeClass('hidden');
img.xPos = w / 2;
img.yPos = h / 2;
img.initWidth = img.width;
img.initHeight = img.height;
img.currentWidth = scale(img.initHeight, viewableArea, img.initWidth);
img.currentHeight = viewableArea;
if (img.currentWidth < viewableArea) {
img.currentWidth = viewableArea;
img.currentHeight = scale(img.initWidth, viewableArea, img.initHeight);
}
img.initAngle = 0;
img.angle = 0;
};
img.src = event.target.result;
};
reader.readAsDataURL(e.target.files[0]);
}
It turns out requestAnimationFrame was my issue, I added this shim, and that solved it:
https://gist.github.com/paulirish/1579671