I am working on JavaScript code that will show a preview of a local image file before it is uploaded. A web page has a preview area that uses an HTML5 canvas for the preview so that the image can be scaled to fit in the area while keeping the original aspect ratio.
See the test page now at www.rectorsquid.com/previewtest.php.
The problems I faced while writing this code are:
How do I use a FileReader object in JavaScript?
How do I manage an image object in JavaScript?
This is a short list but a serious one.
I obtained some code and went about experimenting. The issues that cropped up along the way were:
Documentation about the FileReader is vague and online examples were all a little different.
Images are not available until after the browser has read the file. Width and height seemed set to zero when I first got the code working a little.
I finally found solutions to what seems like all of the problems I first encountered. First the HTML:
... <div id="previewcanvascontainer"> <canvas id="previewcanvas"> </canvas> </div> <form action="/uploadfile.php" enctype="multipart/form-data" method="post"> <input type="file" onchange="return ShowImagePreview( this.files );" /> </form> ...
The HTML is quite simple. There is a canvas element and a form with an upload button. The onchange attribute of the file upload input will get called whenever a file is selected. I have not tried to implement drag and drop yet so an upload form field is all that is available right now.
There is some CSS to make the canvas a reasonable width but it is not worth showing. Create a style with a width and height that fits your needs.
The JavaScript code is a bit more complicated but not so much:
function ShowImagePreview( files ) { if( !( window.File && window.FileReader && window.FileList && window.Blob ) ) { alert('The File APIs are not fully supported in this browser.'); return false; } if( typeof FileReader === "undefined" ) { alert( "Filereader undefined!" ); return false; } var file = files[0]; if( !( /image/i ).test( file.type ) ) { alert( "File is not an image." ); return false; } reader = new FileReader(); reader.onload = function(event) { var img = new Image; img.onload = UpdatePreviewCanvas; img.src = event.target.result; } reader.readAsDataURL( file ); } function UpdatePreviewCanvas() { var img = this; var canvas = document.getElementById( 'previewcanvas' ); if( typeof canvas === "undefined" || typeof canvas.getContext === "undefined" ) return; var context = canvas.getContext( '2d' ); var world = new Object(); world.width = canvas.offsetWidth; world.height = canvas.offsetHeight; canvas.width = world.width; canvas.height = world.height; if( typeof img === "undefined" ) return; var WidthDif = img.width - world.width; var HeightDif = img.height - world.height; var Scale = 0.0; if( WidthDif > HeightDif ) { Scale = world.width / img.width; } else { Scale = world.height / img.height; } if( Scale > 1 ) Scale = 1; var UseWidth = Math.floor( img.width * Scale ); var UseHeight = Math.floor( img.height * Scale ); var x = Math.floor( ( world.width - UseWidth ) / 2 ); var y = Math.floor( ( world.height - UseHeight ) / 2 ); context.drawImage( img, x, y, UseWidth, UseHeight ); }
The ShowImagePreview() function will get called when the file input is changed. The files list is passed in when it is called and the list contains file information for each selected file. I only support a single file to simplify things right now. Multiple files require a loop and a unique way to preview the images but nothing else. Multiple canvases or a canvas drawing function that uses multiple images would do the trick.
The important part of ShowImagePreview() is setting the FileReader.onload() function correctly. A new function is created and the first operation creates a new image object.
The second thing that is done is the onload function is set. Be careful not to call the function by including the parentheses. I made that mistake the first time I wrote this post. I passed the img object to the function and it somehow worked depending on the timing of things. After the onload function is set correctly, set the .src value based on the FileReader results.
The UpdatePreviewCanvas() function is not too complicated. It sets the canvas width and height to value that match the canvas style width and height. This creates a 1 to 1 relationship between the pixels on the screen and the virtual pixels on the canvas. In other words, a canvas has two width and two height values. One set is the set that controls how things are rendered and the other is the actual screen width and height in pixels. For me, I always set them the same.
good afternoon greetings from Venezuela can help me grasp this same function but on another page and I liked it but I have a problem or rather lack there something and not know how to do eg:
first select a picture and show me truth
I return after selecting a file selection but then I put that if image is not clear the name in input but I can not erase the image can help me?
See this post: https://blog.rectorsquid.com/clear-preview-image-file-selection/
good I do not want a button to clear only they quieron if I selection one picture shows true but let’s assume I want to change and put any file then I get perfect mistake and there is the route I started to take off but I only need to remove previous image selected
Here is some new code. This clears the canvas and keeps the file name when the file is not an image. These are the functions that I changed.
function ShowImagePreview( object, selection )
{
if( typeof object.files === “undefined” )
return;
ClearCanvas( selection );
var files = object.files;
if( !( window.File && window.FileReader && window.FileList && window.Blob ) )
{
alert(‘The File APIs are not fully supported in this browser.’);
return false;
}
if( typeof FileReader === “undefined” )
{
alert( “Filereader undefined!” );
return false;
}
var file = files[0];
if( file !== undefined && file != null && !( /image/i ).test( file.type ) )
{
UpdatePreviewFilename( selection );
alert( “File is not an image.” );
return false;
}
reader = new FileReader();
reader.onload = function( event ) { HandleFileEvent( event, selection ) }
reader.readAsDataURL( file );
}
function ClearImagePreview( selection )
{
var UploadForm = document.getElementById( “UploadForm” );
if( UploadForm !== undefined && UploadForm != null )
{
var button = document.getElementById( “inputoverlaybutton” + selection );
if( typeof button !== “undefined” && button != null )
button.value = “Select Upload File”;
UploadForm.reset();
}
ClearCanvas( selection );
}
function ClearCanvas( selection )
{
var canvas = document.getElementById( “previewcanvas” + selection );
if( canvas === “undefined” )
return;
var context = canvas.getContext( ‘2d’ );
context.clearRect( 0, 0, canvas.width, canvas.height );
}
function UpdatePreviewFilename( selection )
{
var filename = document.getElementById( “uploadfileselection” + selection );
var newvalue = filename.value.replace( “C:\\fakepath\\”, “” );
var button = document.getElementById( “inputoverlaybutton” + selection );
if( typeof button !== “undefined” && button != null )
button.value = newvalue;
}
function UpdatePreviewCanvas( event, img, selection )
{
UpdatePreviewFilename( selection );
var canvas = document.getElementById( “previewcanvas” + selection );
// if( typeof canvas === “undefined” || typeof canvas.getContext === “undefined” )
// return;
var context = canvas.getContext( ‘2d’ );
var world = new Object();
world.width = canvas.offsetWidth;
world.height = canvas.offsetHeight;
canvas.width = world.width;
canvas.height = world.height;
if( typeof img === “undefined” )
return;
var WidthDif = img.width – world.width;
var HeightDif = img.height – world.height;
var Scale = 0.0;
if( WidthDif > HeightDif )
{
Scale = world.width / img.width;
}
else
{
Scale = world.height / img.height;
}
if( Scale > 1 )
Scale = 1;
var UseWidth = Math.floor( img.width * Scale );
var UseHeight = Math.floor( img.height * Scale );
var x = Math.floor( ( world.width – UseWidth ) / 2 );
var y = Math.floor( ( world.height – UseHeight ) / 2 );
context.drawImage( img, x, y, UseWidth, UseHeight );
}
this is my code looks these all well but if I make this process:
1. select one image
2. I wanted to change and put any file other than img
do it displays a message and clear the input fle name but the image stays there we selected I want is erased if you select a file after selecting an image
function ShowImagePreview( files )
{
if( !( window.File && window.FileReader && window.FileList && window.Blob ) )
{
alert(‘El archivo APLS no se apoya plenamente en este navegador.’);
document.getElementById(‘idimagen’).value =”;
return false;
}
if( typeof FileReader === “undefined” )
{
alert( “Lector de archivo no definido!” );
document.getElementById(‘idimagen’).value =”;
return false;
}
var file = files[0];
if( !( /image/i ).test( file.type ) )
{
alert( “Archivo no es un imagen.” );
document.getElementById(‘idimagen’).value =”;
return false;
}
reader = new FileReader();
reader.onload = function(event)
{ var img = new Image;
img.onload = UpdatePreviewCanvas;
img.src = event.target.result; }
reader.readAsDataURL( file );
}
function UpdatePreviewCanvas()
{
var img = this;
var canvas = document.getElementById( ‘previewcanvas’ );
if( typeof canvas === “undefined” || typeof canvas.getContext === “undefined” )
return;
var context = canvas.getContext( ‘2d’ );
var world = new Object();
world.width = canvas.offsetWidth;
world.height = canvas.offsetHeight;
canvas.width = world.width;
canvas.height = world.height;
if( typeof img === “undefined” )
return;
var WidthDif = img.width – world.width;
var HeightDif = img.height – world.height;
var Scale = 0.0;
if( WidthDif > HeightDif )
{
Scale = world.width / img.width;
}
else
{
Scale = world.height / img.height;
}
if( Scale > 1 )
Scale = 1;
var UseWidth = Math.floor( img.width * Scale );
var UseHeight = Math.floor( img.height * Scale );
var x = Math.floor( ( world.width – UseWidth ) / 2 );
var y = Math.floor( ( world.height – UseHeight ) / 2 );
context.drawImage( img, x, y, UseWidth, UseHeight );
}
Ingrese una Foto
angelgoitia,
I don’t have any more time to work on this now. I’ll post something if I work on it later. But for now, you will need to figure it out yourself. Sorry.
Dave
Hi,
Many thanks for your this post.
This some what I need.
I try to do similar thing for photo frame seller site.
So, user can upload an image and click a frame to see how frame suits the image.
But, my problem is about width and height of preview image. Images doesn’t fit the framed canvas area because of width or height of preview image.
How can I define width and height of canvas according to preview image, dynamically? Or any advise?
Here is my test site: http://webozeltasarim.com/cerceve/cerceve.php
I use border-image facility of CSS3.
If you help me, it is so appreciated.
Best regards
Selahattin
Selahattin, it’s been a few years since I worked on image previews. I don’t know how you help you with this.
I will look at the code and see if I can find anything obvious to change. It seems like you just need to do a little math somewhere, and maybe a min or max operation, to get this to work.
By now, there may also be other blogs that have more information that me.
Dave