how reduce the memory usage of canvas in mobile browser - javascript

I'm trying to implement a feature in our project which will trigger the mobile camera when pressing a capture button from mobile browser, it will append the captured image as canvas to html page, but if we capture more than 2 image, the browser get's restart automatically and showing memory low warning. Please let me know is there any way to optimize the canvas size?.
Note:
I have tested only in mobile chrome.
I'm attaching the sample code below
html code:
<input type="file" id="camera" accept="image/*" capture>
<div id="MediaOutput"></div>
javascript:
var storedMediaList=[];
var mobile_picture = document.querySelector("#camera");
mobile_picture.onchange = function () {
var file = mobile_picture.files[0];
storedMediaList.push({id:(Math.floor(Math.random()*90000)+10000),data:file});
drawImage();
};
function drawImage()
{
$(".rc").remove().empty();
$("#MediaOutput").html("");
$("#MediaOutput").html("<div class='mediaWrapper'></div>");
storedMediaList.forEach(function(row,index,array)
{
$(".mediaWrapper").append("<div style='float:left;' class='ir' id='c"+row.id+"' class='pic'><div media-id="+row.id+" class='close-image' id='close-image' style='width:24px;background:url(images/close.png) no-repeat;height:25px;cursor:pointer;'></div><canvas id='cv"+row.id+"'></canvas> </div>");
var reader = new FileReader();
reader.onload = function (e) {
var dataURL = e.target.result,
c = document.querySelector("#cv"+row.id),
ctx = c.getContext('2d'),
img = new Image();
img.onload = function() {
c.width = img.width;
c.height = img.height;
ctx.drawImage(img, 0, 0);
};
img.src = dataURL;
};
reader.readAsDataURL(row.data);
});
function resizeImage(img, percentage) {
var coeff = percentage/100,
width = $(img).width(),
height = $(img).height();
return {"width": width*coeff, "height": height*coeff}
}
$('.ir canvas').each(function(){
var newDimensions = resizeImage( this, 70);
this.style.width = newDimensions.width + "px";
this.style.height = newDimensions.height + "px";
});
var width = 0;
$('#MediaOutput .mediaWrapper div').each(function() {
width += $(this).outerWidth( true );
});
$('#MediaOutput .mediaWrapper').css('width', width + "px");
}
Thanks in Advance!!

Related

JavaScript: Multiple cropping selection in one image?

