JavaScript >> Javascript 文檔 >  >> Tags >> CSS

帶有 PHP、jQuery 和 CSS3 的 Photobooth

在本教程中,我們將構建一個 jQuery 和 PHP 驅動的 photobooth。它將允許您的網站訪問者使用他們的網絡攝像頭拍攝快照並從簡潔的 CSS3 界面上傳。

您可能知道,直接從 JavaScript 訪問網絡攝像頭和其他外圍設備是不可能的(一段時間內不會)。然而,我們的問題有一個解決方案——我們可以使用 Flash 電影。 Flash 具有完美的網絡攝像頭支持,並且安裝在幾乎所有支持 Internet 的計算機上。

我們將為這個應用程序使用的解決方案是 webcam.js。它是一個圍繞 flash 的 API 的 JavaScript 包裝器,可以讓我們控制用戶的網絡攝像頭。

HTML

構建我們的 Photobooth 的第一步是設置主頁的 HTML 結構。我們將使用 jQuery 來獲取最新照片的列表,因此我們不需要在此處嵌入任何 PHP 邏輯。這意味著我們可以將其保留為純 HTML 文件。

index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Photobooth with PHP, jQuery and CSS3</title>

<link rel="stylesheet" type="text/css" href="assets/css/styles.css" />
<link rel="stylesheet" type="text/css" href="assets/fancybox/jquery.fancybox-1.3.4.css" />

</head>
<body>

<div id="topBar">
    <h1>jQuery &amp; CSS3 Photobooth</h1>
    <h2>&laquo; Go back to Tutorialzine</h2>
</div>

<div id="photos"></div>

<div id="camera">
    <span class="tooltip"></span>
    <span class="camTop"></span>

    <div id="screen"></div>
    <div id="buttons">
        <div class="buttonPane">
            <a id="shootButton" href="" class="blueButton">Shoot!</a>
        </div>
        <div class="buttonPane hidden">
            <a id="cancelButton" href="" class="blueButton">Cancel</a> <a id="uploadButton" href="" class="greenButton">Upload!</a>
        </div>
    </div>

    <span class="settings"></span>
</div>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
<script src="assets/fancybox/jquery.easing-1.3.pack.js"></script>
<script src="assets/fancybox/jquery.fancybox-1.3.4.pack.js"></script>
<script src="assets/webcam/webcam.js"></script>
<script src="assets/js/script.js"></script>

</body>
</html>

頁面主要有3個div:

  • #topBar 顯示標題;
  • #照片 是使用 jQuery 的 $.getJSON 方法請求圖像後插入圖像的位置;
  • #相機 保存 webcam.swf 電影(我們用來與網絡攝像頭通信)。它還包含用於拍照和上傳的控制按鈕。

控制按鈕分為兩個.buttonPane 分區。在教程的 jQuery 部分,我們將製作一個簡單的函數來在窗格之間切換。

在正文的底部,我們包含了一些 JavaScript 文件。從 jQuery 庫開始,我們還添加了用於顯示照片的 fancybox 插件、緩動插件(使 fancybox 更加精美)、webcam.js - 使我們能夠通過 flash 與網絡攝像頭通信的插件,最後是我們自己的script.js 讓所有這些一起工作。

請注意,如果您正在調整您的網站以實現高負載,您可能希望將所有這些 JS 文件組合在一起。這將使頁面加載速度更快,因為 JavaScript 文件在加載時會阻塞頁面。

PHP

雖然主頁是普通的舊 HTML,但我們確實需要 PHP 來使我們的照相亭工作。更準確地說,我們需要 PHP 的應用程序有兩個功能 - 從 Flash 接收上傳的圖像,以及列出上傳的文件。

上傳.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.'"}';

正如我之前提到的,我們無法直接通過 JavaScript 與網絡攝像頭進行通信。這就是為什麼我們需要具有出色網絡攝像頭支持的閃光燈作為中間層的原因。這給我們留下了兩個選擇:

  • 我們可以通過 Flash 導出快照並使其可用於 JavaScript(緩慢且無效);
  • 讓 Flash 將照片直接上傳到 PHP 腳本。

明智地,flash 網絡攝像頭插件使用第二種方法。它還具有將快照作為有效的 JPEG 圖像上傳的好處,這意味著我們可以直接使用 PHP 將其保存到文件中,而無需對其進行轉換。

在我們的 upload.php 我們驗證上傳的數據是 JPEG 圖像,將其保存到 uploads/original/ 中的文件 目錄,並生成一個 154 × 110 像素的縮略圖。出於方便,我選擇了這個縮略圖大小,因為它與原始圖像具有相同的寬高比(520 × 370 像素),這使得調整大小更容易。

瀏覽.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);$i<$z;$i++){
    $path = explode('/',$g[$i]);
    $names[$i] = array_pop($path);

    $modified[$i] = filemtime($g[$i]);
}

// Multisort will sort the array with the filenames
// according to their timestamps, given in $modified:

array_multisort($modified,SORT_DESC,$names);

$start = 0;

