Пишем Photobooth на PHP, jQuery и CSS3
В этой статье мы будем писать WEB приложение Photobooth на jQuery и PHP. Оно позволит посетителям сделать снимок с веб-камеры и загрузить его в галерею, доступную для просмостра всем остальным.
Как вы знаете, с помощью JavaScript не возможно получить доступ к веб-камере и к другим периферийным устройствам. Однако есть решение – мы можем использовать flash. Flash имеет полный доступ к веб-камере, а так же установлен на большинство компьютеров.
Решать данную задачу мы будем с помощью библиотеки webcam.js. Это JavaScript-оболочка для flash API, что даст нам контроль над веб-камерой пользователя.
Первым шагом к построению нашего Photobooth приложения будет написание HTML структуры страницы. Поэтому создадим простой HTML файл index.html со следующим содержимым:
12345678910111213141516171819202122232425262728293031323334353637383940414243
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Пишем Photobooth на PHP, jQuery и CSS3</title>
<link rel="stylesheet" type="text/css" href="assets/fancybox/jquery.fancybox.css"/>
<link rel="stylesheet" type="text/css" href="assets/css/buttons.css"/>
<link rel="stylesheet" type="text/css" href="assets/css/styles.css"/>
</head>
<body>
<a href="http://nikitakiselev.ru/post/20" class="ref-link" title="Вернуться к статье">
Назад к статье
</a>
<div id="photos"></div>
<div id="camera">
<div id="screen"></div>
<div id="buttons">
<div class="buttonPane">
<a id="shootButton" href="" class="blueButton">Фото!</a>
</div>
<div class="buttonPane hidden">
<a id="cancelButton" href="" class="red">Отмена</a> <a id="uploadButton" href="" class="green">Загрузить!</a>
</div>
</div>
<span class="settings"></span>
<span class="camTop"></span>
<span class="tooltip"></span>
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script>
<script src="assets/fancybox/jquery.fancybox.pack.js"></script>
<script src="assets/jpegcam/webcam.js"></script>
<script src="assets/js/script.js"></script>
</body>
</html>
На странице у нас 3 DIV элемента:
- #topBar отображает заголовки.
- #photos куда изображения будут добавляться с помощью jQuery и метода $.getJSON.
- #camera в этом блоке находится флеш ролик webcam.swf, который мы будем использовать для доступа к камере пользователя. Так же в этом блоке находятся управляющие элементы: кнопка фотографирования и загрузки в галерею.
Управляющие элементы разделены на 2 div'a с классами .buttonPane. С помощью jQuery мы будем переключаться между копкой фотографирования и кнопкой загрузки фото в галерею.
Перед закрывающимся тегом </body> мы подключаем несколько javascript библиотек:
- библиотека jQuery.
- jQuery плагин fancybox для отображения фотографий во всплывающем окне.
- easing plugin добавляет новые анимации для jQuery.
- webcam.js
Если данное WEB приложение планируется использовать на высоконагруженном сайте, то есть смысл объединить все js в один и сжать.
Напишем PHP скрипт, который будет получать фото от камеры (upload.php), а так же скрипт для получения списока уже существующих изображений (browse.php)
upload.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
<?php
if (strtolower($_SERVER['REQUEST_METHOD']) != 'post') {
exit;
}
$folder = 'uploads/';
$filename = md5($_SERVER['REMOTE_ADDR'] . rand()) . '.jpg';
$original = $folder . $filename;
$input = file_get_contents('php://input');
if (md5($input) == '7d4df9cc423720b7f1f3d672b89362be') {
exit;
}
$result = file_put_contents($original, $input);
if (!$result) {
echo '{
"error" : 1,
"message" : "Ошибка при сохранении изображения. Убедитесь, что у Вас есть доступ к папке ' . $folder . '
}';
exit;
}
$info = getimagesize($original);
if ($info['mime'] != 'image/jpeg') {
unlink($original);
exit;
}
rename($original, 'uploads/original/' . $filename);
$original = 'uploads/original/' . $filename;
$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 . '"}';
Как я упоминал ранее, мы не можем общаться с веб-камерой пользователя непосредственно из JavaScript. Вот почему нам нужен flash, который имеет полный доступ к камере. Он чтобы выступать в качестве промежуточного слоя. Получить изображение от веб-камеры можно двумя путями:
- Мы можем передавать изображения напрямую из флеш к javascript (медленно и неоптимально).
- Передавать изображение напрямую в php скрипт с помощью POST.
Мы используем второй способ и передаём снимок напрямую в PHP скрипт. Так же снимок получается в формате JPEG, что означает, что мы можем сохранить его в файл напрямую из PHP, без его преобразования.
В PHP скрипте upload.php мы получаем JPEG изображение и сохраняем его в файл в папку uploads/original/. Затем генерируем миниатюру размером 154x110 px в папку uploads/thumbs. Я выбрал этот размер эскизов из соображений удобства, как он сохраняет пропорции исходного изображения (520x370 px).
browse.php
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
<?php
header('Content-type: application/json');
$perPage = 24;
$g = glob('uploads/thumbs/*.jpg');
if (!$g) {
$g = array();
}
$names = array();
$modified = array();
for ($i = 0, $z = count($g); $i < $z; $i++) {
$path = explode('/', $g[$i]);
$names[$i] = array_pop($path);
$modified[$i] = filemtime($g[$i]);
}
array_multisort($modified, SORT_DESC, $names);
$start = 0;
if (isset($_GET['start']) && strlen($_GET['start']) > 1) {
$_GET['start'] = trim($_GET['start']);
$_GET['start'] = htmlspecialchars($_GET['start']);
$start = array_search($_GET['start'], $names);
if ($start === false) {
$start = 0;
}
}
$nextStart = '';
if ($names[$start + $perPage]) {
$nextStart = $names[$start + $perPage];
}
$names = array_slice($names, $start, $perPage);
echo json_encode(array(
'files' => $names,
'nextStart' => $nextStart
));
Скрипт browse.php, возвращает содержимое папки с изображениями в формате JSON. С помощью функции glob() мы сканируем папку и возвращает массив с именами файлов. Затем мы сортируем массив по дате функцией array_multisort(), после чего "отрезаем" 24 фотографии с помощью array_slice().
Плагин webcam.js предоставляет простое API, доступное как глобальный объект с именем webcam. Он дает нам методы для получения и загрузки фотографий, а так же методы для работы с SWF-файлом.
В script.js мы будем использовать это api. Сначала мы определим некоторые переменные и наиболее часто используемые jQuery селекторы для повышения производительности:
assets/js/script.js – Часть 1
12345678910111213141516171819202122
$(document).ready(function(){
var camera = $('#camera'),
photos = $('#photos'),
screen = $('#screen');
var template = '<a href="uploads/original/{src}" rel="cam" '
+'style="background-image:url(uploads/thumbs/{src})"></a>';
webcam.set_swf_url('assets/webcam/webcam.swf');
webcam.set_api_url('upload.php');
webcam.set_quality(80);
webcam.set_shutter_sound(true, 'assets/jpegcam/shutter.mp3');
screen.html(
webcam.get_html(screen.width(), screen.height())
);
Переменная template в коде выше содержит HTML разметку, которая создается для каждой фотографии. Это обычная гиперссылка, у которой в качестве фона используется миниатюра загруженного изображения. Эта ссылка так же ведёт на полноразмерную версию этого изображения. Атрибут {src} мы заменим на имя файла фотографии (имена файлов генерируются автоматически в upload.php из раздела про PHP).
Далее мы будем привязывать события для кнопок управления. Обратите внимание на использование методов webcam.freeze() и webcam.upload(). Это дает пользователю сфотографиговать и решить, следует ли загрузить фото позднее. Webcam.Reset() подготавливает веб-камеру для следующего фотографирования.
assets/js/script.js - Часть 2
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
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');
});
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');
});
После этого нам нужно реализовать некоторые callbacks, предоставляемые плагином веб-камеры
assets/js/script.js- Часть 3
123456789101112131415161718192021222324252627282930
webcam.set_hook('onLoad',function(){
shootEnabled = true;
});
webcam.set_hook('onComplete', function(msg){
msg = $.parseJSON(msg);
if(msg.error) {
alert(msg.message);
}
else {
photos.prepend(templateReplace(template,{src:msg.filename}));
initFancyBox();
}
});
webcam.set_hook('onError',function(e){
screen.html(e);
});
Здесь мы завершаем работу с WEB камерой. Далее нужно показать список последних фотографий, чтобы пользователи могли их просматривать. Мы будем делать это с помощью пользовательской функции под названием loadPics(), которая будет общаться к browse.php:
assets/js/script.js - Часть 4
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
var start = '';
function loadPics(){
if(this != window){
if($(this).html() == 'Загрузка..') {
}
return false;
}
$(this).html('Загрузка..');
}
$.getJSON('browse.php',{'start':start},function(r){
photos.find('a').show();
var loadMore = $('#loadMore').detach();
if(!loadMore.length){
loadMore = $('<span>',{
id : 'loadMore',
html : 'Далее',
click : loadPics
});
}
$.each(r.files,function(i,filename){
photos.append(templateReplace(template,{src:filename}));
});
if(r.nextStart){
start = r.nextStart;
photos.find('a:last').hide();
photos.append(loadMore.html('Далее'));
}
initFancyBox();
});
return false;
}
loadPics();
Так как loadPics() привязана еще и к событию click для кнопки загрузки дополнительнх фотографий "Далее", эта функция может быть вызвана двумя способами: нормальный и качестве обратного вызова. Разница заключается в том, что объект this будет window или DOM-элементом. Мы проверяем это и создаём защиту от многократных кликов подряд, чтобы небыло множественных запросов к browse.php.
И наконец мы напишем вспомогательные функции, используемые в остальной части кода.
assets/js/script.js - Часть 5
12345678910111213141516171819202122232425262728293031323334
function initFancyBox(filename) {
photos.find('a:visible').fancybox({
'transitionIn': 'elastic',
'transitionOut': 'elastic',
'overlayColor': '#111'
});
}
function togglePane() {
var visible = $('#camera .buttonPane:visible:first');
var hidden = $('#camera .buttonPane:hidden:first');
visible.fadeOut('fast', function () {
hidden.show();
});
}
function templateReplace(template, data) {
return template.replace(/{([^}]+)}/g, function (match, group) {
return data[group.toLowerCase()];
});
}
});
Теперь, когда мы разобрали весь код, давайте напишем CSS-стили.
С внедрением поддержки CSS3 в Firefox 4 эффекты переходов можно, наконец то, использовать на всю катушку.
assets/css/styles.css
12345678910111213141516171819202122232425262728293031323334353637383940414243444546
#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;
}
В фрагменте выше вы можете увидеть, что мы указали 0.25-ти секундный переход на плитки с фотографиями и к кнопкой "загрузить еще". Теперь при наведении мышью на эти элементы они будут анимироваться.
++++
jquery-photobooth-hover.jpgВы можете использовать это приложение как дополнение к форуму сообщества или другой социальной сети на сайте. При необходимости можно построить встроить комментарии к фотографиями и более глубоко интегрировать с Вашим сайтом.