Membuat QR Code scanner dengan Javascript di CodeIgniter 4
Membuat QR code scanner dengan Javascript di CodeIgniter 4 menggunakan library zxing-js
atau “zebra crossing”
Include file library nya
<script src="https://unpkg.com/@zxing/library@latest"></script>
Contoh tampilan view nya, disini menggunakan Bootstrap 4
<button class="btn btn-success btn-block mb-2" id="btnScan"><i class="fa fa-camera mr-2"></i> Scan QR Code</button>
<a href="<?= route_to('absensi') ?>" id="btnBatal" class="btn btn-danger btn-block mb-4 d-none"><i class="fa fa-times mr-2"></i> Batal</a>
<div class="form-group mb-2 d-none" id="formKamera">
<label for="selectKamera">Pilih Kamera</label>
<select class="form-control" id="selectKamera"></select>
</div>
<div class="d-flex justify-content-center">
<video class="mb-2 d-none" id="previewKamera" style="width: 300px; height: 300px;"></video>
</div>
- Pertama tampil hanya button scan saja, untuk button batal, select kamera dan preview kamera di hidden dulu dengan class
d-none
dari Bootstrap - Button batal nge route ke halaman index nya, jadi jika ditekan akan nge refresh
- Komponen select kamera default kosong, nanti diisi dengan daftar device/kamera yang tersedia
- Elemen preview video sebagai tampilan kameranya
Pada kasus ini menggunakan SweetAlert untuk menampilkan alert saat scan berhasil, jadi harus include library nya
<script src="//cdnjs.cloudflare.com/ajax/libs/sweetalert/2.1.2/sweetalert.min.js"></script>
Script untuk scanner nya, jangan lupa include library jquery nya
<script src="<?= base_url('assets/vendor/libs/jquery/jquery.js') ?>"></script>
<script>
// inisialisasi scanner
const qrCodeReader = new ZXing.BrowserMultiFormatReader();
const selectKamera = $("#selectKamera");
let selectedDeviceId = null;
// event handler button scan saat click
$("#btnScan").on("click", startScanning);
// event handler select kamera pada saat change value
$(document).on("change", "#selectKamera", function () {
// set selected device id
selectedDeviceId = $(this).val();
// lalu reset dan init scanner
resetAndInitScanner();
});
function startScanning() {
// cek jika qrCodeReader tidak null, maka reset dan init scanner
if (qrCodeReader) {
resetAndInitScanner();
}
}
function resetAndInitScanner() {
// reset scanner
qrCodeReader.reset();
// tampilkan form kamera, preview kamera, dan button batal
$("#formKamera, #previewKamera, #btnBatal").removeClass("d-none");
// init scanner
initScanner();
}
function initScanner() {
qrCodeReader
.listVideoInputDevices() // ambil list video input devices
.then(handleVideoInputDevices) // lalu handle video input devices
.catch(handleError); // handle error
}
// fungsi untuk handle video input devices
function handleVideoInputDevices(videoInputDevices) {
// jika video input devices lebih dari 0
if (videoInputDevices.length > 0) {
// update select kamera dengan list video input devices
updateSelectKamera(videoInputDevices);
// decode sekali (tidak berulang) dari video device terpilih ke preview kamera
qrCodeReader
.decodeOnceFromVideoDevice(selectedDeviceId, "previewKamera")
.then(handleScanResult) // lalu handle hasil scan
.catch(handleError);
} else {
// jika video input devices tidak ditemukan, maka tampilkan alert
alert("Kamera tidak ditemukan!");
}
}
// fungsi untuk update select kamera dengan list video input devices
function updateSelectKamera(videoInputDevices) {
// kosongkan select kamera
selectKamera.empty();
// loop video input devices
videoInputDevices.forEach((element) => {
// tambahkan option ke select kamera dengan value deviceId dan label device label
const option = $("<option>").text(element.label).val(element.deviceId);
// jika deviceId sama dengan selectedDeviceId, maka set option selected true
if (element.deviceId == selectedDeviceId) {
option.prop("selected", true);
}
// tambahkan option ke select kamera
selectKamera.append(option);
});
}
// fungsi untuk handle hasil scan
function handleScanResult(result) {
console.log(result.text);
// result.text berisi data dari qr code
// sampai sini scan qr code sudah berhasil
// bisa lakukan apa saja dengan hasilnya
// script dibawah akan mengirim post ke controller CodeIgniter 4 untuk update data
// pada contoh kasus yang digunakan, qr code nya berisi URL untuk update data
const formData = new FormData();
// contoh jika ingin mengirim data lewat form body nya
// formData.append('qr_code', result.text);
// pada kasus yang digunakan hanya mengirim post dengan body kosong
// kirim post request ke URL yang didapatkan dari qr code
fetch(result.text, {
method: "POST",
body: formData,
})
.then((response) => response.json())
.then((data) => {
// response dari controller CI berupa JSON
if (data.success) {
// tampilkan alert success menggunakan sweetalert
swal({
icon: "success",
title: "Success",
text: data.success,
showConfirmButton: true,
}).then(() => {
// redirect ke halaman yang didapat dari response CI
window.location.href = data.redirect;
});
}
})
.catch((error) => {
console.error("Error dalam POST request:", error);
});
}
// untuk handle error
function handleError(err) {
console.error(err);
}
// cek jika browser tidak support mediaDevices, maka tampilkan alert
if (!navigator.mediaDevices) {
alert("Tidak bisa mengakses kamera!");
}
</script>
Script di controller CodeIgniter untuk kasus diatas
// app/Controllers/Absensi.php
// ...
public function proses()
{
// cek jika request post
if ($this->request->is('post')) {
// contoh jika mengirim data saat post request
// $qr_code = $this->request->getPost('qr_code');
// ...
// proses update ke database
// ...
// return response json
return $this->response->setJSON([
'success' => 'Absensi masuk berhasil dilakukan',
'redirect' => route_to('absensi'),
]);
}
}
Untuk contoh route nya
$routes->group('absensi', ['filter' => 'auth'], function ($routes) {
$routes->get('/', 'Absensi::index', ['as' => 'absensi']);
$routes->post('proses', 'Absensi::proses', ['as' => 'absensi.proses']);
});
Jika tampil alert Tidak bisa mengakses kamera!
atau bahkan tidak jalan sama sekali, ada beberapa kemungkinan:
- Browser nya memang tidak support, mungkin browser versi lama
- Permission kamera ke block (saat meminta izin akses kamera mungkin kepencet
block
). Bisa dicek di browser di samping kiri alamat URL, disitu bisa dilihat permission yang digunakan, harusnya ada permission camera dengan statusAllow
- Penggunaan permission kamera ini hanya bisa jalan di
localhost
atau di protocolhttps
. Jadi saat testing menggunakan localhost, harusnya bisa jalan - Bahkan menggunakan virtual host pun tidak jalan kalau tidak
https
, saat saya coba menggunakan URLappscanner.test
misalnya, atau dengan IP address laptop192.168.2.101
, kameranya tetap tidak jalan - Jadi harus menggunakan protocol
https
atau denganlocalhost
Saat berhasil dijalankan, scanner akan menampilkan list device/kamera yang tersambung pada perangkat client.
Jika dijalankan di laptop maka akan menampilkan list webcam default laptop, atau webcam external yang tersambung ke laptop.
Jika dijalankan dari Android, maka akan menampilkan list kamera depan atau kamera belakang. Untuk melakukan pengetesan dari mobile device, aplikasi harus di hosting dengan protocol https
yang sudah aktif, jika tidak, gak bakal jalan.
Bagaimana kalau ingin menjalankan dari Android tapi tetap localhost
, apakah mungkin?
Bisa, dengan bantuan DroidCam. Cukup install aplikasi DroidCam dari Play Store di HP Android nya, dan install juga aplikasi DroidCam client nya di laptop.
Ikuti saja tata cara instalasinya, sudah cukup jelas, Android dan laptop harus dalam satu Wifi network
Setelah laptop dengan DroidCam tersambung, nantinya saat scanner dijalankan di laptop, akan ada tambahan daftar device nya yang dari DroidCam. Jadi saat Android melakukan scan QR Code, aplikasi di localhost akan tetap jalan.