In this tutorial, we will be building a jQuery and PHP powered photobooth. It will allow your website visitors to take a snapshot with their web camera and upload it from a neat CSS3 interface.
As you might know, it is not possible to access web cameras and other peripheral devices directly from JavaScript (and it won’t be for some time). However there is a solution to our problem – we can use a flash movie. Flash has perfect web camera support, and is installed on nearly all internet – enabled computers.
The solution we are going to use for this app is webcam.js. It is a JavaScript wrapper around flash’s API that gives us control over the user’s webcam.
HTML
The first step to building our Photobooth is laying down the HTML structure of the main page. We will be using jQuery to fetch a list of the latest photos, so we don’t need to embed any PHP logic here. This means we can leave it as a plain HTML file.
index.html
Photobooth with PHP, jQuery and CSS3 | Tutorialzine Demo jQuery & CSS3 Photobooth
« Go back to Tutorialzine
Shoot!
Cancel Upload!
There are three main divs in the page:
- #topBar displays the headings;
- #photos is where the images are inserted after they are requested with jQuery’s $.getJSON method;
- #camera holds the webcam.swf movie (which we are using to communicate with the web camera). It also holds the control buttons for taking photos and uploading.
At the bottom of the body, we are including a number of JavaScript files. Starting with the jQuery library, we are also adding the fancybox plugin for displaying the photos, the easing plugin (to make fancybox even fancier), webcam.js – the plugin that enables us to communicate with web cameras through flash, and finally our own script.js to make all this work together.
Note that if you are tweaking your website for high load, you might want to combine all these JS files together. This will make the page load faster as JavaScript files block the page while loading.
jQuery, CSS3 & PHP PhotoboothPHP
Although the main page is plain old HTML, we do need PHP to make our photo booth work. To be more exact, there are two features of the app that we need PHP for – receiving the uploaded image from flash, and for listing the uploaded files.
upload.php
/* This file receives the JPEG snapshot from assets/webcam/webcam.swf as a POST request.*/// We only need to handle POST requests:if(strtolower($_SERVER['REQUEST_METHOD']) != 'post'){ exit;}$folder = 'uploads/';$filename = md5($_SERVER['REMOTE_ADDR'].rand()).'.jpg';$original = $folder.$filename;// The JPEG snapshot is sent as raw input:$input = file_get_contents('php://input');if(md5($input) == '7d4df9cc423720b7f1f3d672b89362be'){ // Blank image. We don't need this one. exit;}$result = file_put_contents($original, $input);if (!$result) { echo '{ "error" : 1, "message" : "Failed save the image. Make sure you chmod the uploads folder and its subfolders to 777." }'; exit;}$info = getimagesize($original);if($info['mime'] != 'image/jpeg'){ unlink($original); exit;}// Moving the temporary file to the originals folder:rename($original,'uploads/original/'.$filename);$original = 'uploads/original/'.$filename;// Using the GD library to resize// the image into a thumbnail:$origImage = imagecreatefromjpeg($original);$newImage = imagecreatetruecolor(154,110);imagecopyresampled($newImage,$origImage,0,0,0,0,154,110,520,370); imagejpeg($newImage,'uploads/thumbs/'.$filename);echo '{"status":1,"message":"Success!","filename":"'.$filename.'"}';As I mentioned earlier, we can’t communicate with web camerasdirectly from JavaScript. This is why we need flash, which has excellent web camera support, to act as an intermediate layer. This leaves us with two choices:
- we can have flash export the snapshot and make it available to JavaScript (slow and ineffective);
- have flash upload the photo directly to a PHP script.
In our upload.php we validate that the uploaded data is a JPEG image, save it to a file in the uploads/original/ directory, and generate a 154 -by- 110 px thumbnail. I chose this thumbnail size out of convenience, as it shares the same width to height ratio as the original image (520 -by- 370 px), which makes the resize easier.
Take a photo - Webcam Photoboothbrowse.php
/* In this file we are scanning the image folders and returning a JSON object with file names. It is used by jQuery to display the images on the main page:*/// The standard header for json data:header('Content-type: application/json');$perPage = 24;// Scanning the thumbnail folder for JPG images:$g = glob('uploads/thumbs/*.jpg');if(!$g){ $g = array();}$names = array();$modified = array();// We loop though the file names returned by glob,// and we populate a second file with modifed timestamps.for($i=0,$z=count($g);$i1){ $start = array_search($_GET['start'],$names); if($start === false){ // Such a picture was not found $start = 0; }}// nextStart is returned alongside the filenames,// so the script can pass it as a $_GET['start']// parameter to this script if "Load More" is clicked$nextStart = '';if($names[$start+$perPage]){ $nextStart = $names[$start+$perPage];}$names = array_slice($names,$start,$perPage);// Formatting and returning the JSON object:echo json_encode(array( 'files' => $names, 'nextStart' => $nextStart));The browse.php file lists the contents of the image folders as a JSON object. It does it with PHP’s glob function, which scans the folder and returns an array with file names. We then sort this array according to the photo upload dates with the array_multisort function, after which we slice it with array_slice to return only 24 photos at a time.
jQuery
As I mentioned earlier, we are using the webcam.js plugin to control the user’s web camera. This plugin exposes a simple API, available as a global object named webcam. It gives us methods for taking and uploading photos, and for generating the necessary embed code for the swf file.
In script.js below, we will be using this api and build our photo booth script around it. First we will define some variables and cache the most commonly used jQuery selectors throughout the code for better performance:
assets/js/script.js – Part 1
$(document).ready(function(){ var camera = $('#camera'), photos = $('#photos'), screen = $('#screen'); var template = ''; /*---------------------------------- Setting up the web camera ----------------------------------*/ webcam.set_swf_url('assets/webcam/webcam.swf'); webcam.set_api_url('upload.php'); // The upload script webcam.set_quality(80); // JPEG Photo Quality webcam.set_shutter_sound(true, 'assets/webcam/shutter.mp3'); // Generating the embed code and adding it to the page: screen.html( webcam.get_html(screen.width(), screen.height()) );The template variable above holds the markup that will be generated for each photo. It is basically a hyperlink that has the thumbnail of the photo as its background-image, and which points to the full size shot. The {src} attribute gets replaced with the actual file name of the photo (the file names were generated automatically by upload.php in the previous section).
Next we will be binding event listeners for the control buttons. Notice the use of webcam.freeze(), and the webcam.upload() methods. This gives the user the ability to take a shot and decide whether to upload it later. webcam.reset() prepares the web camera for another shot.
assets/js/script.js – Part 2
/*---------------------------------- Binding event listeners ----------------------------------*/ var shootEnabled = false; $('#shootButton').click(function(){ if(!shootEnabled){ return false; } webcam.freeze(); togglePane(); return false; }); $('#cancelButton').click(function(){ webcam.reset(); togglePane(); return false; }); $('#uploadButton').click(function(){ webcam.upload(); webcam.reset(); togglePane(); return false; }); camera.find('.settings').click(function(){ if(!shootEnabled){ return false; } webcam.configure('camera'); }); // Showing and hiding the camera panel: var shown = false; $('.camTop').click(function(){ $('.tooltip').fadeOut('fast'); if(shown){ camera.animate({ bottom:-466 }); } else { camera.animate({ bottom:-5 },{easing:'easeOutExpo',duration:'slow'}); } shown = !shown; }); $('.tooltip').mouseenter(function(){ $(this).fadeOut('fast'); });After this we will need to implement some of the callbacks exposed by the webcam plugin:
assets/js/script.js – Part 3
/*---------------------- Callbacks ----------------------*/ webcam.set_hook('onLoad',function(){ // When the flash loads, enable // the Shoot and settings buttons: shootEnabled = true; }); webcam.set_hook('onComplete', function(msg){ // This response is returned by upload.php // and it holds the name of the image in a // JSON object format: msg = $.parseJSON(msg); if(msg.error){ alert(msg.message); } else { // Adding it to the page; photos.prepend(templateReplace(template,{src:msg.filename})); initFancyBox(); } }); webcam.set_hook('onError',function(e){ screen.html(e); });This completes the web camera integration. However we still need to display a list with the latest photos (and give users a way to browse through older images as well). We will be doing this with a custom function called loadPics() which will communicate with browse.php:
assets/js/script.js – Part 4
/*------------------------------------- Populating the page with images -------------------------------------*/ var start = ''; function loadPics(){ // This is true when loadPics is called // as an event handler for the LoadMore button: if(this != window){ if($(this).html() == 'Loading..'){ // Preventing more than one click return false; } $(this).html('Loading..'); } // Issuing an AJAX request. The start parameter // is either empty or holds the name of the first // image to be displayed. Useful for pagination: $.getJSON('browse.php',{'start':start},function(r){ photos.find('a').show(); var loadMore = $('#loadMore').detach(); if(!loadMore.length){ loadMore = $('<span>',{ id : 'loadMore', html : 'Load More', click : loadPics }); } $.each(r.files,function(i,filename){ photos.append(templateReplace(template,{src:filename})); }); // If there is a next page with images: if(r.nextStart){ // r.nextStart holds the name of the image // that comes after the last one shown currently. start = r.nextStart; photos.find('a:last').hide(); photos.append(loadMore.html('Load More')); } // We have to re-initialize fancybox every // time we add new photos to the page: initFancyBox(); }); return false; } // Automatically calling loadPics to // populate the page onload: loadPics();As loadPics() is bound as a handler for the click event of the Load More tile, this function can be called in two ways: the normal one and as a callback. The difference is that the this object of the function points either to window, or to the DOM element. We can check for this and take appropriate action, like preventing double clicks from issuing multiple requests to browse.php.
Lastly, we have the helper functions, used throughout the rest of the code.
assets/js/script.js – Part 5
/*---------------------- Helper functions ------------------------*/ // This function initializes the // fancybox lightbox script. function initFancyBox(filename){ photos.find('a:visible').fancybox({ 'transitionIn' : 'elastic', 'transitionOut' : 'elastic', 'overlayColor' : '#111' }); } // This function toggles the two // .buttonPane divs into visibility: function togglePane(){ var visible = $('#camera .buttonPane:visible:first'); var hidden = $('#camera .buttonPane:hidden:first'); visible.fadeOut('fast',function(){ hidden.show(); }); } // Helper function for replacing "{KEYWORD}" with // the respectful values of an object: function templateReplace(template,data){ return template.replace(/{([^}]+)}/g,function(match,group){ return data[group.toLowerCase()]; }); }});Now that we’ve discussed all of the code, lets say a few words about the CSS styles.
CSS3
With the recent introduction of Firefox 4, CSS3 transitions can finally become a fully qualified member of our developer toolbox. In our photobooth, we are using CSS3 to add a bit of class to the photo booth.
assets/css/styles.css
/*------------------- Photo area--------------------*/#photos{ margin: 60px auto 100px; overflow: hidden; width: 880px;}#photos:hover a{ opacity:0.5;}#photos a,#loadMore{ background-position: center center; background-color: rgba(14, 14, 14, 0.3); float: left; height: 110px; margin: 1px 1px 0 0; overflow: hidden; width: 145px; -moz-transition:0.25s; -webkit-transition:0.25s; -o-transition:0.25s; transition:0.25s;}#photos a:hover{ opacity:1;}#loadMore{ cursor: pointer; line-height: 110px; text-align: center; text-transform: uppercase; font-size:10px;}#loadMore:hover{ color:#fff; text-shadow:0 0 4px #fff;}In the fragment above, you can see that we’ve defined a 0.25s transition on the photo anchors (these hold our images). This will animate every change to the styles of these elements, including those applied by :hover definitions. This in effect makes all the photos fade out to 50% when we hover over the #photos div, except the one directly beneath the mouse pointer.
With that same transition definition, we also affect the #loadMore span. It has a text shadow rule applied on hover, which gets animated into a smooth glow effect.
CSS3 EffectsWith this our photo booth is complete!
Conclusion
You can use this app as a fun addition to a community forum or other social networking website. You can optionally build in a comment field to go with the photo, or integrate it more deeply with your site.
Did you like this week’s tutorial? Share you thoughts in the comment section.


Read more (locally) ...
Partner : Tutorialzine
LOGECT Publisher Network™ (beta) is a Service that allows others to publish their syndicated content by RSS Feeds.
موضوعات مشابه:
- [JQuery] Getting a checkbox in a function - LOGECT Publisher
- نمایش کدهای HTML5 و CSS3 در مرورگر اینترنت اکسپلورر - LOGECT Publisher
- Coding a Rotating Image Slideshow w/ CSS3 - LOGECT Publisher
- Making Better Select Elements with jQuery and CSS3 - LOGECT Publisher
- CSS3 Animated Bubble Buttons - LOGECT Publisher
LinkBack URL
در مورد LinkBacks
پاسخ با نقل قول
