Skip to content

Commit 47ebd5a

Browse files
authored
V2.3.2 (#616)
* Refactor camera component to ./src/camera - Refactor camera component to ./src/camera from ./src/html5-qrcode.ts * Misc fixes (codacy) * Codacy fixes * (refactor) Abstract camera selection UI to a separate class * Add support for zoom slider And misc changes. * misc fixes. * Misc changes per PR comments. * Update change log + codacy fix. * Update html5-qrcode.min.js * Add unit test for camera-selection-ui and camera-zoom-ui * Refactor TorchButton and remove html5qrcode deps To make it feasible to test the class. * Create torch-button.test.ts * Update torch-button.test.ts
1 parent 6710464 commit 47ebd5a

20 files changed

+1056
-233
lines changed

changelog.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,29 @@
33
#### Features or bug fixes.
44
- Hide margin of parent container when camera selection UI is hidden (if only 1 camera is found.) - [Issue#599](https://github.com/mebjas/html5-qrcode/issues/599), [PR#607](https://github.com/mebjas/html5-qrcode/pull/607) by [adamwolf@](https://github.com/adamwolf).
55

6+
**Support for zoom slider in `Html5QrcodeScanner`.**
7+
Added basic support for zoom feature under configuration flag (not enabled by default). This was raised in issue [issue#330](https://github.com/mebjas/html5-qrcode/issues/330).This should help address some focus issues raised so far.
8+
9+
Not supported on Safari or any IOS browser though!
10+
11+
How to use
12+
13+
```js
14+
let html5QrcodeScanner = new Html5QrcodeScanner(
15+
"reader",
16+
{
17+
fps: 10,
18+
qrbox: qrboxFunction,
19+
useBarCodeDetectorIfSupported: true,
20+
rememberLastUsedCamera: true,
21+
aspectRatio: 4/3,
22+
showTorchButtonIfSupported: true,
23+
showZoomSliderIfSupported: true,
24+
defaultZoomValueIfSupported: 2
25+
// ^ this means by default camera will load at 2x zoom.
26+
});
27+
```
28+
629
#### Tech debts
730
- Refactored the camera components out of `src/html5-qrcode.ts`
831

minified/html5-qrcode.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/camera/core-impl.ts

Lines changed: 129 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,127 @@
77

88
import {
99
Camera,
10+
CameraCapabilities,
11+
CameraCapability,
12+
RangeCameraCapability,
1013
CameraRenderingOptions,
1114
RenderedCamera,
12-
RenderingCallbacks
15+
RenderingCallbacks,
16+
BooleanCameraCapability
1317
} from "./core";
1418

19+
/** Interface for a range value. */
20+
interface RangeValue {
21+
min: number;
22+
max: number;
23+
step: number;
24+
}
25+
26+
/** Abstract camera capability class. */
27+
abstract class AbstractCameraCapability<T> implements CameraCapability<T> {
28+
protected readonly name: string;
29+
protected readonly track: MediaStreamTrack;
30+
31+
constructor(name: string, track: MediaStreamTrack) {
32+
this.name = name;
33+
this.track = track;
34+
}
35+
36+
public isSupported(): boolean {
37+
return this.name in this.track.getCapabilities();
38+
}
39+
40+
public apply(value: T): Promise<void> {
41+
let constraint: any = {};
42+
constraint[this.name] = value;
43+
let constraints = { advanced: [ constraint ] };
44+
return this.track.applyConstraints(constraints);
45+
}
46+
47+
public value(): T | null {
48+
let settings: any = this.track.getSettings();
49+
if (this.name in settings) {
50+
let settingValue = settings[this.name];
51+
return settingValue;
52+
}
53+
54+
return null;
55+
}
56+
}
57+
58+
abstract class AbstractRangeCameraCapability extends AbstractCameraCapability<number> {
59+
constructor(name: string, track: MediaStreamTrack) {
60+
super(name, track);
61+
}
62+
63+
public min(): number {
64+
return this.getCapabilities().min;
65+
}
66+
67+
public max(): number {
68+
return this.getCapabilities().max;
69+
}
70+
71+
public step(): number {
72+
return this.getCapabilities().step;
73+
}
74+
75+
public apply(value: number): Promise<void> {
76+
let constraint: any = {};
77+
constraint[this.name] = value;
78+
let constraints = {advanced: [ constraint ]};
79+
return this.track.applyConstraints(constraints);
80+
}
81+
82+
private getCapabilities(): RangeValue {
83+
this.failIfNotSupported();
84+
let capabilities: any = this.track.getCapabilities();
85+
let capability: any = capabilities[this.name];
86+
return {
87+
min: capability.min,
88+
max: capability.max,
89+
step: capability.step,
90+
};
91+
}
92+
93+
private failIfNotSupported() {
94+
if (!this.isSupported()) {
95+
throw new Error(`${this.name} capability not supported`);
96+
}
97+
}
98+
}
99+
100+
/** Zoom feature. */
101+
class ZoomFeatureImpl extends AbstractRangeCameraCapability {
102+
constructor(track: MediaStreamTrack) {
103+
super("zoom", track);
104+
}
105+
}
106+
107+
/** Torch feature. */
108+
class TorchFeatureImpl extends AbstractCameraCapability<boolean> {
109+
constructor(track: MediaStreamTrack) {
110+
super("torch", track);
111+
}
112+
}
113+
114+
/** Implementation of {@link CameraCapabilities}. */
115+
class CameraCapabilitiesImpl implements CameraCapabilities {
116+
private readonly track: MediaStreamTrack;
117+
118+
constructor(track: MediaStreamTrack) {
119+
this.track = track;
120+
}
121+
122+
zoomFeature(): RangeCameraCapability {
123+
return new ZoomFeatureImpl(this.track);
124+
}
125+
126+
torchFeature(): BooleanCameraCapability {
127+
return new TorchFeatureImpl(this.track);
128+
}
129+
}
130+
15131
/** Implementation of {@link RenderedCamera}. */
16132
class RenderedCameraImpl implements RenderedCamera {
17133

@@ -55,18 +171,18 @@ class RenderedCameraImpl implements RenderedCamera {
55171
throw "RenderedCameraImpl video surface onerror() called";
56172
};
57173

58-
this.surface.addEventListener("playing", () => this.onVideoStart());
174+
let onVideoStart = () => {
175+
const videoWidth = this.surface.clientWidth;
176+
const videoHeight = this.surface.clientHeight;
177+
this.callbacks.onRenderSurfaceReady(videoWidth, videoHeight);
178+
this.surface.removeEventListener("playing", onVideoStart);
179+
};
180+
181+
this.surface.addEventListener("playing", onVideoStart);
59182
this.surface.srcObject = this.mediaStream;
60183
this.surface.play();
61184
}
62185

63-
private onVideoStart() {
64-
const videoWidth = this.surface.clientWidth;
65-
const videoHeight = this.surface.clientHeight;
66-
this.callbacks.onRenderSurfaceReady(videoWidth, videoHeight);
67-
this.surface.removeEventListener("playing", this.onVideoStart);
68-
}
69-
70186
static async create(
71187
parentElement: HTMLElement,
72188
mediaStream: MediaStream,
@@ -177,6 +293,10 @@ class RenderedCameraImpl implements RenderedCamera {
177293

178294
});
179295
}
296+
297+
getCapabilities(): CameraCapabilities {
298+
return new CameraCapabilitiesImpl(this.getFirstTrackOrFail());
299+
}
180300
//#endregion
181301
}
182302

src/camera/core.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,46 @@ export interface CameraDevice {
1111
label: string;
1212
}
1313

14+
//#region Features
15+
/** Generic capability of camera. */
16+
export interface CameraCapability<T> {
17+
/** Returns {@code true} if the capability is supported by the camera. */
18+
isSupported(): boolean;
19+
20+
/** Apply the {@code value} to camera for this capability. */
21+
apply(value: T): Promise<void>;
22+
23+
/** Returns current value of this capability. */
24+
value(): T | null;
25+
}
26+
27+
/** Capability of the camera that has range. */
28+
export interface RangeCameraCapability extends CameraCapability<number> {
29+
/** Min value allowed for this capability. */
30+
min(): number;
31+
32+
/** Max value allowed for this capability. */
33+
max(): number;
34+
35+
/** Steps allowed for this capability. */
36+
step(): number;
37+
}
38+
39+
/** Capability of camera that is boolean in nature. */
40+
export interface BooleanCameraCapability extends CameraCapability<boolean> {}
41+
42+
/** Class exposing different capabilities of camera. */
43+
export interface CameraCapabilities {
44+
45+
/** Zoom capability of the camera. */
46+
zoomFeature(): RangeCameraCapability;
47+
48+
/** Torch capability of the camera. */
49+
torchFeature(): BooleanCameraCapability;
50+
}
51+
52+
//#endregion
53+
1454
/** Type for callback called when camera surface is ready. */
1555
export type OnRenderSurfaceReady
1656
= (viewfinderWidth: number, viewfinderHeight: number) => void;
@@ -102,6 +142,11 @@ export interface RenderedCamera {
102142
* @throws error if {@link RenderedCamera} instance is already closed.
103143
*/
104144
applyVideoConstraints(constraints: MediaTrackConstraints): Promise<void>;
145+
146+
/**
147+
* Returns all capabilities of the camera.
148+
*/
149+
getCapabilities(): CameraCapabilities;
105150
}
106151

107152
/** Options for rendering camera feed. */

src/core.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,4 +320,16 @@ export class BaseLoggger implements Logger {
320320
export function isNullOrUndefined(obj?: any) {
321321
return (typeof obj === "undefined") || obj === null;
322322
}
323+
324+
/** Clips the {@code value} between {@code minValue} and {@code maxValue}. */
325+
export function clip(value: number, minValue: number, maxValue: number) {
326+
if (value > maxValue) {
327+
return maxValue;
328+
}
329+
if (value < minValue) {
330+
return minValue;
331+
}
332+
333+
return value;
334+
}
323335
//#endregion

0 commit comments

Comments
 (0)