PS: Is it not a research kind of question! I have been trying to do this from very long time.
I am trying to make web based an image editor where user can select multiple cropping area and after selection save/download all the image area. like below.
As of now I discovered two libraries
1.Cropper.JS where is only single selection feature is available.
2.Jcrop where only single selection area restrictions.
I am currently using cropper.Js but it seems impossible for me to make multiple selection cropping.
Any help is much appreciated.if any other method/library available in JavaScript, Angular or PHP or reactJS for multiple image area selection and crop and download in one go as in the image below.
As per #Keyhan Answer I am Updating my Jcrop library Code
<div style="padding:0 5%;">
<img id="target" src="https://d3o1694hluedf9.cloudfront.net/market-750.jpg">
</div>
<button id="save">Crop it!</button>
<link rel="stylesheet" href="https://unpkg.com/jcrop/dist/jcrop.css">
<script src="https://unpkg.com/jcrop"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
JavaScript
<script>
setImage();
var jcp;
var jcp;
Jcrop.load('target').then(img => {
//You can enable multiple cropping with this line:
jcp = Jcrop.attach(img, { multi: true });
});
// to fix security issue when trying to convert to Data URI
function setImage() {
document.getElementById('target').setAttribute('crossOrigin', 'anonymous');
document.getElementById('target').src = 'https://d3o1694hluedf9.cloudfront.net/market-750.jpg';
}
var link = document.getElementById('save');
link.onclick = function () {
//we check if at least one crop is available
if (jcp.active) {
var i = 0;
var fullImg = document.getElementById("target");
//we are looping cropped areas
for (area of jcp.crops) {
i++;
//creating temp canvas and drawing cropped area on it
canvas = document.createElement("canvas");
canvas.setAttribute('width', area.pos.w);
canvas.setAttribute('height', area.pos.h);
ctx = canvas.getContext("2d");
ctx.drawImage(fullImg, area.pos.x, area.pos.y, area.pos.w, area.pos.h, 0, 0, area.pos.w, area.pos.h);
//creating temp link for saving/serving new image
temp = document.createElement('a');
temp.setAttribute('download', 'area' + i + '.jpg');
temp.setAttribute('href', canvas.toDataURL("image/jpg").replace("image/jpg", "image/octet-stream"));
temp.click();
}
}
};
</script>
I tried to explain the code with comments:
var jcp;
Jcrop.load('target').then(img => {
//You can enable multiple cropping with this line:
jcp = Jcrop.attach(img,{multi:true});
});
//assuming you have a button with id="save" for exporting cropped areas
var link=document.getElementById('save');
link.onclick = function(){
//we check if at least one crop is available
if(jcp.active){
var i=0;
var fullImg = document.getElementById("target");
//we are looping cropped areas
for(area of jcp.crops){
i++;
//creating temp canvas and drawing cropped area on it
canvas = document.createElement("canvas");
canvas.setAttribute('width',area.pos.w);
canvas.setAttribute('height',area.pos.h);
ctx = canvas.getContext("2d");
ctx.drawImage(fullImg, area.pos.x, area.pos.y, area.pos.w, area.pos.h, 0, 0, area.pos.w, area.pos.h);
//creating temp link for saving/serving new image
temp = document.createElement('a');
temp.setAttribute('download', 'area'+i+'.jpg');
temp.setAttribute('href', canvas.toDataURL("image/jpg").replace("image/jpg", "image/octet-stream"));
temp.click();
}
}
};
EDIT: As you commented it would be nicer if we have local image loader, we can add a file input to our html
<img id="target" />
<br/>
<input type="file" id="imageLoader" name="imageLoader"/><!-- add this for file picker -->
<button id="save">save</button>
and a function to our js to handle it
var jcp;
var save=document.getElementById('save');
var imageLoader = document.getElementById('imageLoader');
var img = document.getElementById("target");
imageLoader.onchange=function handleImage(e){//handling our image picker <input>:
var reader = new FileReader();
reader.onload = function(event){
img.src = event.target.result;
}
reader.readAsDataURL(e.target.files[0]);
}
save.onclick = function(){
if(jcp&&jcp.active){
var i=0;
for(area of jcp.crops){
i++;
canvas = document.createElement("canvas");
canvas.setAttribute('width',area.pos.w);
canvas.setAttribute('height',area.pos.h);
ctx = canvas.getContext("2d");
ctx.drawImage(img, area.pos.x, area.pos.y, area.pos.w, area.pos.h, 0, 0, area.pos.w, area.pos.h);
temp = document.createElement('a');
temp.setAttribute('download', 'area'+i+'.jpg');
temp.setAttribute('href', canvas.toDataURL("image/jpg").replace("image/jpg", "image/octet-stream"));
temp.click();
}
}
};
Jcrop.load('target').then(img => {
jcp = Jcrop.attach(img,{multi:true});
});
Yes, #keyhan was right <input type="file"> is another question, but still, I am giving you an idea of how to implement Kayhan's code above.
<div>
<input type="file" id="image-input" accept="image/*">
<!-- img id name should be "target" as it is also using by Jcrop -->
<img id="target"></img>
</div>
and Now you can put below JavaScript Code just above setImage()
<script>
let imgInput = document.getElementById('image-input');
imgInput.addEventListener('change', function (e) {
if (e.target.files) {
let imageFile = e.target.files[0];
var reader = new FileReader();
reader.onload = function (e) {
var img = document.createElement("img");
img.onload = function (event) {
var MAX_WIDTH = 1600;
var MAX_HEIGHT = 800;
var width = img.width;
var height = img.height;
// Change the resizing logic
if (width > height) {
if (width > MAX_WIDTH) {
height = height * (MAX_WIDTH / width);
width = MAX_WIDTH;
}
} else {
if (height > MAX_HEIGHT) {
width = width * (MAX_HEIGHT / height);
height = MAX_HEIGHT;
}
}
// Dynamically create a canvas element
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
// var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// Actual resizing
ctx.drawImage(img, 0, 0, width, height);
// Show resized image in preview element
var dataurl = canvas.toDataURL(imageFile.type);
document.getElementById("target").src = dataurl;
}
img.src = e.target.result;
}
reader.readAsDataURL(imageFile);
}
});
</script>

