diff --git a/dist/components/Camera/styles.d.ts b/dist/components/Camera/styles.d.ts
index 3f15820..c887596 100644
--- a/dist/components/Camera/styles.d.ts
+++ b/dist/components/Camera/styles.d.ts
@@ -6,5 +6,6 @@ export declare const Container: import("styled-components").StyledComponent<"div
export declare const ErrorMsg: import("styled-components").StyledComponent<"div", any, {}, never>;
export declare const Cam: import("styled-components").StyledComponent<"video", any, {
mirrored: boolean;
+ objectFit?: string | undefined;
}, never>;
export declare const Canvas: import("styled-components").StyledComponent<"canvas", any, {}, never>;
diff --git a/dist/components/Camera/types.d.ts b/dist/components/Camera/types.d.ts
index 975bb80..52d8e42 100644
--- a/dist/components/Camera/types.d.ts
+++ b/dist/components/Camera/types.d.ts
@@ -1,6 +1,7 @@
///
export declare type FacingMode = 'user' | 'environment';
export declare type AspectRatio = 'cover' | number;
+export declare type ObjectFit = React.CSSProperties['objectFit'];
export declare type Stream = MediaStream | null;
export declare type SetStream = React.Dispatch>;
export declare type SetNumberOfCameras = React.Dispatch>;
@@ -9,6 +10,7 @@ export declare type SetPermissionDenied = React.Dispatch> & {
- takePhoto(): string;
+ takePhoto(type?: 'base64url' | 'imgData'): string | ImageData;
switchCamera(): FacingMode;
getNumberOfCameras(): number;
+ toggleTorch(): boolean;
+ torchSupported: boolean;
};
diff --git a/dist/index.cjs.js b/dist/index.cjs.js
index f38882e..579824c 100644
--- a/dist/index.cjs.js
+++ b/dist/index.cjs.js
@@ -23,6 +23,43 @@ See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
+function __awaiter(thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+}
+
+function __generator(thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (_) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+}
+
function __makeTemplateObject(cooked, raw) {
if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
return cooked;
@@ -37,7 +74,10 @@ var Container = styled.div(templateObject_2 || (templateObject_2 = __makeTemplat
: "\n position: relative;\n padding-bottom: " + 100 / aspectRatio + "%;";
});
var ErrorMsg = styled.div(templateObject_3 || (templateObject_3 = __makeTemplateObject(["\n padding: 40px;\n"], ["\n padding: 40px;\n"])));
-var Cam = styled.video(templateObject_4 || (templateObject_4 = __makeTemplateObject(["\n width: 100%;\n height: 100%;\n object-fit: cover;\n z-index: 0;\n transform: rotateY(", ");\n"], ["\n width: 100%;\n height: 100%;\n object-fit: cover;\n z-index: 0;\n transform: rotateY(", ");\n"])), function (_a) {
+var Cam = styled.video(templateObject_4 || (templateObject_4 = __makeTemplateObject(["\n width: 100%;\n height: 100%;\n object-fit: ", ";\n z-index: 0;\n transform: rotateY(", ");\n"], ["\n width: 100%;\n height: 100%;\n object-fit: ", ";\n z-index: 0;\n transform: rotateY(", ");\n"])), function (_a) {
+ var objectFit = _a.objectFit;
+ return objectFit || 'cover';
+}, function (_a) {
var mirrored = _a.mirrored;
return (mirrored ? '180deg' : '0deg');
});
@@ -45,26 +85,65 @@ var Canvas = styled.canvas(templateObject_5 || (templateObject_5 = __makeTemplat
var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5;
var Camera = React__default.forwardRef(function (_a, ref) {
- var _b = _a.facingMode, facingMode = _b === void 0 ? 'user' : _b, _c = _a.aspectRatio, aspectRatio = _c === void 0 ? 'cover' : _c, _d = _a.numberOfCamerasCallback, numberOfCamerasCallback = _d === void 0 ? function () { return null; } : _d, _e = _a.videoSourceDeviceId, videoSourceDeviceId = _e === void 0 ? undefined : _e, _f = _a.errorMessages, errorMessages = _f === void 0 ? {
+ var _b = _a.facingMode, facingMode = _b === void 0 ? 'user' : _b, _c = _a.aspectRatio, aspectRatio = _c === void 0 ? 'cover' : _c, _d = _a.cameraObjectFit, cameraObjectFit = _d === void 0 ? 'cover' : _d, _e = _a.numberOfCamerasCallback, numberOfCamerasCallback = _e === void 0 ? function () { return null; } : _e, _f = _a.videoSourceDeviceId, videoSourceDeviceId = _f === void 0 ? undefined : _f, _g = _a.errorMessages, errorMessages = _g === void 0 ? {
noCameraAccessible: 'No camera device accessible. Please connect your camera or try a different browser.',
permissionDenied: 'Permission denied. Please refresh and give camera permission.',
switchCamera: 'It is not possible to switch camera to different one because there is only one video device accessible.',
canvas: 'Canvas is not supported.',
- } : _f, _g = _a.videoReadyCallback, videoReadyCallback = _g === void 0 ? function () { return null; } : _g;
+ } : _g, _h = _a.videoReadyCallback, videoReadyCallback = _h === void 0 ? function () { return null; } : _h;
var player = React.useRef(null);
var canvas = React.useRef(null);
+ var context = React.useRef(null);
var container = React.useRef(null);
- var _h = React.useState(0), numberOfCameras = _h[0], setNumberOfCameras = _h[1];
- var _j = React.useState(null), stream = _j[0], setStream = _j[1];
- var _k = React.useState(facingMode), currentFacingMode = _k[0], setFacingMode = _k[1];
- var _l = React.useState(false), notSupported = _l[0], setNotSupported = _l[1];
- var _m = React.useState(false), permissionDenied = _m[0], setPermissionDenied = _m[1];
+ var _j = React.useState(0), numberOfCameras = _j[0], setNumberOfCameras = _j[1];
+ var _k = React.useState(null), stream = _k[0], setStream = _k[1];
+ var _l = React.useState(facingMode), currentFacingMode = _l[0], setFacingMode = _l[1];
+ var _m = React.useState(false), notSupported = _m[0], setNotSupported = _m[1];
+ var _o = React.useState(false), permissionDenied = _o[0], setPermissionDenied = _o[1];
+ var _p = React.useState(false), torchSupported = _p[0], setTorchSupported = _p[1];
+ var _q = React.useState(false), torch = _q[0], setTorch = _q[1];
+ var mounted = React.useRef(false);
+ React.useEffect(function () {
+ mounted.current = true;
+ return function () {
+ mounted.current = false;
+ };
+ }, []);
React.useEffect(function () {
numberOfCamerasCallback(numberOfCameras);
}, [numberOfCameras]);
+ var switchTorch = function (on) {
+ if (on === void 0) { on = false; }
+ return __awaiter(void 0, void 0, void 0, function () {
+ var supportedConstraints, track, _a;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ if (!(stream && (navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) && !!mounted.current)) return [3 /*break*/, 4];
+ supportedConstraints = navigator.mediaDevices.getSupportedConstraints();
+ track = stream.getTracks()[0];
+ if (!(supportedConstraints && 'torch' in supportedConstraints && track)) return [3 /*break*/, 4];
+ _b.label = 1;
+ case 1:
+ _b.trys.push([1, 3, , 4]);
+ return [4 /*yield*/, track.applyConstraints({ advanced: [{ torch: on }] })];
+ case 2:
+ _b.sent();
+ return [2 /*return*/, true];
+ case 3:
+ _a = _b.sent();
+ return [2 /*return*/, false];
+ case 4: return [2 /*return*/, false];
+ }
+ });
+ });
+ };
+ React.useEffect(function () {
+ switchTorch(torch);
+ }, [torch]);
React.useImperativeHandle(ref, function () { return ({
- takePhoto: function () {
- var _a, _b, _c, _d;
+ takePhoto: function (type) {
+ var _a, _b, _c, _d, _e;
if (numberOfCameras < 1) {
throw new Error(errorMessages.noCameraAccessible);
}
@@ -75,7 +154,7 @@ var Camera = React__default.forwardRef(function (_a, ref) {
var canvasWidth = ((_c = container === null || container === void 0 ? void 0 : container.current) === null || _c === void 0 ? void 0 : _c.offsetWidth) || 1280;
var canvasHeight = ((_d = container === null || container === void 0 ? void 0 : container.current) === null || _d === void 0 ? void 0 : _d.offsetHeight) || 1280;
var canvasAR = canvasWidth / canvasHeight;
- var sX = void 0, sY = void 0, sW = void 0, sH = void 0;
+ var sX = void 0, sY = void 0, sW = void 0, sH = void 0, imgData = void 0;
if (playerAR > canvasAR) {
sH = playerHeight;
sW = playerHeight * canvasAR;
@@ -90,11 +169,20 @@ var Camera = React__default.forwardRef(function (_a, ref) {
}
canvas.current.width = sW;
canvas.current.height = sH;
- var context = canvas.current.getContext('2d');
- if (context && (player === null || player === void 0 ? void 0 : player.current)) {
- context.drawImage(player.current, sX, sY, sW, sH, 0, 0, sW, sH);
+ if (!context.current) {
+ context.current = canvas.current.getContext('2d', { willReadFrequently: true });
+ }
+ if (context.current && (player === null || player === void 0 ? void 0 : player.current)) {
+ context.current.drawImage(player.current, sX, sY, sW, sH, 0, 0, sW, sH);
+ }
+ switch (type) {
+ case 'imgData':
+ imgData = (_e = context.current) === null || _e === void 0 ? void 0 : _e.getImageData(0, 0, sW, sH);
+ break;
+ default: /* base64url */
+ imgData = canvas.current.toDataURL('image/jpeg');
+ break;
}
- var imgData = canvas.current.toDataURL('image/jpeg');
return imgData;
}
else {
@@ -115,11 +203,18 @@ var Camera = React__default.forwardRef(function (_a, ref) {
getNumberOfCameras: function () {
return numberOfCameras;
},
+ toggleTorch: function () {
+ var torchVal = !torch;
+ setTorch(torchVal);
+ return torchVal;
+ },
+ torchSupported: torchSupported,
}); });
React.useEffect(function () {
- initCameraStream(stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied);
+ initCameraStream(stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied, !!mounted.current);
}, [currentFacingMode, videoSourceDeviceId]);
React.useEffect(function () {
+ switchTorch(false).then(function (success) { return setTorchSupported(success); });
if (stream && player && player.current) {
player.current.srcObject = stream;
}
@@ -135,57 +230,105 @@ var Camera = React__default.forwardRef(function (_a, ref) {
React__default.createElement(Wrapper, null,
notSupported ? React__default.createElement(ErrorMsg, null, errorMessages.noCameraAccessible) : null,
permissionDenied ? React__default.createElement(ErrorMsg, null, errorMessages.permissionDenied) : null,
- React__default.createElement(Cam, { ref: player, id: "video", muted: true, autoPlay: true, playsInline: true, mirrored: currentFacingMode === 'user' ? true : false, onLoadedData: function () {
+ React__default.createElement(Cam, { ref: player, id: "video", muted: true, objectFit: cameraObjectFit, autoPlay: true, playsInline: true, mirrored: currentFacingMode === 'user' ? true : false, onLoadedData: function () {
videoReadyCallback();
} }),
React__default.createElement(Canvas, { ref: canvas }))));
});
Camera.displayName = 'Camera';
-var initCameraStream = function (stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied) {
- var _a;
- // stop any active streams in the window
- if (stream) {
- stream.getTracks().forEach(function (track) {
- track.stop();
- });
- }
- var constraints = {
- audio: false,
- video: {
- deviceId: videoSourceDeviceId ? { exact: videoSourceDeviceId } : undefined,
- facingMode: currentFacingMode,
- width: { ideal: 1920 },
- height: { ideal: 1920 },
- },
- };
- if ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) {
- navigator.mediaDevices
- .getUserMedia(constraints)
- .then(function (stream) {
- setStream(handleSuccess(stream, setNumberOfCameras));
- })
- .catch(function (err) {
- handleError(err, setNotSupported, setPermissionDenied);
- });
- }
- else {
- var getWebcam = navigator.getUserMedia ||
- navigator.webkitGetUserMedia ||
- navigator.mozGetUserMedia ||
- navigator.mozGetUserMedia ||
- navigator.msGetUserMedia;
- if (getWebcam) {
- getWebcam(constraints, function (stream) {
- setStream(handleSuccess(stream, setNumberOfCameras));
- }, function (err) {
- handleError(err, setNotSupported, setPermissionDenied);
- });
+var shouldSwitchToCamera = function (currentFacingMode) { return __awaiter(void 0, void 0, void 0, function () {
+ var cameras;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ cameras = [];
+ if (!(currentFacingMode === 'environment')) return [3 /*break*/, 2];
+ return [4 /*yield*/, navigator.mediaDevices.enumerateDevices().then(function (devices) {
+ var videoDevices = devices.filter(function (i) { return i.kind == 'videoinput'; });
+ videoDevices.forEach(function (device) {
+ var capabilities = device.getCapabilities();
+ if (capabilities.facingMode && capabilities.facingMode.indexOf('environment') >= 0 && capabilities.deviceId) {
+ cameras.push(capabilities.deviceId);
+ }
+ });
+ })];
+ case 1:
+ _a.sent();
+ _a.label = 2;
+ case 2:
+ if (cameras.length > 1) {
+ return [2 /*return*/, cameras.pop()];
+ }
+ return [2 /*return*/, undefined];
}
- else {
- setNotSupported(true);
+ });
+}); };
+var initCameraStream = function (stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied, isMounted) { return __awaiter(void 0, void 0, void 0, function () {
+ var cameraDeviceId, switchToCamera, constraints, getWebcam;
+ var _a;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ // stop any active streams in the window
+ if (stream) {
+ stream.getTracks().forEach(function (track) {
+ track.stop();
+ });
+ }
+ return [4 /*yield*/, shouldSwitchToCamera(currentFacingMode)];
+ case 1:
+ switchToCamera = _b.sent();
+ if (switchToCamera) {
+ cameraDeviceId = switchToCamera;
+ }
+ else {
+ cameraDeviceId = videoSourceDeviceId ? { exact: videoSourceDeviceId } : undefined;
+ }
+ constraints = {
+ audio: false,
+ video: {
+ deviceId: cameraDeviceId,
+ facingMode: currentFacingMode,
+ },
+ };
+ if ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) {
+ navigator.mediaDevices
+ .getUserMedia(constraints)
+ .then(function (stream) {
+ if (isMounted) {
+ setStream(handleSuccess(stream, setNumberOfCameras));
+ }
+ })
+ .catch(function (err) {
+ handleError(err, setNotSupported, setPermissionDenied);
+ });
+ }
+ else {
+ getWebcam = navigator.getUserMedia ||
+ navigator.webkitGetUserMedia ||
+ navigator.mozGetUserMedia ||
+ navigator.mozGetUserMedia ||
+ navigator.msGetUserMedia;
+ if (getWebcam) {
+ getWebcam(constraints, function (stream) { return __awaiter(void 0, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ if (isMounted) {
+ setStream(handleSuccess(stream, setNumberOfCameras));
+ }
+ return [2 /*return*/];
+ });
+ }); }, function (err) {
+ handleError(err, setNotSupported, setPermissionDenied);
+ });
+ }
+ else {
+ setNotSupported(true);
+ }
+ }
+ return [2 /*return*/];
}
- }
-};
+ });
+}); };
var handleSuccess = function (stream, setNumberOfCameras) {
navigator.mediaDevices
.enumerateDevices()
diff --git a/dist/index.esm.js b/dist/index.esm.js
index 8459a52..57c4d05 100644
--- a/dist/index.esm.js
+++ b/dist/index.esm.js
@@ -16,6 +16,43 @@ See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
+function __awaiter(thisArg, _arguments, P, generator) {
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+}
+
+function __generator(thisArg, body) {
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+ function verb(n) { return function (v) { return step([n, v]); }; }
+ function step(op) {
+ if (f) throw new TypeError("Generator is already executing.");
+ while (_) try {
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+ if (y = 0, t) op = [op[0] & 2, t.value];
+ switch (op[0]) {
+ case 0: case 1: t = op; break;
+ case 4: _.label++; return { value: op[1], done: false };
+ case 5: _.label++; y = op[1]; op = [0]; continue;
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
+ default:
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+ if (t[2]) _.ops.pop();
+ _.trys.pop(); continue;
+ }
+ op = body.call(thisArg, _);
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+ }
+}
+
function __makeTemplateObject(cooked, raw) {
if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
return cooked;
@@ -30,7 +67,10 @@ var Container = styled.div(templateObject_2 || (templateObject_2 = __makeTemplat
: "\n position: relative;\n padding-bottom: " + 100 / aspectRatio + "%;";
});
var ErrorMsg = styled.div(templateObject_3 || (templateObject_3 = __makeTemplateObject(["\n padding: 40px;\n"], ["\n padding: 40px;\n"])));
-var Cam = styled.video(templateObject_4 || (templateObject_4 = __makeTemplateObject(["\n width: 100%;\n height: 100%;\n object-fit: cover;\n z-index: 0;\n transform: rotateY(", ");\n"], ["\n width: 100%;\n height: 100%;\n object-fit: cover;\n z-index: 0;\n transform: rotateY(", ");\n"])), function (_a) {
+var Cam = styled.video(templateObject_4 || (templateObject_4 = __makeTemplateObject(["\n width: 100%;\n height: 100%;\n object-fit: ", ";\n z-index: 0;\n transform: rotateY(", ");\n"], ["\n width: 100%;\n height: 100%;\n object-fit: ", ";\n z-index: 0;\n transform: rotateY(", ");\n"])), function (_a) {
+ var objectFit = _a.objectFit;
+ return objectFit || 'cover';
+}, function (_a) {
var mirrored = _a.mirrored;
return (mirrored ? '180deg' : '0deg');
});
@@ -38,26 +78,65 @@ var Canvas = styled.canvas(templateObject_5 || (templateObject_5 = __makeTemplat
var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5;
var Camera = React.forwardRef(function (_a, ref) {
- var _b = _a.facingMode, facingMode = _b === void 0 ? 'user' : _b, _c = _a.aspectRatio, aspectRatio = _c === void 0 ? 'cover' : _c, _d = _a.numberOfCamerasCallback, numberOfCamerasCallback = _d === void 0 ? function () { return null; } : _d, _e = _a.videoSourceDeviceId, videoSourceDeviceId = _e === void 0 ? undefined : _e, _f = _a.errorMessages, errorMessages = _f === void 0 ? {
+ var _b = _a.facingMode, facingMode = _b === void 0 ? 'user' : _b, _c = _a.aspectRatio, aspectRatio = _c === void 0 ? 'cover' : _c, _d = _a.cameraObjectFit, cameraObjectFit = _d === void 0 ? 'cover' : _d, _e = _a.numberOfCamerasCallback, numberOfCamerasCallback = _e === void 0 ? function () { return null; } : _e, _f = _a.videoSourceDeviceId, videoSourceDeviceId = _f === void 0 ? undefined : _f, _g = _a.errorMessages, errorMessages = _g === void 0 ? {
noCameraAccessible: 'No camera device accessible. Please connect your camera or try a different browser.',
permissionDenied: 'Permission denied. Please refresh and give camera permission.',
switchCamera: 'It is not possible to switch camera to different one because there is only one video device accessible.',
canvas: 'Canvas is not supported.',
- } : _f, _g = _a.videoReadyCallback, videoReadyCallback = _g === void 0 ? function () { return null; } : _g;
+ } : _g, _h = _a.videoReadyCallback, videoReadyCallback = _h === void 0 ? function () { return null; } : _h;
var player = useRef(null);
var canvas = useRef(null);
+ var context = useRef(null);
var container = useRef(null);
- var _h = useState(0), numberOfCameras = _h[0], setNumberOfCameras = _h[1];
- var _j = useState(null), stream = _j[0], setStream = _j[1];
- var _k = useState(facingMode), currentFacingMode = _k[0], setFacingMode = _k[1];
- var _l = useState(false), notSupported = _l[0], setNotSupported = _l[1];
- var _m = useState(false), permissionDenied = _m[0], setPermissionDenied = _m[1];
+ var _j = useState(0), numberOfCameras = _j[0], setNumberOfCameras = _j[1];
+ var _k = useState(null), stream = _k[0], setStream = _k[1];
+ var _l = useState(facingMode), currentFacingMode = _l[0], setFacingMode = _l[1];
+ var _m = useState(false), notSupported = _m[0], setNotSupported = _m[1];
+ var _o = useState(false), permissionDenied = _o[0], setPermissionDenied = _o[1];
+ var _p = useState(false), torchSupported = _p[0], setTorchSupported = _p[1];
+ var _q = useState(false), torch = _q[0], setTorch = _q[1];
+ var mounted = useRef(false);
+ useEffect(function () {
+ mounted.current = true;
+ return function () {
+ mounted.current = false;
+ };
+ }, []);
useEffect(function () {
numberOfCamerasCallback(numberOfCameras);
}, [numberOfCameras]);
+ var switchTorch = function (on) {
+ if (on === void 0) { on = false; }
+ return __awaiter(void 0, void 0, void 0, function () {
+ var supportedConstraints, track, _a;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ if (!(stream && (navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) && !!mounted.current)) return [3 /*break*/, 4];
+ supportedConstraints = navigator.mediaDevices.getSupportedConstraints();
+ track = stream.getTracks()[0];
+ if (!(supportedConstraints && 'torch' in supportedConstraints && track)) return [3 /*break*/, 4];
+ _b.label = 1;
+ case 1:
+ _b.trys.push([1, 3, , 4]);
+ return [4 /*yield*/, track.applyConstraints({ advanced: [{ torch: on }] })];
+ case 2:
+ _b.sent();
+ return [2 /*return*/, true];
+ case 3:
+ _a = _b.sent();
+ return [2 /*return*/, false];
+ case 4: return [2 /*return*/, false];
+ }
+ });
+ });
+ };
+ useEffect(function () {
+ switchTorch(torch);
+ }, [torch]);
useImperativeHandle(ref, function () { return ({
- takePhoto: function () {
- var _a, _b, _c, _d;
+ takePhoto: function (type) {
+ var _a, _b, _c, _d, _e;
if (numberOfCameras < 1) {
throw new Error(errorMessages.noCameraAccessible);
}
@@ -68,7 +147,7 @@ var Camera = React.forwardRef(function (_a, ref) {
var canvasWidth = ((_c = container === null || container === void 0 ? void 0 : container.current) === null || _c === void 0 ? void 0 : _c.offsetWidth) || 1280;
var canvasHeight = ((_d = container === null || container === void 0 ? void 0 : container.current) === null || _d === void 0 ? void 0 : _d.offsetHeight) || 1280;
var canvasAR = canvasWidth / canvasHeight;
- var sX = void 0, sY = void 0, sW = void 0, sH = void 0;
+ var sX = void 0, sY = void 0, sW = void 0, sH = void 0, imgData = void 0;
if (playerAR > canvasAR) {
sH = playerHeight;
sW = playerHeight * canvasAR;
@@ -83,11 +162,20 @@ var Camera = React.forwardRef(function (_a, ref) {
}
canvas.current.width = sW;
canvas.current.height = sH;
- var context = canvas.current.getContext('2d');
- if (context && (player === null || player === void 0 ? void 0 : player.current)) {
- context.drawImage(player.current, sX, sY, sW, sH, 0, 0, sW, sH);
+ if (!context.current) {
+ context.current = canvas.current.getContext('2d', { willReadFrequently: true });
+ }
+ if (context.current && (player === null || player === void 0 ? void 0 : player.current)) {
+ context.current.drawImage(player.current, sX, sY, sW, sH, 0, 0, sW, sH);
+ }
+ switch (type) {
+ case 'imgData':
+ imgData = (_e = context.current) === null || _e === void 0 ? void 0 : _e.getImageData(0, 0, sW, sH);
+ break;
+ default: /* base64url */
+ imgData = canvas.current.toDataURL('image/jpeg');
+ break;
}
- var imgData = canvas.current.toDataURL('image/jpeg');
return imgData;
}
else {
@@ -108,11 +196,18 @@ var Camera = React.forwardRef(function (_a, ref) {
getNumberOfCameras: function () {
return numberOfCameras;
},
+ toggleTorch: function () {
+ var torchVal = !torch;
+ setTorch(torchVal);
+ return torchVal;
+ },
+ torchSupported: torchSupported,
}); });
useEffect(function () {
- initCameraStream(stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied);
+ initCameraStream(stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied, !!mounted.current);
}, [currentFacingMode, videoSourceDeviceId]);
useEffect(function () {
+ switchTorch(false).then(function (success) { return setTorchSupported(success); });
if (stream && player && player.current) {
player.current.srcObject = stream;
}
@@ -128,57 +223,105 @@ var Camera = React.forwardRef(function (_a, ref) {
React.createElement(Wrapper, null,
notSupported ? React.createElement(ErrorMsg, null, errorMessages.noCameraAccessible) : null,
permissionDenied ? React.createElement(ErrorMsg, null, errorMessages.permissionDenied) : null,
- React.createElement(Cam, { ref: player, id: "video", muted: true, autoPlay: true, playsInline: true, mirrored: currentFacingMode === 'user' ? true : false, onLoadedData: function () {
+ React.createElement(Cam, { ref: player, id: "video", muted: true, objectFit: cameraObjectFit, autoPlay: true, playsInline: true, mirrored: currentFacingMode === 'user' ? true : false, onLoadedData: function () {
videoReadyCallback();
} }),
React.createElement(Canvas, { ref: canvas }))));
});
Camera.displayName = 'Camera';
-var initCameraStream = function (stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied) {
- var _a;
- // stop any active streams in the window
- if (stream) {
- stream.getTracks().forEach(function (track) {
- track.stop();
- });
- }
- var constraints = {
- audio: false,
- video: {
- deviceId: videoSourceDeviceId ? { exact: videoSourceDeviceId } : undefined,
- facingMode: currentFacingMode,
- width: { ideal: 1920 },
- height: { ideal: 1920 },
- },
- };
- if ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) {
- navigator.mediaDevices
- .getUserMedia(constraints)
- .then(function (stream) {
- setStream(handleSuccess(stream, setNumberOfCameras));
- })
- .catch(function (err) {
- handleError(err, setNotSupported, setPermissionDenied);
- });
- }
- else {
- var getWebcam = navigator.getUserMedia ||
- navigator.webkitGetUserMedia ||
- navigator.mozGetUserMedia ||
- navigator.mozGetUserMedia ||
- navigator.msGetUserMedia;
- if (getWebcam) {
- getWebcam(constraints, function (stream) {
- setStream(handleSuccess(stream, setNumberOfCameras));
- }, function (err) {
- handleError(err, setNotSupported, setPermissionDenied);
- });
+var shouldSwitchToCamera = function (currentFacingMode) { return __awaiter(void 0, void 0, void 0, function () {
+ var cameras;
+ return __generator(this, function (_a) {
+ switch (_a.label) {
+ case 0:
+ cameras = [];
+ if (!(currentFacingMode === 'environment')) return [3 /*break*/, 2];
+ return [4 /*yield*/, navigator.mediaDevices.enumerateDevices().then(function (devices) {
+ var videoDevices = devices.filter(function (i) { return i.kind == 'videoinput'; });
+ videoDevices.forEach(function (device) {
+ var capabilities = device.getCapabilities();
+ if (capabilities.facingMode && capabilities.facingMode.indexOf('environment') >= 0 && capabilities.deviceId) {
+ cameras.push(capabilities.deviceId);
+ }
+ });
+ })];
+ case 1:
+ _a.sent();
+ _a.label = 2;
+ case 2:
+ if (cameras.length > 1) {
+ return [2 /*return*/, cameras.pop()];
+ }
+ return [2 /*return*/, undefined];
}
- else {
- setNotSupported(true);
+ });
+}); };
+var initCameraStream = function (stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied, isMounted) { return __awaiter(void 0, void 0, void 0, function () {
+ var cameraDeviceId, switchToCamera, constraints, getWebcam;
+ var _a;
+ return __generator(this, function (_b) {
+ switch (_b.label) {
+ case 0:
+ // stop any active streams in the window
+ if (stream) {
+ stream.getTracks().forEach(function (track) {
+ track.stop();
+ });
+ }
+ return [4 /*yield*/, shouldSwitchToCamera(currentFacingMode)];
+ case 1:
+ switchToCamera = _b.sent();
+ if (switchToCamera) {
+ cameraDeviceId = switchToCamera;
+ }
+ else {
+ cameraDeviceId = videoSourceDeviceId ? { exact: videoSourceDeviceId } : undefined;
+ }
+ constraints = {
+ audio: false,
+ video: {
+ deviceId: cameraDeviceId,
+ facingMode: currentFacingMode,
+ },
+ };
+ if ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) {
+ navigator.mediaDevices
+ .getUserMedia(constraints)
+ .then(function (stream) {
+ if (isMounted) {
+ setStream(handleSuccess(stream, setNumberOfCameras));
+ }
+ })
+ .catch(function (err) {
+ handleError(err, setNotSupported, setPermissionDenied);
+ });
+ }
+ else {
+ getWebcam = navigator.getUserMedia ||
+ navigator.webkitGetUserMedia ||
+ navigator.mozGetUserMedia ||
+ navigator.mozGetUserMedia ||
+ navigator.msGetUserMedia;
+ if (getWebcam) {
+ getWebcam(constraints, function (stream) { return __awaiter(void 0, void 0, void 0, function () {
+ return __generator(this, function (_a) {
+ if (isMounted) {
+ setStream(handleSuccess(stream, setNumberOfCameras));
+ }
+ return [2 /*return*/];
+ });
+ }); }, function (err) {
+ handleError(err, setNotSupported, setPermissionDenied);
+ });
+ }
+ else {
+ setNotSupported(true);
+ }
+ }
+ return [2 /*return*/];
}
- }
-};
+ });
+}); };
var handleSuccess = function (stream, setNumberOfCameras) {
navigator.mediaDevices
.enumerateDevices()
diff --git a/example/src/App.tsx b/example/src/App.tsx
index 212502a..e8d9aba 100644
--- a/example/src/App.tsx
+++ b/example/src/App.tsx
@@ -171,6 +171,7 @@ const App = () => {
setNumberOfCameras(i)}
videoSourceDeviceId={activeDeviceId}
diff --git a/example/src/Camera/components/Camera/styles.d.ts b/example/src/Camera/components/Camera/styles.d.ts
index 632a5a8..452fe10 100644
--- a/example/src/Camera/components/Camera/styles.d.ts
+++ b/example/src/Camera/components/Camera/styles.d.ts
@@ -6,5 +6,6 @@ export declare const Container: import("styled-components").StyledComponent<"div
export declare const ErrorMsg: import("styled-components").StyledComponent<"div", any, {}, never>;
export declare const Cam: import("styled-components").StyledComponent<"video", any, {
mirrored: boolean;
+ objectFit?: string | undefined;
}, never>;
export declare const Canvas: import("styled-components").StyledComponent<"canvas", any, {}, never>;
diff --git a/example/src/Camera/components/Camera/types.d.ts b/example/src/Camera/components/Camera/types.d.ts
index 53e8cf7..c5166d4 100644
--- a/example/src/Camera/components/Camera/types.d.ts
+++ b/example/src/Camera/components/Camera/types.d.ts
@@ -1,6 +1,7 @@
///
export declare type FacingMode = 'user' | 'environment';
export declare type AspectRatio = 'cover' | number;
+export declare type ObjectFit = React.CSSProperties['objectFit'];
export declare type Stream = MediaStream | null;
export declare type SetStream = React.Dispatch>;
export declare type SetNumberOfCameras = React.Dispatch>;
@@ -9,6 +10,7 @@ export declare type SetPermissionDenied = React.Dispatch(
{
facingMode = 'user',
aspectRatio = 'cover',
+ cameraObjectFit = 'cover',
numberOfCamerasCallback = () => null,
videoSourceDeviceId = undefined,
errorMessages = {
@@ -185,6 +186,7 @@ export const Camera = React.forwardRef(
ref={player}
id="video"
muted={true}
+ objectFit={cameraObjectFit}
autoPlay={true}
playsInline={true}
mirrored={currentFacingMode === 'user' ? true : false}
diff --git a/src/components/Camera/styles.ts b/src/components/Camera/styles.ts
index 02f76d5..2bb1e17 100644
--- a/src/components/Camera/styles.ts
+++ b/src/components/Camera/styles.ts
@@ -28,10 +28,10 @@ export const ErrorMsg = styled.div`
padding: 40px;
`;
-export const Cam = styled.video<{ mirrored: boolean }>`
+export const Cam = styled.video<{ mirrored: boolean; objectFit?: string }>`
width: 100%;
height: 100%;
- object-fit: cover;
+ object-fit: ${({ objectFit }) => objectFit || 'cover'};
z-index: 0;
transform: rotateY(${({ mirrored }) => (mirrored ? '180deg' : '0deg')});
`;
diff --git a/src/components/Camera/types.ts b/src/components/Camera/types.ts
index 361bc33..d0a9908 100644
--- a/src/components/Camera/types.ts
+++ b/src/components/Camera/types.ts
@@ -1,5 +1,6 @@
export type FacingMode = 'user' | 'environment';
export type AspectRatio = 'cover' | number; // for example 16/9, 4/3, 1/1
+export type ObjectFit = React.CSSProperties['objectFit'];
export type Stream = MediaStream | null;
export type SetStream = React.Dispatch>;
export type SetNumberOfCameras = React.Dispatch>;
@@ -8,6 +9,7 @@ export type SetPermissionDenied = React.Dispatch>;
export interface CameraProps {
facingMode?: FacingMode;
aspectRatio?: AspectRatio;
+ cameraObjectFit?: ObjectFit;
numberOfCamerasCallback?(numberOfCameras: number): void;
videoSourceDeviceId?: string | undefined;
errorMessages: {