Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: filters Integrate experiment #12

Merged
merged 6 commits into from
Feb 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,73 @@
<!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CamCheckMate | Browser Webcam Testing Tool</title>

<!-- <script>
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
// if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
// document.documentElement.classList.add('dark')
// } else {
// document.documentElement.classList.remove('dark')
// }

// Whenever the user explicitly chooses light mode
// localStorage.theme = 'light'

// Whenever the user explicitly chooses dark mode
// localStorage.theme = 'dark'

// Whenever the user explicitly chooses to respect the OS preference
// localStorage.removeItem('theme')


var themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
var themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');

// Change the icons inside the button based on previous settings
if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
themeToggleLightIcon.classList.remove('hidden');
} else {
themeToggleDarkIcon.classList.remove('hidden');
}

var themeToggleBtn = document.getElementById('theme-toggle');

themeToggleBtn.addEventListener('click', function() {

// toggle icons inside button
themeToggleDarkIcon.classList.toggle('hidden');
themeToggleLightIcon.classList.toggle('hidden');

// if set via local storage previously
if (localStorage.getItem('color-theme')) {
if (localStorage.getItem('color-theme') === 'light') {
document.documentElement.classList.add('dark');
localStorage.setItem('color-theme', 'dark');
} else {
document.documentElement.classList.remove('dark');
localStorage.setItem('color-theme', 'light');
}

// if NOT set via local storage previously
} else {
if (document.documentElement.classList.contains('dark')) {
document.documentElement.classList.remove('dark');
localStorage.setItem('color-theme', 'light');
} else {
document.documentElement.classList.add('dark');
localStorage.setItem('color-theme', 'dark');
}
}

});