Why img.onload() function not working on ipad for exporting my chart data in pdf format?

I am trying to export my chart data consisting of image and data in a PDF. It works fine for me in laptop browser, but while I am trying to do the same in Ipad, the img.onload() function is not firing. I am not sure but I feel like it is due to the size of the image that is rendering in it. Can anybody help me with it? Can we change the pixels of the image that is being rendered?
The code is given below:-
function exportData(exportType) {
if (exportType) {
if (exportType == "pdf") {
alert("clicked pdf")
console.log("pdf export ....")
vm.toggleCanvas = !vm.toggleCanvas;
var canvas = "";
var ctxt = "";
// var img = "";
var sheets = "";
var svgURL = "";
canvas = document.getElementById('canvas');
$log.log($window.screen.width)
//resizing image for PDF
if ($window.screen.width >= 1500) {
canvas.width = $window.screen.width - 150;
} else {
canvas.width = $window.screen.width;
}
console.log("before canvas")
ctxt = canvas.getContext('2d');
sheets = document.styleSheets;
svgURL = generateSVGWithStyling('svg');
// var img = document.createElement('img');
var img = new Image();
console.log("after image")
console.log(img)
alert("imb")
img.src = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgURL);
img.onload = function () {
debugger
console.log("in load")
alert("in load")
ctxt.drawImage(this, 0, 0);
vm.cnvasImg = "";
vm.cnvasImg = canvas.toDataURL();
var content = "";
console.log("on load")
subExportData(vm.cnvasImg, exportType);
}
console.log(img.src)
} else {
vm.cnvasImg = "nothing";
subExportData(vm.cnvasImg, exportType);
}
}
}
Any help would be apprciated.
Can you please try with passing the width, height properties to Image constructor Object.
var img = new Image(Width, Height);

How to crop an image with jquery?

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

Scaling image from input[type=file]

