Uploading an image from computer to Fabric.JS Canvas - javascript

Are there any Fabric.JS Wizards out there?
I've done my fair research and I can't seem to find much of an explanation on how to add an image to the fabric.JS canvas.
User Journey:
a) User uploads an image from an input file type button.
b) As soon as they have selected an image from their computer I want to place it into the users canvas.
So far I've got to storing the image into an expression, this is my code below:
scope.setFile = function(element) {
scope.currentFile = element.files[0];
var reader = new FileReader();
/**
*
*/
reader.onload = function(event) {
// This stores the image to scope
scope.imageSource = event.target.result;
scope.$apply();
};
// when the file is read it triggers the onload event above.
reader.readAsDataURL(element.files[0]);
};
My HTML/Angular:
<label class="app-file-input button">
<i class="icon-enter"></i>
<input type="file"
id="trigger"
onchange="angular.element(this).scope().setFile(this)"
accept="image/*">
</label>
If you haven't guessed yet I am using a MEAN stack. Mongoose, Express, Angular and Node.
The scope imageSource is what the image is store in. I've read this SO
and it talks about pushing the image to the Image object with my result, and then pass it to the fabric.Image object. Has anyone done a similar thing that they can help me with?
Thanks in advance
**** UPDATE ****
Directive defines the canvas variable:
var canvas = new fabric.Canvas(attrs.id, {
isDrawingMode: true
});