</script> -->
</head>
<body>
<!-- <button id="theme-toggle" type="button" class="text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5">
<svg id="theme-toggle-dark-icon" class="hidden w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path></svg>
<svg id="theme-toggle-light-icon" class="hidden w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" fill-rule="evenodd" clip-rule="evenodd"></path></svg>
</button> -->
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "CamCheckMate",
"private": true,
"version": "0.0.0",
"version": "0.1.0",
"type": "module",
"homepage": "https://hidaytrahman.github.io/camcheckmate/",
"homepage": "https://camcheckmate.vercel.app/",
"author": {
"name": "Hidayt Rahman",
"email": "hidaytrahman@gmail.com",
Expand Down
28 changes: 28 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,31 @@ i {
/* video {
transform: scaleX(-1);
} */


.filter-sections {
display: flex;
gap: 10px;
align-items: center;
justify-content: center;
}

.filter-sections img {
width: 100px;
cursor: pointer;
background-color: bisque;
height: 81px;
position: relative;
}

.filter-sections img::after {
content: "";
position: absolute;
background-color: red;
z-index: 99;
top: 0;
height: 100%;
width: 100%;
left: 0;
right: 0;
}
8 changes: 6 additions & 2 deletions src/components/common/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ function classNames(...classes) {
type DropdownPropsTypes = { title: string; items: []; onChangeAction: (arg: any) => void; activeDevice: any };

export default function Dropdown({ title = 'Choose', items, onChangeAction, activeDevice }: DropdownPropsTypes) {
console.log({activeDevice})
return (
<Menu as='div' className='relative inline-block text-left'>
<div>
<Menu.Button className='inline-flex w-full justify-center gap-x-1.5 rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50'>
{activeDevice ? activeDevice?.label : title}
{/* @ts-ignore */}
{activeDevice ? activeDevice?.label : items?.[0].label || title}

{/* {items?.[0].label} */}
<ChevronDownIcon className='-mr-1 h-5 w-5 text-gray-400' aria-hidden='true' />
</Menu.Button>
</div>
Expand All @@ -29,7 +33,7 @@ export default function Dropdown({ title = 'Choose', items, onChangeAction, acti
leaveFrom='transform opacity-100 scale-100'
leaveTo='transform opacity-0 scale-95'
>
<Menu.Items className='absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none'>
<Menu.Items className='absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md w-full bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none'>
<div className='py-1'>
{items.map((device: any) => (
<Menu.Item key={device.deviceId}>
Expand Down
4 changes: 3 additions & 1 deletion src/components/features/LiveView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ function LiveView({ cameraMode, setCameraMode, setImgSrc }: LiveViewPropsTypes)
</div>
</div>

<WebCamera cameraMode={cameraMode} setImgSrc={setImgSrc} />
<WebCamera
cameraMode={cameraMode}
setImgSrc={setImgSrc} />
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/features/ResultView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function ResultView({ cameraMode, imgSrc }: ResultViewPropsTypes) {
<AttentionSeeker effect='pulse' delay={3000}>
<div>
<h2 className='text-1xl font-bold tracking-tight text-gray-900 sm:text-2xl'>
{cameraMode ? 'Scanning from camera' : 'Connect with Camera'}
{cameraMode ? 'Camera is running' : 'Connect with Camera'}
</h2>
<p className='mt-4 text-gray-500'>
{cameraMode
Expand Down
83 changes: 80 additions & 3 deletions src/components/features/WebCamera.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,78 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import Dropdown from '../common/Dropdown';
import Webcam from 'react-webcam';
import { FILTERS } from '../../utils/camera.utils';

type WebCameraPropsTypes = {
cameraMode: boolean;
setImgSrc: any;
};
function WebCamera({ cameraMode, setImgSrc }: WebCameraPropsTypes) {
const webcamRef = useRef(null);
const webcamRef = useRef<any>(null);
const [deviceId, setDeviceId] = useState({});
const [devices, setDevices] = useState<[]>([]);
const [activeDevice, setActiveDevice] = useState(null);

const [mirrored, setMirrored] = useState<boolean>(false);
const [filter, setFilter] = useState<string>("none");
const [thumbnail, setThumbnail] = useState<string>("none");

const videoConstraints = {
facingMode: 'user',
deviceId: deviceId,
};

// Prefer camera resolution nearest to 1280x720.
// const constraints = {
// audio: true,
// video: { width: 1280, height: 720 },
// };

// async function getMedia(constraints: any) {
// let stream = null;

// try {
// stream = await navigator.mediaDevices.getUserMedia(constraints);
// /* use the stream */
// } catch (err) {
// /* handle the error */
// }
// }

// navigator.mediaDevices
// .getUserMedia(constraints)
// .then((mediaStream) => {
// const video: any = document.querySelector("video");
// video.srcObject = mediaStream;
// video.onloadedmetadata = () => {
// video.play();
// };


// })
// .catch((err) => {
// // always check for errors at the end.
// console.error(`${err.name}: ${err.message}`);
// });


const onChangeAction = (device: any) => {
setDeviceId(device.deviceId);
setActiveDevice(device);
};

const capture = useCallback(() => {
// @ts-ignore
const imageSrc = webcamRef.current.getScreenshot();
const imageSrc = webcamRef.current?.getScreenshot();
setImgSrc(imageSrc);
}, [webcamRef, setImgSrc]);

const captureThumbnail = useCallback(() => {
// @ts-ignore
const imageSrc = webcamRef.current?.getScreenshot();
setThumbnail(imageSrc);
}, [webcamRef, setImgSrc]);

const handleDevices = useCallback(
// @ts-ignore
(mediaDevices) =>
Expand All @@ -38,10 +83,12 @@ function WebCamera({ cameraMode, setImgSrc }: WebCameraPropsTypes) {
[setDevices]
);


useEffect(() => {
navigator.mediaDevices.enumerateDevices().then(handleDevices);
}, [handleDevices]);


return (
<div>
{cameraMode ? (
Expand All @@ -61,10 +108,40 @@ function WebCamera({ cameraMode, setImgSrc }: WebCameraPropsTypes) {
audio={false}
ref={webcamRef}
videoConstraints={videoConstraints}
style={{
// transform: mirrored ? "scaleX(-1)" : "none"
filter: filter
}}
mirrored={mirrored}
/>

<section className='filter-sections'>
<div
onClick={captureThumbnail}
className='bg-gray-300 h-20 p-5 flex items-center cursor-pointer'>Apply Filters</div>

{
FILTERS.map((_filter, index) =>
<img key={index}
src={thumbnail}
onClick={() => setFilter(_filter)}
alt={_filter}
title={_filter}
style={{
filter: _filter
}}
/>
)
}
</section>
</div>

<button onClick={capture}>Capture photo</button>
<button
onClick={() => setMirrored(!mirrored)}
className='bg-slate-300 text-black mr-2'>
Mirror View [{mirrored ? "ON" : "OFF"}]
</button>
<button onClick={capture}>Capture photo</button> {" "}
</>
) : (
<p>Switch on the camera</p>
Expand Down
1 change: 1 addition & 0 deletions src/utils/camera.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const FILTERS = ["grayscale(1)", "invert(1)", "sepia(1)", "brightness(5)"];
5 changes: 4 additions & 1 deletion src/views/Camera.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ export default function Camera() {
<div className='mx-auto grid max-w-2xl grid-cols-1 items-center gap-x-8 gap-y-10 px-4 py-24 sm:px-6 sm:py-10 lg:max-w-7xl lg:grid-cols-2 lg:px-8 dark:bg-gray-900'>
{loading && <LoaderWrapped />}

<LiveView cameraMode={cameraMode} setCameraMode={setCameraMode} setImgSrc={setImgSrc} />
<LiveView cameraMode={cameraMode}
setCameraMode={setCameraMode}
setImgSrc={setImgSrc} />

<ResultView cameraMode={cameraMode} imgSrc={imgSrc} />
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export default {
theme: {
extend: {},
},
darkMode: 'class',
plugins: [require('@tailwindcss/forms')],
};
Loading