תחי ישראל - אין לנו ארץ אחרת

תחי ישראל -אין לנו ארץ אחרת

מדריך אפליקציית web לצילום ושמירת תמונות

מחבר:
בתאריך:

למדריך זה קיימת גרסה אנגלית שאותה ניתן למצוא בכתובת Learn to code web app that takes pictures with the webcam

האפליקציה שנייצר במדריך באמצעות מעט html וקוד קצר של JavaScript מאפשרת לנו להשתמש בדפדפן האינטרנט כדי לצלם תמונות בדומה לאפליקציה של ניידים. את התמונות נשמור בצד השרת בעזרת PHP.

את התמונות שנצלם באמצעות האפליקציה נערום על המסך. צפו בסרטון הבא שמדגים ב-15 שניות את פעולת הקוד שאותו נסביר במדריך:

 

ואחרי שצפיתם בסרטון, נסביר את באופן עקרוני איך זה עובד:

  • תגית video מציגה כל מה שנקלט במצלמה.
  • ברגע שאנחנו לוחצים על כפתור השמירה, הקוד מדביק את הפריים הנוכחי אל תוך אלמנט canvas.
  • את ה-canvas נמיר למידע שאותו ניתן לשמור כקובץ.
  • את המידע נעביר לצד השרת ב-Ajax, וצד השרת ייצר ממנו קובץ תמונה.
  • המידע שמוחזר מה-Ajax כולל את מיקום הקובץ, שאותו נשלב כ-src של התמונות שיוצגו למשתמש.
  • אפקט ערימת התמונות מושג באמצעות התכונות rotate וz-index. התכונה rotate מטה את התמונות, והתכונה z-index עורמת את התמונות.

HTML

ה-HTML כולל את האלמנטים הבאים:

<div id="newImages"></div>
<video id="player" autoplay></video>
<canvas id="canvas" width="320px" height="240px"></canvas>
<button class="btn btn-primary" id="capture-btn">Capture</button>
<div id="pick-image">
  <label>Video is not supported. Pick an Image instead</label>
  <input type="file" accept="image/*" id="image-picker">
</div>
  • תגית video שבתוכה יוצג כל מה שייקלט במצלמה עד לרגע הצילום. לתגית נוסיף autoplay.
  • canvas שעליו נדביק את התמונה שנצלם באמצעות המצלמה.
  • כפתור שלחיצה עליו תקפיא את הצילום ותפעיל את הקוד ששומר את התמונות על צד השרת.
  • ה-div ששמו newImages יכיל את התמונות החדשות שיוצגו למשתמש אחרי שהאפליקציה יצרה אותם.
  • אלמנט נוסף הוא ה-div ששמו pick-image מאפשר העלאת תמונה כשאין אפשרות לצלם תמונה חדשה.

 

CSS

#newImages {
    height : 300px;
    position : relative;
    max-width : 100%;
}
img.masked {
    position : absolute;
    background-color : #fff;
    border : 1px solid #babbbd;
    padding :10px;
    box-shadow :1px 1px 1px #babbbd;
    margin : 10px auto 0;
}
#player {
    width : 320px;
    height : 240px;
    margin :10px auto;
}
canvas{
    width : 320px;
    height : 240px;
    margin : 10px auto;
}
#capture-btn{
    width : 130px;
    margin : 0 auto;
}
#pick-image{
    display : none;
}
  • האלמנט newImages חייב לקבל position: relative כדי שנוכל לתת לתמונות שנערום בתוכו position: absolute ואז לשחק עם המיקום שלהם ביחס לאלמנט.
  • התמונות שיערמו יקבלו את הקלאס masked, עם position: absolute ושוליים רחבים בדומה לתמונה.
  • האלמנט pick-image לא יוצג כי אנחנו מעוניינים להשתמש בו רק במידה והמשתמש לא יכול לצלם תמונות.

 

JavaScript

  • אחרי טעינת הדף נקרא לפונקציה startMedia שמפעילה את הקוד שאחראי לצילום הווידאו.
  • האזנה ללחיצה על הכפתור תפעיל את הפעולות ששומרות את התמונה ומציגות את הערימה.
const videoPlayer = document.querySelector('#player');
const canvasElement = document.querySelector('#canvas');
const captureButton = document.querySelector('#capture-btn');
const imagePicker = document.querySelector('#image-picker');
const imagePickerArea = document.querySelector('#pick-image');
const newImages = document.querySelector('#newImages');

// Image dimensions
const width = 320;
const height = 240;
let   zIndex = 1;


const startMedia = () => {
    // Play the video if possible
};

// Capture the image, save it and then present it in the page
captureButton.addEventListener('click', (event) => {

});

window.addEventListener("load", (event) => startMedia());

הפונקציה startMedia מאפשרת להפעיל את יכולות הווידאו של הדפדפן.

const startMedia = () => {
  if (!('mediaDevices' in navigator)) {
    navigator.mediaDevices = {};
  }

  if (!('getUserMedia' in navigator.mediaDevices)) {
    navigator.mediaDevices.getUserMedia = (constraints) => {
      const getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;

       if (!getUserMedia) {
          return Promise.reject(new Error('getUserMedia is not supported'));
        } else {
          return new Promise((resolve, reject) => getUserMedia.call(navigator, constraints, resolve, reject));
        }
    };
  }

  navigator.mediaDevices.getUserMedia({video: true})
    .then((stream) => {
      videoPlayer.srcObject = stream;
      videoPlayer.style.display = 'block';
    })
  .catch((err) => {
    imagePickerArea.style.display = 'block';
  });
};