Desired Result: I'm looking to select an image from a file upload form, scale it to a thumbnail and display it.
Problem: The following code does exactly what I want it to, however, I must select the file not once, but twice to see the image preview. (Select image, no display, select same image, I get the scaled display) Everything was working great when I was manually assigning width & height, though now that i'm scaling it - this issue began. I'm in need of a code review! When I comment out the if/if else / else statement and manually assign img.width & img.height to be 75 each, I get the display though it's of course not scaled.
previewFiles = function (file, i) {
preview = function (file) {
// Make sure `file.name` matches our extensions criteria
switch (/\.(jpe?g|png|gif)$/i.test(file.name)) {
case true:
var reader = new FileReader();
reader.onload = function (e) {
var img = new Image();
img.src = reader.result;
var width = img.width,
height = img.height,
max_size = 75;
if (width <= max_size && height <= max_size) {
var ratio = 1;
} else if (width > height) {
var ratio = max_size / width;
} else {
var ratio = max_size / height;
}
img.width = Math.round(width * ratio);
img.height = Math.round(height * ratio);
img.title = file.type;
$('div.box.box-primary').find('span.prev').eq(i).append(img);
};
reader.readAsDataURL(file);
break;
default:
$('div.box.box-primary').find('span.prev').eq(i).append('<a class="btn btn-app" href="#"><span class="vl vl-bell-o"></span> Unsupported File</a>');
break;
}
};
preview(file);
};
I have changing the scaling up a bit - tried https://hacks.mozilla.org/2011/01/how-to-develop-a-html5-image-uploader/ following this article and I have the same issue. Is the problem due to the fact that i'm not using a canvas? I'm pretty new w/jQuery & javascript - Any help here is greatly appreciated!
I made this snippet that fetches an image, thumbnails it & exports it as an img element.
// limit the image to 150x100 maximum size
var maxW=150;
var maxH=100;
var input = document.getElementById('input');
input.addEventListener('change', handleFiles);
function handleFiles(e) {
var img = new Image;
img.onload = function() {
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
var iw=img.width;
var ih=img.height;
var scale=Math.min((maxW/iw),(maxH/ih));
var iwScaled=iw*scale;
var ihScaled=ih*scale;
canvas.width=iwScaled;
canvas.height=ihScaled;
ctx.drawImage(img,0,0,iwScaled,ihScaled);
var thumb=new Image();
thumb.src=canvas.toDataURL();
document.body.appendChild(thumb);
}
img.src = URL.createObjectURL(e.target.files[0]);
}
body{ background-color: ivory; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Select a file to create a thumbnail from</h4>
<input type="file" id="input"/>
<br>

How to make client side image resize function reusable

HTML Template
<html>
<form>
Image to resize: <input type="file" id="getImage"><br><br>
</form>
<img src="." id="image">
<html>
Java Script
<script>
document.getElementById('getImage').onchange = imageResize(60,60);
var imageResize = function (Width, Height) {
//-- GET FILE FROM FORM
var selectedFile = this.files[0];
//-- GET BASE 64
File.prototype.convertToBase64 = function (callback) {
var reader = new FileReader();
reader.onload = function (e) {
callback(e.target.result);
};
reader.readAsDataURL(this);
};
selectedFile.convertToBase64(function (base64) {
//-- MAKE IMAGE
var img = document.createElement('img');
img.src = base64;
//-- PUSH INTO CANVAS
img.onload = function () {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width = Width; // If i change Width & Height to numbers it works!!
canvas.height = Height;
ctx.drawImage(this, 0, 0, Width, Height);
//-- SHOW IMAGE ON PAGE
document.getElementById("image").src = canvas.toDataURL();
};
});
};
</script>
Just above I'm setting the canvas width and height to 60x60 but i cant use the variables I've passed in without getting an error, and I cant figure out why. the error is
Uncaught TypeError: Cannot read property 'files' of undefined
here you should attach the event listener to input element.but instead you attached to img element
and you also want to access the files inside the event listener,so you should pass this reference to the listener
and the way you are attaching the event is not right.
your are doing this document.getElementById('getImage').onchange = imageResize(60,60);
this is wrong as it will execute the imageResize() and assigns the result to on change event.
actually you should attach reference of the function like below
document.getElementById('getImage').onchange = imageResize;
i edited your code a little.
try this snippet.
var imageResize = function(Width, Height, files) {
var selectedFile = files[0];
File.prototype.convertToBase64 = function(callback) {
var reader = new FileReader();
reader.onload = function(e) {
callback(e.target.result);
};
reader.readAsDataURL(this);
};
selectedFile.convertToBase64(function(base64) {
var img = document.createElement('img');
img.src = base64;
img.onload = function() {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width = Width;
canvas.height = Height;
ctx.drawImage(this, 0, 0, Width, Height);
document.getElementById("image").src = canvas.toDataURL();
};
});
};
var file = document.getElementById('getImage');
file.onchange = function() {
imageResize(160, 160, this.files);
};
<form>
Image to resize:
<input type="file" id="getImage">
<br>
<br>
</form>
<img src="." id="image">

Categories