Andreas Kviby

March 21, 2021

Using the Live Camera feature in Laravel Livewire

So I thought I would enjoy something fun between working hours and came up with a cool idea.

I have no idea if this code is crappy or could be done better in anyway (probably) but it works and I think it is really cool.

I am using Laravel Livewire together with the video and canvas tags to obtain a live snapshot from the camera on your computer, on your phone or tablet.

It works great and I think it shows off how to use the new Laravel Livewire in a TALL Stack application together with some vanilla Javascript to make cool things happen.

Download and watch the video and grab the code below if you like it.

CleanShot 2021-03-21 at 16.13.07.mp4
CleanShot 2021-03-21 at 16.13.07.mp4 260 MB


Livewire HTML code
<div>
        <video class="rounded-lg shadow-lg" playsinline autoplay></video>
        <canvas style="display:none"></canvas>
        <button class="px-4 py-2 mt-4 text-white bg-green-500 rounded-lg" >Save Photo</button>

        <div class="grid grid-cols-12 gap-2 mt-4 sm:grid-cols-3">
            @foreach ($photos as $photo)
                <div class="relative flex items-center px-5 py-5 space-x-3 bg-white border border-gray-300 rounded-lg shadow-sm hover:border-gray-400 focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500">
                    <div class="flex-shrink-0">
                        <img class="h-32 rounded-lg" src="{{ $photo->path }}" alt="">
                    </div>
                </div>
                
            @endforeach
        </div>
</div>
<script>
    const video = document.querySelector('video');
    const canvas = document.querySelector('canvas');

    const button = document.querySelector('button');
    button.onclick = function() {
        canvas.width = video.videoWidth;
        canvas.height = video.videoHeight;
        canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
        Livewire.emit('storePhoto', canvas.toDataURL());
    };

    const constraints = {
        audio: false,
        video: true
    };

    function handleSuccess(stream) {
        window.stream = stream; // make stream available to browser console
        video.srcObject = stream;
    }

    function handleError(error) {
        console.log('navigator.MediaDevices.getUserMedia error: ', error.message, error.name);
    }
    
    navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError);
</script>



Livewire Component code

<?php

namespace App\Http\Livewire;

use App\Models\Photo;
use Carbon\Carbon;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage;

use Livewire\Component;
use Livewire\WithFileUploads;


class UploadPhotos extends Component
{
    use WithFileUploads;

    protected $listeners = ['storePhoto'];
    
    public $photos = [];

    public function storePhoto($imageBlob) {
        $imageBlob = str_replace('data:image/png;base64,', '', $imageBlob);
        $imageBlob = str_replace(' ', '+', $imageBlob);
        $imageName = 'photo' . str_slug(Carbon::now()) . '.png';

        $photo = new Photo();
        $photo->name = $imageName;
        $photo->path = '/storage/' . $imageName;
        Storage::disk('public')->put($imageName, base64_decode($imageBlob));
        $photo->save();

        $this->photos = Photo::all();
    }
    public function mount() {
        Photo::truncate();
    }
    public function render()
    {
        return view('livewire.upload-photos');
    }
}