שימו לב, שהקוד מברר שהדפדפן תומך ביכולות של צילום וידאו באמצעות התכונה mediaDevices. ואם הדפדפן לא תומך הוא מנסה ליצור את האובייקט באמצעות התכונות webkitGetUserMedia או mozGetUserMedia. אם התמיכה קיימת, הווידאו מוצג. אחרת מוצג השדה של העלאת תמונה.

 

הקלקה על הכפתור גורמת לדברים הבאים:

  • הצגת ה-canvas, והציור שלו.
  • הפיכת הצילום למידע שניתן לשמור כקובץ תמונה:
canvasElement.toDataURL()
  • והעברת המידע לשמירה בצד השרת באמצעות fetch בכתובת 'api/save_image.php'. המידע המוחזר מצד השרת כולל את מיקומו של קובץ התמונה שיצר ושמר השרת, והדבקתו חזרה לדף עם אפקט ערימה.
// Capture the image, save it and show it in the page
captureButton.addEventListener('click', (event) => {
  // Draw the image from the video player on the canvas
  canvasElement.style.display = 'block';
  const context = canvasElement.getContext('2d');
  context.drawImage(videoPlayer, 0, 0, canvas.width, canvas.height);

   videoPlayer.srcObject.getVideoTracks().forEach((track) => {
    // track.stop(); 
  });

  // Convert the data so it can be saved as a file
  let picture = canvasElement.toDataURL();

  // Save the file by posting it to the server
  fetch('/api/save_image.php', {
    method : 'post',
    body   : JSON.stringify({data: picture })
  })
  .then((res) => res.json())
  .then((data) => {
    if(data.success){
      // Create the image and give it the CSS style with a random tilt 
      //  and a z-index which gets bigger
      //  each time that an image is added to the div
      let newImage = createImage(data.path, "new image", "new image", width, height, "masked");
      let tilt = -(20 + 60 * Math.random());
      newImage.style.transform = "rotate("+tilt+"deg)";
      zIndex++;
      newImage.style.zIndex    = zIndex;
      newImages.appendChild(newImage);
      canvasElement.classList.add('masked');
    }
  })
  .catch((error) => console.log(error))
});
  • התמונות שנערמות בצד המשתמש מיוצרות על ידי הפונקציה createImage:
const createImage = (src, alt, title, width, height, className) => {
    let newImg = document.createElement("img");

    if(src !== null)       newImg.setAttribute("src", src);
    if(alt !== null)       newImg.setAttribute("alt", alt);
    if(title !== null)     newImg.setAttribute("title", title);
    if(width !== null)     newImg.setAttribute("width", width);
    if(height !== null)    newImg.setAttribute("height", height);
    if(className !== null) newImg.setAttribute("class", className);

    return newImg;
}

 

PHP

קוד ה-PHP שומר את התמונה בצד השרת בתיקיית uploads/images, ומחזיר json שכולל מידע על מיקום קובץ התמונה שיוצר השרת או מידע על שגיאות, במידה ואירעו.

api/save_image.php

<?php
$folder = "/uploads/images/";
$destinationFolder = $_SERVER['DOCUMENT_ROOT'] . $folder;
$maxFileSize         = 2*1024*1024;

// Get the posted data
$postdata = file_get_contents("php://input");

if(!isset($postdata) || empty($postdata))
    exit(json_encode(["success"=>false, "reason"=>"Not a post data"]));

// Extract the data
$request = json_decode($postdata);

// Validate
if(trim($request->data) === "")
    exit(json_encode(["success"=>false, "reason"=>"Not a post data"]));


$file = $request->data;

// getimagesize is used to get the file extension
// Only png / jpg mime types are allowed
$size = getimagesize ($file);
$ext  = $size['mime'];
if($ext == 'image/jpeg')
    $ext = '.jpg';
elseif($ext == 'image/png')
    $ext = '.png';
else
    exit(json_encode(['success'=>false, 'reason'=>'only png and jpg mime types are allowed']));

// Prevent the upload of large files
if(strlen(base64_decode($file)) > $maxFileSize)
    exit(json_encode(['success'=>false, 'reason'=>"file size exceeds {$maxFileSize} Mb"]));

// Remove inline tags and spaces
$img = str_replace('data:image/png;base64,', '', $file);
$img = str_replace('data:image/jpeg;base64,', '', $img);
$img = str_replace(' ', '+', $img);

// Read base64 encoded string as an image
$img = base64_decode($img);

// Give the image a unique name. Don't forget the extension
$filename = date("d_m_Y_H_i_s")."-".time().$ext;

// The path to the newly created file inside the upload folder
$destinationPath = "$destinationFolder$filename";

// Create the file or return false
$success = file_put_contents($destinationPath, $img);

if(!$success)
    exit(json_encode(['success'=>false, 'reason'=>'the server failed in creating the image']));

// Inform the browser about the path to the newly created image
exit(json_encode(['success'=>true,'path'=>"$folder$filename"]));

מדריכי JavaScript למתקדמים

 

אהבתם? לא אהבתם? דרגו!

0 הצבעות, ממוצע 0 מתוך 5 כוכבים