Upload image from computer with Fabric js.
var canvas = new fabric.Canvas('canvas');
document.getElementById('file').addEventListener("change", function (e) {
var file = e.target.files[0];
var reader = new FileReader();
reader.onload = function (f) {
var data = f.target.result;
fabric.Image.fromURL(data, function (img) {
var oImg = img.set({left: 0, top: 0, angle: 00,width:100, height:100}).scale(0.9);
canvas.add(oImg).renderAll();
var a = canvas.setActiveObject(oImg);
var dataURL = canvas.toDataURL({format: 'png', quality: 0.8});
});
};
reader.readAsDataURL(file);
});
canvas{
border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.min.js"></script>
<input type="file" id="file"><br />
<canvas id="canvas" width="450" height="450"></canvas>

once you have a dataURL done, you can do either:
reader.onload = function(event) {
// This stores the image to scope
fabric.Image.fromURL(event.target.result, function(img) {
canvas.add(img);
});
scope.$apply();
};
Or you put on your image tag an onload event where you do:
var img = new fabric.Image(your_img_element);
canvas.add(img);

This is for drag and drop from desktop using the dataTransfer interface.
canvas.on('drop', function(event) {
// prevent the file to open in new tab
event.e.stopPropagation();
event.e.stopImmediatePropagation();
event.e.preventDefault();
// Use DataTransfer interface to access the file(s)
if(event.e.dataTransfer.files.length > 0){
var files = event.e.dataTransfer.files;
for (var i = 0, f; f = files[i]; i++) {
// Only process image files.
if (f.type.match('image.*')) {
// Read the File objects in this FileList.
var reader = new FileReader();
// listener for the onload event
reader.onload = function(evt) {
// put image on canvas
fabric.Image.fromURL(evt.target.result, function(obj) {
obj.scaleToHeight(canvas.height);
obj.set('strokeWidth',0);
canvas.add(obj);
});
};
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
}
}
});
Resources
https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop

Related

Javascript canvas drawImage results in blank? -revised-

I'm working inside a lit-element component trying to draw an img element onto a canvas element:
return html`
<img></img>
<br>
<canvas width="1500" height="1500"></canvas>
<br>
<input type='file' accept='image/*' #change=${this.readImage}> </input>
`
readImage(event) {
// Get the image element
var img = this.shadowRoot.querySelector('img')
// Get the file
var file = event.target.files[0]
// File reader
var reader = new FileReader()
// Handle read
reader.onload = event=>{
img.src = event.target.result
// Get the canvas element
var canvas = this.shadowRoot.querySelector('canvas')
// Get the canvas context
var context = canvas.getContext('2d')
// Draw image on canvas
context.drawImage(img,0,0)
}
// Read the file
reader.readAsDataURL(file)
}
However for some reason the canvas element remains blank. I can't seam to find out where I'm going wrong or if I'm running into some kind of bug?
You need to use onload to make sure the image has finished loading. Is there a reason you aren't just using a new Image() object instead of using an embedded img element?
this.shadowRoot=document;
function
readImage(event) {
// Get the image element
var img = this.shadowRoot.querySelector('img')
// Get the file
var file = event.target.files[0]
// File reader
var reader = new FileReader()
// Handle read
reader.onload = event => {
img.onload = () => {
// Get the canvas element
var canvas = this.shadowRoot.querySelector('canvas')
// Get the canvas context
var context = canvas.getContext('2d')
// Draw image on canvas
context.drawImage(img, 0, 0)
}
img.src = event.target.result
}
// Read the file
reader.readAsDataURL(file)
}
<img></img>
<br>
<canvas width="1500" height="1500"></canvas>
<br>
<input type='file' accept='image/*' onchange=readImage(event)> </input>

FabricJS SVG to canvas (using FileReader)

On my page, I have a button and a canvas with the following attributes -
<button onclick="openSVG()">Open</button>
<canvas></canvas>
On clicking the button, the openSVG() function is supposed to
Let the user select and open an SVG file from their local hard drive (previously created using FabricJS)
Read the contents of the SVG file using FileReader and storing it in a variable
Using that variable to populate the canvas using fabric.loadSVGFromString() method
Now clicking on Open button opens a file selection window but doesnt draw objects into canvas. Here's my openSVG() function -
function openSVG() {
// importing file
var input = $(document.createElement('input'));
input.attr("id", "mySVGFile")
input.attr("type", "file");
input.trigger('click');
//storing file contents in var using FileReader
var myInputFile = document.getElementById('mySVGFile');
var myFile = myInputFile.files[0];
var fr = new FileReader();
fr.onload = function(){
var canvasD = this.result;
//loading data from var to canvas
fabric.loadSVGFromString(canvasD, function(objects, options) {
var obj = fabric.util.groupSVGElements(objects, options);
canvas.add(obj).renderAll();
});
};
fr.readAsText(myFile); }
I'm new to FileReader. There's no errors in console so I can't even tell where I'm going wrong.
Does my approach not work? Is there a better way to achieve what I want.
Here is a much more easier and better approach using URL.createObjectURL() method ...
var canvas = new fabric.Canvas('c');
function openSVG(e) {
var url = URL.createObjectURL(e.target.files[0]);
fabric.loadSVGFromURL(url, function(objects, options) {
objects.forEach(function(svg) {
svg.set({
top: 90,
left: 90,
originX: 'center',
originY: 'center'
});
svg.scaleToWidth(50);
svg.scaleToHeight(50);
canvas.add(svg).renderAll();
});
});
}
canvas{margin-top:5px;border:1px solid #ccc}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.11/fabric.min.js"></script>
<input type="file" onchange="openSVG(event)">
<canvas id="c" width="180" height="180"></canvas>

Why is this FileReader onload not firing?

I am starting with this working JavaScript code:
// Begin File Open Code.
function fileOpen()
{
var fileInput = document.getElementById('fileInput');
var fileDisplayArea = document.getElementById('iDisplay');
fileInput.addEventListener('change', function(e)
{
file = fileInput.files[0];
var imageType = /image.*/;
if (file.type.match(imageType))
{
var reader = new FileReader();
reader.onload = function(e)
{
iDisplay.innerHTML = "";
image = new Image();
image.src = reader.result;
image.id = "I"; // Add an id attribute so the image editor code can access the image.
iDisplay.appendChild(image);
image.onload = function()
{
c = document.getElementById("canvas1");
context = c.getContext("2d");
c.width = image.width;
c.height = image.height;
context.drawImage(image,0,0);
}
}
reader.readAsDataURL(file);
moreFiles++;
document.getElementById("fileInput").disabled = true;
document.getElementById("fileInputD").innerHTML = '<p>Please refresh page to open a new image</p>';
}
else
{
iDisplay.innerHTML = "File type not supported."
}
});
}
// End File Open Code
And trying to break it up into two separate functions, fileOpen() and loadImage(). The reason is that loadImage will also be used to load default images. I came up with several non-working versions. After reading about the topic here, most recently this answer Javascript FileReader onload not firing I adjusted the code as follows:
// Begin File Open Code
function fileOpen(radio)
{
fileInput = document.getElementById('fileInput');
fileDisplayArea = document.getElementById('iDisplay');
fileInput.addEventListener('change', function(e)
{
file = fileInput.files[0];
var imageType = /image.*/;
if (file.type.match(imageType))
{
reader = new FileReader();
reader.onload = fileSelected;
function fileSelected(e)
{
iDisplay.innerHTML = "";
image = new Image();
image.src = reader.result;
image.id = "I"; // Add an id attribute so the image editor code can access the image.
iDisplay.appendChild(image);
image.onload = loadImage();
}
}
else
{
iDisplay.innerHTML = "File type not supported."
}
})
}
// End File Open Code
// Begin Load Image Code - This will be used to load a preselected image
function loadImage()
{
c = document.getElementById("canvas1");
context = c.getContext("2d");
c.width = image.width;
c.height = image.height;
context.drawImage(image,0,0);
reader.readAsDataURL(file);
document.getElementById("fileInput").disabled = true;
document.getElementById("fileInputD").innerHTML = '<p>Please refresh page to open a new image</p>';
}
// End Load Image Code
But, after reviewing in the Chrome Inspector, the code never gets past
reader.onload = fileSelected;
After that line, the function exits, and nothing is loaded. I have spent hours on variations of this code and cannot get it to fire past that line. So, my question is "why?" I should note that I want to stay with pure JavaScript.
Thank you in advance.
There appears to be a bug in Chrome 73 where it isn't fired if you have a debugger statement.
I literally have
reader.readAsText(blob);
debugger;
and it never hits the onload
If I change it to
debugger;
reader.readAsText(blob);
then it works.
Fixed by Chrome 75.
You can also use FileReader.readAsDataURL() to parse the file from your . This will create a string in memory containing a base64 representation of the image.
<input type="file" accept="image/*" onchange="loadFile(event)">
<img id="output"/>
<script>
var loadFile = function(event) {
var reader = new FileReader();
reader.onload = function(){
var output = document.getElementById('output');
output.src = reader.result;
};
reader.readAsDataURL(event.target.files[0]);
};
</script>
Ref: Preview an image before it is uploaded

Determine the dimensions of image from dataURI

I'm allowing users to load their own image file from file system according to this post (last part - Images from the local file system)
https://stackoverflow.com/a/20285053
My Code
handleFiles: function (evt) {
var file = evt.target.files[0];
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = function (e) {
var contents = e.target.result;
console.log(contents);
//apply contents to src of img element
};
},
After getting the dataUri, I want to display the image on screen, however some images are bigger than the area in which I'm displaying them. Is there a way to check the size of the image given the dataUri (width and height) and resize is accordingly?
I am placing the dataUri in the 'src' attribute of image tag.
You can simply create an element in memory and read the value from it. Assuming "contents" is the dataURI of the image
handleFiles: function (evt) {
var file = evt.target.files[0];
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = function (e) {
var contents = e.target.result;
console.log(contents);
var memoryImg = document.createElement('img');
memoryImg.src = contents;
var width = memoryImg.width;
var height = memoryImg.height;
};
},
The element will be cleaned up as soon as the interpreter finishes your function.

How to deal with iOS taking upside down picture

I use an input field on a website so that a user can take himself into photo.
On iPad, iPhone, the resulting picture is upside down. How can I easily detect if the user used the camera so that I rotate the image via Javascript ?
I use the picture in a Javascript Canvas after.
I got this input field :
<div class="input-field">
<label>Choose image or take a picture :</label>
>input type="file" id="imageLoader" name="imageLoader" accept="image/*"/>
</div>
And in JS :
var imageLoader;
imageLoader = document.getElementById('imageLoader');
imageLoader.addEventListener('change', _handleImage, false);
function _handleImage( e ){
var reader = new FileReader();
reader.onload = function(event){
picture.onload = function(){
// The image here is upside down :( I want to turn it to 180 degrees here
do_stuff( );
};
picture.src = event.target.result;
};
reader.readAsDataURL(e.target.files[0]);
}
I managed to do it using those libs ( I don't have the links in my mind but search google, those specific versions just works ):
Javascript EXIF Reader 0.1.4
Binary Ajax 0.1.10
Heres the full code :
HTML :
<div class="input-field">
<label>Choose image or take a picture :</label>
<input type="file" id="imageLoader" name="imageLoader" accept="image/*"/>
</div>
JS :
var imageLoader, _isUpsideDown = false;
imageLoader = document.getElementById('imageLoader');
imageLoader.addEventListener('change', _handleImage, false);
function _handleImage( e ){
var reader = new FileReader();
reader.onload = function(event){
picture.onload = function(){
// Launch Canvas, display image, etc...
doStuff();
};
picture.src = event.target.result;
};
reader.readAsDataURL(e.target.files[0]);
// Second file reader which will read the file as binaryString to detect the orientation.
var file = this.files[0]; // file
filereaderString = new FileReader; // to read file contents
filereaderString.onloadend = function() {
// get EXIF data
var exif = EXIF.readFromBinaryFile(new BinaryFile(this.result));
// the 3 value means that the image is upside down. 1 is when the image is correct size.
if( exif.Orientation === 3 ){
_isUpsideDown = true;
}
};
filereaderString.readAsBinaryString(file); // read the file
}
From which camera? front or rear? Because they are different too and depends what you want from them. I considered the rear camera.
I created some buttons representing what you have to do for each case:
var angle = 0;
$('#portraitButton').on('click', function() {
angle = 90;
$("#picture").rotate(angle);
});
$('#landscapeLeft').on('click', function() {
angle = 180;
$("#picture").rotate(angle);
});
$('#landscapeRight').on('click', function() {
angle = 180;
$("#picture").rotate(angle);
});
$('#upsideDown').on('click', function() {
angle = -90;
$("#picture").rotate(angle);
});
The demo is here: http://jsfiddle.net/s6zSn/382/
I hope i could help :)

Categories

Resources