// browse.php can also paginate results with an optional
// GET parameter with the filename of the image to start from:

if(isset($_GET['start']) && strlen($_GET['start'])>1){
    $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
));

browse.php 文件將圖像文件夾的內容列為 JSON 對象。它使用 PHP 的 glob 函數來完成,該函數掃描文件夾並返回一個帶有文件名的數組。然後我們使用array_multisort函數根據照片上傳日期對這個數組進行排序,然後使用array_slice對它進行切片,一次只返回24張照片。

jQuery

正如我之前提到的,我們使用 webcam.js 插件來控制用戶的網絡攝像頭。這個插件公開了一個簡單的 API,可以作為一個名為 webcam 的全局對象使用 .它為我們提供了拍攝和上傳照片以及為 swf 文件生成必要的嵌入代碼的方法。

在下面的 script.js 中,我們將使用這個 api 並圍繞它構建我們的照相亭腳本。首先,我們將定義一些變量並在整個代碼中緩存最常用的 jQuery 選擇器,以獲得更好的性能:

assets/js/script.js - 第 1 部分

$(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>';

    /*----------------------------------
        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())
    );

template 上面的變量保存將為每張照片生成的標記。它基本上是一個超鏈接,將照片的縮略圖作為其背景圖像,並指向完整尺寸的照片。 {src} 屬性被替換為照片的實際文件名(文件名由 upload.php 自動生成 上一節)。

接下來,我們將為控制按鈕綁定事件監聽器。注意 webcam.freeze() 的使用 ,以及 webcam.upload() 方法。這使用戶能夠拍攝並決定是否稍後上傳。 網絡攝像頭.reset() 準備網絡攝像頭進行下一次拍攝。

assets/js/script.js - 第 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');
    });

在此之後,我們將需要實現網絡攝像頭插件公開的一些回調:

assets/js/script.js - 第 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);
    });

這樣就完成了網絡攝像頭的集成。然而,我們仍然需要顯示一個包含最新照片的列表(並為用戶提供一種瀏覽舊圖像的方法)。我們將使用一個名為 loadPics() 的自定義函數來執行此操作 它將與 browse.php 通信

assets/js/script.js - 第 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();

作為 loadPics() 綁定為 click 的處理程序 Load More 事件 tile,這個函數可以通過兩種方式調用:普通的和作為回調的。不同的是 this 函數的對象指向 window , 或 DOM 元素。我們可以檢查這一點並採取適當的措施,例如防止雙擊向browse.php發出多個請求。

最後,我們有在其餘代碼中使用的輔助函數。

assets/js/script.js - 第 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()];
        });
    }
});

現在我們已經討論了所有的代碼,讓我們談談 CSS 樣式。

CSS3

隨著最近 Firefox 4 的推出,CSS3 過渡終於可以成為我們開發人員工具箱中完全合格的成員。在我們的照相亭中,我們正在使用 CSS3 為照相亭添加一些類。

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;
}

在上面的片段中,您可以看到我們在照片錨點上定義了 0.25 秒的過渡(這些錨點保存了我們的圖像)。這將為這些元素的樣式的每次更改設置動畫,包括由 :hover 應用的樣式 定義。當我們將鼠標懸停在 #photos 上時,這實際上使所有照片淡出到 50% div,除了鼠標指針正下方的那個。

使用相同的轉換定義,我們也會影響 #loadMore 跨度。它在懸停時應用了文本陰影規則,從而將動畫變成平滑的發光效果。

有了這個,我們的照相亭就完成了!

結論

您可以將此應用程序用作社區論壇或其他社交網站的有趣補充。您可以選擇在評論字段中添加照片,或將其與您的網站更深入地集成。

你喜歡這週的教程嗎?在評論部分分享你的想法。


Tutorial JavaScript 教程
  1. 並行工作者的 Javascript 簡單平衡

  2. Javascript 傳播運算符...放在一起

  3. 如何使用 React 構建營銷儀表板(twitter 和 youtube)

  4. React 代碼中最常見的問題

  5. JavaScript 中的數據結構和算法(雙向鍊錶)

  6. 帶有私有 npm 包的 Nodejs 雲功能部署

  7. 如何在 React Firebase 中使用 Redux

  1. 為 Node.js 引入 Hole 一種新的無服務器技術

  2. 在 Wintersmith 中創建帖子、自定義元數據和數據

  3. React 中的前 10 名日期選擇器

  4. 反應不確定

  5. 🚀 GitHub 上面向 Web 開發人員的 10 個熱門項目 - 2020 年 8 月 7 日

  6. 使用 Node.js 和 Socket.io 反應即時聊天應用程序

  7. IoT-SaaS:使用 ReactJS、MongoDB Atlas 和 Realm 的 Web 應用程序

  1. 我喜歡前端,討厭 CSS。這是我如何處理它

  2. JavaScript 的奇怪歷史

  3. 需要版主/維護者:Laravel + Vue OSS 項目

  4. 阻塞 iframe 的後備:使用 Vue.js 的(粗略)解決方案