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
Related
I'm using AngularJS and TypeScript. I am using the cytoscape library with the extension cytoscape-edgehandles and the wrapper ngCytoscape for AngularJS. I am trying to add an image to the hover handle of the nodes, however the implementation of the drawImage method in cytoscape-edgehandles 2.7.1 does not wait for the image to load before drawing, therefore a range error occurs.
I have tried to give the cytoscape graph the options after the image is loaded, however the cytoscape graph expects the options to immediately be available for use. So somehow I need to load the image before passing it to the graph.
In the constructor of the controller:
this.$scope.ehOptions = this.GetEhOptions();
The get EhOptions method:
protected GetEhOptions(): GraphModels.EhOptions {
var options = new GraphModels.EhOptions;
var img = new Image();
img.width = 25;
img.height = 25
img.src = "../../Content/icons/ic_circle_add_24px.svg";
options.handleImage = img;
img.addEventListener('load', e => {
return options;
});
}
This results in the options never being loaded by the graph. If I do this in stead, I get all the options, except the image:
var options = new GraphModels.EhOptions;
var img = new Image();
img.width = 25;
img.height = 25
img.src = "../../Content/icons/ic_circle_add_24px.svg";
img.addEventListener('load', e => {
options.handleImage = img;
});
return options;
The code of cytoscape-edgehandles concerning the handleIcon:
if(options().handleIcon){
var icon = options().handleIcon;
var width = icon.width*cy.zoom(), height = icon.height*cy.zoom();
ctx.drawImage(icon, hx-(width/2), hy-(height/2), width, height);
}
If it is changed to this, then it works:
if (true) {
var img = new Image();
var width = 25 * cy.zoom();
var height = 25 * cy.zoom();
img.width = width;
img.height = height
img.src = "../../Content/icons/ic_circle_add_24px.svg";
img.addEventListener('load', e => {
ctx.drawImage(img, hx - (width/2), hy - (height/2), width, height)
})
}
However, I cannot change the library code.
Hope you have any ideas. Thanks!
Create an AngularJS promise:
protected GetEhOptions(): GraphModels.EhOptions {
var deferred = $q.defer();
var options = new GraphModels.EhOptions;
var img = new Image();
img.width = 25;
img.height = 25
img.src = "../../Content/icons/ic_circle_add_24px.svg";
options.handleImage = img;
img.addEventListener('load', e => {
̶r̶e̶t̶u̶r̶n̶ ̶o̶p̶t̶i̶o̶n̶s̶;̶
deferred.resolve(options);
});
img.addEventListener('error', e => {
deferred.reject(e);
});
return deferred.promise;
}
Then extract the options from the returned promise:
this.GetEhOptions().then(options => {
this.$scope.ehOptions = options;
});
For more information, see
AngularJS $q Service API Reference - The Deferred API
I don't have any idea about cytoscape, but try this,
options:any = new GraphModels.EhOptions;
let globleThis = this;
var img = new Image();
img.width = 25;
img.height = 25
img.src = "../../Content/icons/ic_circle_add_24px.svg";
this.options.handleImage = img;
img.addEventListener('load', e => {
this.GetEhOptions(globleThis.options);
});
protected GetEhOptions(options): GraphModels.EhOptions {
return options;
}
or
var options = new GraphModels.EhOptions;
var img = new Image();
img.width = 25;
img.height = 25
img.src = "../../Content/icons/ic_circle_add_24px.svg";
img.addEventListener('load', e => {
return options.handleImage = img;
});
No experiance with a specific library but I worked with similar ones. In those cases I wrapped it into my own directive and then controlled the creation from there directly. In that case you have access to the $element and you can create it on the fly.
I tried to load images into several canvas elements, but only the last image was loaded from the list of files. I need different images in different canvas elements, each in its own. Thanks for help.
HTML
<input type='file' id='imgfile' multiple />
The canvas element will be created by jQuery.
JavaScript
function loadImage(picture) {
var canvas = document.querySelectorAll('canvas');
var input, fr, file, img;
input = document.getElementById('imgfile');
$.each(canvas, function(i, v) {
file = input.files[i];
fr = new FileReader();
fr.onload = function createImage() {
img = new Image();
img.onload = function imageLoaded() {
var ctx = canvas[i].getContext("2d");
ctx.drawImage(img, 0, 0, 50, 50);
}
img.src = fr.result;
}
fr.readAsDataURL(file);
});
}
$("input").change(function() {
var picture = this.files;
var leng = picture.length;
for (var i = 0; i < picture.length; i++) {
$("input").after('<canvas width="50" height="50" style="border:1px solid red"></canvas>');
}
loadImage();
});
You need to declare variables inside iteratee function
function loadImage(picture) {
var canvas = document.querySelectorAll('canvas');
var input = document.getElementById('imgfile');
$.each( canvas, function( i, v) {
var file = input.files[i];
var fr = new FileReader(); // file reader per file
fr.onload = function createImage() {
var img = new Image(); // image per file
img.onload = function imageLoaded() {
var ctx = canvas[i].getContext("2d");
ctx.drawImage(img,0,0, 50, 50);
}
img.src = fr.result;
}
fr.readAsDataURL(file);
});
}
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!!
I'm going to get Url Data of selected image and put it into a created image object. I have the Url Data but this imgOrg.src = imgSrc; is not working on FireFox. any Idea why?
window.onload = function() {
var reader, imgSrc, imgOrg;
if (window.FileReader) {
reader = new FileReader();
reader.onloadend = function(e) {
imgSrc = e.target.result;
console.info(imgSrc);
};
}
var avatarInput = document.querySelector('#imageFile');
avatarInput.addEventListener('change', function(e) {
console.info(imgOrg);
imgOrg = {};
var element = e.target;
imgSrc = '';
if (element.value !== '') {
reader.readAsDataURL(e.target.files[0]);
setTimeout(function() {
imgOrg = new Image();
imgOrg.src = imgSrc;
console.log("imgOrg.src= " +
imgOrg.src);
console.log(imgSrc);
console.log(imgOrg.width);
console.log(imgOrg.height);
var ratio = imgOrg.width / imgOrg.height;
var fWidth, fHeight;
if (ratio >= 1) {
fWidth = 300;
fHeight = (1 / ratio) * 300;
}
}, 1000);
}
}, false);
};
<input id="imageFile" type="file">
The main issue was: I was trying to get image's width and height before its loading was finished. After creating image object, I put rest of my code into imgOrg.onload
window.onload = function() {
var reader, imgSrc, imgOrg;
if (window.FileReader) {
reader = new FileReader();
reader.onloadend = function(e) {
imgSrc = e.target.result;
console.info(imgSrc);
};
}
var avatarInput = document.querySelector('#imageFile');
avatarInput.addEventListener('change', function(e) {
imgOrg = {};
var element = e.target;
imgSrc = '';
if (element.value !== '') {
reader.readAsDataURL(e.target.files[0]);
setTimeout(function() {
imgOrg = new Image();
imgOrg.src = imgSrc;
imgOrg.onload = function() {
console.log("imgOrg.src= " +
imgOrg.src);
console.log(imgSrc);
console.log(imgOrg.width);
console.log(imgOrg.height);
var ratio = imgOrg.width / imgOrg.height;
var fWidth, fHeight;
if (ratio >= 1) {
fWidth = 300;
fHeight = (1 / ratio) * 300;
}
}
}, 1000);
}
}, false);
};
<input id="imageFile" type="file">
my main goal is to return false out of _validateFile if a File (that is a jpeg) does not have the minimum dimensions. The issue I am having is that it looks like code underneath is getting hit and I can't return false out of _validateFile. I think I have to use a closure but I am not sure. Here is some code:
_validateFile: function(file){
var validDim = 1;
this._helper(file, function(x,y){
if(x < 682 || y < 459){
validDim = 2;
} else{
validDim = 3;
}
});
if(validDim == 2) return false;
else if(validDim == 3) return true;
return true;
},
_helper: function(file, callback){
var fr = new FileReader;
fr.onload = function() {
var img = new Image;
img.onload = function(){
var x = img.width;
var y = img.height;
callback(x,y);
}
img.src = fr.result;
};
fr.readAsDataURL(file);
},
Any pointers in the right direction are greatly appreciated.
Thank you!
you missing '()' to invoke a constructor
var fr = new FileReader();
var img = new Image();
Could you give some detail code about the way you use this object method (_validateFile)?