Webcam Capture
Headless camera capture: preview, device switching, countdown shutter, and photos.
|
|
| Package |
@vizejs/ui/webcam-capture |
| Maturity |
stable |
| Own the source |
vize lib pull webcam-capture |
| Requires |
context, controllable-state, id |
| Aliases |
webcam, camera capture, photo booth, selfie camera, getUserMedia |
| Covers |
MediaDevices.getUserMedia, HTMLCanvasElement.toBlob, @vizejs/composable useUserMedia, react-webcam |
Usage
import { WebcamCaptureDeviceSelect, WebcamCapturePhoto, WebcamCapture, WebcamCaptureRoot, WebcamCaptureShutter, WebcamCaptureStartButton, WebcamCaptureStatusMessage, WebcamCaptureStopButton, WebcamCaptureSwitchCamera, WebcamCaptureVideo } from "@vizejs/ui/webcam-capture";
Or copy the source into your project with vize lib pull webcam-capture (see Source Distribution).
API
WebcamCaptureDeviceSelect
Source: src/families/media/webcam-capture/webcam-capture-device-select.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
undefined |
Accessible name. Defaults to messages.deviceSelect. |
Slots
| Slot |
Slot props |
Description |
option |
{ readonly device: WebcamCaptureDevice; readonly selected: boolean } |
Option content per camera. Defaults to the device label. |
Exposed
| Member |
Type |
Description |
element |
HTMLSelectElement | null |
Rendered native select. |
WebcamCapturePhoto
Source: src/families/media/webcam-capture/webcam-capture-photo.vue
Props
| Prop |
Type |
Default |
Description |
alt |
string |
undefined |
Alternative text. Defaults to messages.photo. |
Slots
| Slot |
Slot props |
Description |
empty |
Record<string, never> |
Rendered instead of nothing while no photo exists, e.g. a placeholder. |
default |
{ readonly photo: WebcamCapturePhotoResult; readonly url: string } |
Extra content after the image, e.g. a download link. Receives the photo and its URL. |
Exposed
| Member |
Type |
Description |
element |
HTMLImageElement | null |
Rendered image, or null without a photo. |
WebcamCapture
Source: src/families/media/webcam-capture/webcam-capture-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned root id. null and undefined select a deterministic fallback. |
stream |
WebcamMediaStreamLike | null |
undefined |
Consumer-owned stream, e.g. useUserMedia().stream.value. Any value other than undefined (including null) selects external mode: the root never acquires or stops tracks itself. |
constraints |
MediaTrackConstraints |
undefined |
Extra video track constraints for owned acquisition (resolution, frame rate…). |
audio |
boolean |
false |
Also request the microphone for owned acquisition. |
facingMode |
WebcamFacingMode |
undefined |
Controlled facing direction (v-model:facingMode). undefined selects uncontrolled use. |
defaultFacingMode |
WebcamFacingMode |
"user" |
Initial facing direction for uncontrolled use. |
deviceId |
string | null |
undefined |
Controlled camera (v-model:deviceId). null clears the selection; undefined selects uncontrolled use. |
defaultDeviceId |
string |
undefined |
Initial camera for uncontrolled use. |
autoStart |
boolean |
false |
Request the camera on mount. Otherwise permission is only requested by start(). |
mirrored |
boolean |
undefined |
Mirror the preview and captures. undefined mirrors the user camera only. |
host |
WebcamMediaHost | null |
navigator.mediaDevices |
MediaDevices capability for alternate runtimes and tests. |
captureType |
WebcamCaptureImageType |
"image/png" |
Encoded capture type. |
captureQuality |
number |
undefined |
Encoder quality for lossy capture types, from 0 to 1. |
aspectRatio |
number |
undefined |
Center-crop captures to this width / height ratio. |
maxWidth |
number |
undefined |
Downscale captures to at most this width in pixels. |
countdownInterval |
number |
1000 |
Milliseconds per shutter countdown step. |
messages |
Partial<WebcamCaptureMessages> |
undefined |
Localized labels and announcements. Omitted entries use English defaults. |
Events
| Event |
Payload |
Description |
update:facingMode |
[facingMode: WebcamFacingMode] |
Fired when the facing direction requests a new controlled value. |
update:deviceId |
[deviceId: string | null] |
Fired when the selected camera requests a new controlled value. |
statusChange |
[status: WebcamCaptureStatus, previous: WebcamCaptureStatus] |
Fired after every distinct status transition. |
error |
[failure: WebcamCaptureFailure] |
Fired when owned acquisition fails. |
streamChange |
[stream: WebcamMediaStreamLike | undefined] |
Fired when an owned stream starts or stops. |
capture |
[photo: WebcamCapturePhotoResult] |
Fired after a photo is captured. |
captureError |
[error: unknown] |
Fired when a capture fails to draw or encode. |
Slots
| Slot |
Slot props |
Description |
default |
WebcamCaptureSlotState |
Video, controls, photo, and status parts. Receives the camera state. |
Exposed
| Member |
Type |
Description |
capture |
— |
|
capturing |
— |
|
clearPhoto |
— |
|
countdown |
— |
|
deviceId |
— |
|
devices |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
error |
— |
|
external |
— |
|
facingMode |
— |
|
mirrored |
— |
|
photo |
— |
|
photoUrl |
— |
|
selectDevice |
— |
|
start |
— |
|
status |
— |
|
stop |
— |
|
stream |
— |
|
switchCamera |
— |
|
WebcamCaptureRoot
Source: src/families/media/webcam-capture/webcam-capture-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned root id. null and undefined select a deterministic fallback. |
stream |
WebcamMediaStreamLike | null |
undefined |
Consumer-owned stream, e.g. useUserMedia().stream.value. Any value other than undefined (including null) selects external mode: the root never acquires or stops tracks itself. |
constraints |
MediaTrackConstraints |
undefined |
Extra video track constraints for owned acquisition (resolution, frame rate…). |
audio |
boolean |
false |
Also request the microphone for owned acquisition. |
facingMode |
WebcamFacingMode |
undefined |
Controlled facing direction (v-model:facingMode). undefined selects uncontrolled use. |
defaultFacingMode |
WebcamFacingMode |
"user" |
Initial facing direction for uncontrolled use. |
deviceId |
string | null |
undefined |
Controlled camera (v-model:deviceId). null clears the selection; undefined selects uncontrolled use. |
defaultDeviceId |
string |
undefined |
Initial camera for uncontrolled use. |
autoStart |
boolean |
false |
Request the camera on mount. Otherwise permission is only requested by start(). |
mirrored |
boolean |
undefined |
Mirror the preview and captures. undefined mirrors the user camera only. |
host |
WebcamMediaHost | null |
navigator.mediaDevices |
MediaDevices capability for alternate runtimes and tests. |
captureType |
WebcamCaptureImageType |
"image/png" |
Encoded capture type. |
captureQuality |
number |
undefined |
Encoder quality for lossy capture types, from 0 to 1. |
aspectRatio |
number |
undefined |
Center-crop captures to this width / height ratio. |
maxWidth |
number |
undefined |
Downscale captures to at most this width in pixels. |
countdownInterval |
number |
1000 |
Milliseconds per shutter countdown step. |
messages |
Partial<WebcamCaptureMessages> |
undefined |
Localized labels and announcements. Omitted entries use English defaults. |
Events
| Event |
Payload |
Description |
update:facingMode |
[facingMode: WebcamFacingMode] |
Fired when the facing direction requests a new controlled value. |
update:deviceId |
[deviceId: string | null] |
Fired when the selected camera requests a new controlled value. |
statusChange |
[status: WebcamCaptureStatus, previous: WebcamCaptureStatus] |
Fired after every distinct status transition. |
error |
[failure: WebcamCaptureFailure] |
Fired when owned acquisition fails. |
streamChange |
[stream: WebcamMediaStreamLike | undefined] |
Fired when an owned stream starts or stops. |
capture |
[photo: WebcamCapturePhotoResult] |
Fired after a photo is captured. |
captureError |
[error: unknown] |
Fired when a capture fails to draw or encode. |
Slots
| Slot |
Slot props |
Description |
default |
WebcamCaptureSlotState |
Video, controls, photo, and status parts. Receives the camera state. |
Exposed
| Member |
Type |
Description |
status |
WebcamCaptureStatus |
Stream lifecycle. |
error |
WebcamCaptureFailure | undefined |
Last acquisition failure, or undefined. |
external |
boolean |
Whether the stream is supplied by the consumer through the stream prop. |
facingMode |
WebcamFacingMode |
Requested facing direction. |
deviceId |
string | undefined |
Selected device id, or undefined for the facing-mode default. |
devices |
readonly WebcamCaptureDevice[] |
Known cameras (filled after permission is granted). |
mirrored |
boolean |
Whether the preview and captures are mirrored. |
countdown |
number |
Seconds left in a running shutter countdown, or 0. |
capturing |
boolean |
Whether a capture is being encoded. |
element |
HTMLDivElement | null |
Rendered root element. |
stream |
WebcamMediaStreamLike | undefined |
Current stream (owned or external), or undefined. |
photo |
WebcamCapturePhotoResult | null |
Last captured photo, or null. |
photoUrl |
string | undefined |
Object URL of the last captured photo, owned and revoked by the root. |
start |
() => Promise<WebcamMediaStreamLike | undefined> |
Acquire the camera. Resolves with the stream, or undefined on failure or external mode. |
stop |
() => void |
Stop and release an owned stream. External streams are never stopped. |
switchCamera |
() => Promise<void> |
Toggle between user and environment, restarting an active owned stream. |
selectDevice |
(deviceId: string | undefined) => Promise<void> |
Select one camera, restarting an active owned stream. |
capture |
() => Promise<WebcamCapturePhotoResult | null> |
Capture one frame from the preview. Resolves with null when no frame is available. |
clearPhoto |
() => void |
Forget the last photo and revoke its object URL. |
WebcamCaptureShutter
Source: src/families/media/webcam-capture/webcam-capture-shutter.vue
Props
| Prop |
Type |
Default |
Description |
countdown |
number |
0 |
Seconds to count down (announced through WebcamCaptureStatusMessage) before capturing. |
ariaLabel |
string |
undefined |
Accessible name for icon-only buttons. The default slot renders messages.shutter. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before capturing. Call preventDefault() to cancel. |
Slots
| Slot |
Slot props |
Description |
default |
WebcamCaptureSlotState |
Button content. Receives the camera state, including the running countdown. |
Exposed
| Member |
Type |
Description |
disabled |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
Source: src/families/media/webcam-capture/webcam-capture-start-button.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
undefined |
Accessible name for icon-only buttons. The default slot renders messages.start. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before the action. Call preventDefault() to cancel it. |
Slots
| Slot |
Slot props |
Description |
default |
WebcamCaptureSlotState |
Button content. Receives the camera state. |
Exposed
| Member |
Type |
Description |
disabled |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
WebcamCaptureStatusMessage
Source: src/families/media/webcam-capture/webcam-capture-status-message.vue
Slots
| Slot |
Slot props |
Description |
default |
WebcamCaptureSlotState & { readonly message: string } |
Announcement content. Defaults to the localized status, countdown, or capture text. |
Exposed
| Member |
Type |
Description |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
message |
— |
|
Source: src/families/media/webcam-capture/webcam-capture-stop-button.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
undefined |
Accessible name for icon-only buttons. The default slot renders messages.stop. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before the action. Call preventDefault() to cancel it. |
Slots
| Slot |
Slot props |
Description |
default |
WebcamCaptureSlotState |
Button content. Receives the camera state. |
Exposed
| Member |
Type |
Description |
disabled |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
WebcamCaptureSwitchCamera
Source: src/families/media/webcam-capture/webcam-capture-switch-camera.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
undefined |
Accessible name for icon-only buttons. The default slot renders messages.switchCamera. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before the action. Call preventDefault() to cancel it. |
Slots
| Slot |
Slot props |
Description |
default |
WebcamCaptureSlotState |
Button content. Receives the camera state. |
Exposed
| Member |
Type |
Description |
disabled |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
WebcamCaptureVideo
Source: src/families/media/webcam-capture/webcam-capture-video.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
undefined |
Accessible preview name. Defaults to messages.preview. |
Slots
| Slot |
Slot props |
Description |
default |
WebcamCaptureSlotState |
Fallback content for browsers without video support. |
Exposed
| Member |
Type |
Description |
element |
HTMLVideoElement | null |
Rendered native video element. |
Behavior
Normative state x input -> outcome table for webcam-capture-root.vue,
webcam-capture-video.vue, webcam-capture-start-button.vue,
webcam-capture-stop-button.vue, webcam-capture-switch-camera.vue,
webcam-capture-device-select.vue, webcam-capture-shutter.vue,
webcam-capture-photo.vue, and webcam-capture-status-message.vue
(@vizejs/ui/webcam-capture). Every row is proven by the named test.
The root either owns a camera stream (acquired through getUserMedia only after
start() or autoStart on mount) or displays a consumer stream passed through
stream, such as useUserMedia().stream from @vizejs/composable. Status values
and error codes are identical to the composable's MediaStreamStatus and
MediaStreamErrorCode. Consumer streams are never acquired, restarted, or stopped.
| ID |
State |
Input |
Outcome |
Evidence |
| W1 |
idle |
mount |
renders idle parts with localized defaults, a mirrored muted inline preview, a polite live region, and never requests permission |
renders an idle camera without requesting permission |
| W2 |
idle |
Start |
requests { audio, video: { ...constraints, facingMode } }, announces requesting/active, binds srcObject, and lists video inputs |
start acquires the camera, binds the preview, and lists devices |
| W3 |
requesting |
rejection / missing API |
maps errors with the composable table (permission-denied, unsupported, …), emits error, announces the localized reason, allows retry |
acquisition failures are normalized like useUserMedia |
| W4 |
active (owned) |
Stop / all tracks end / late stream |
stops owned tracks, unbinds the preview, returns to idle; a stream resolved after stop is released immediately |
stop and ended tracks release owned streams, and late streams are discarded |
| W5 |
active (owned) |
Switch camera / device select |
toggles facing mode (mirroring follows user) or selects an exact device, emits update:*, and restarts the owned stream |
switching cameras and selecting devices restart an owned stream |
| W6 |
external stream |
render / start / stop / unmount |
shows the consumer stream as active, disables owned controls, never calls getUserMedia, never stops consumer tracks |
external streams are displayed but never acquired or stopped |
| W7 |
active |
Shutter |
draws a mirrored, center-cropped, downscaled frame, encodes with captureType/captureQuality, renders the photo, revokes old URLs |
the shutter captures a mirrored, cropped frame and owns the photo URL |
| W8 |
idle / no frame |
capture |
captures nothing while idle; draw and encode failures emit captureError with typed diagnostics |
capture failures emit captureError and a missing frame captures nothing |
| W9 |
active |
Shutter with countdown |
announces each remaining second, disables the shutter, captures after the countdown; stop cancels it |
countdowns announce each second, block the shutter, and cancel on stop |
| W10 |
any |
messages |
every default label and announcement is replaced by the typed messages prop |
messages localize every default label and announcement |
| W11 |
autoStart |
mount / devicechange |
requests on mount (client only) and refreshes the device list on devicechange; mirrored overrides the facing-mode default |
autoStart requests on mount and devicechange refreshes the list |
| W12 |
active with photo |
unmount |
stops owned tracks and revokes the photo object URL |
unmounting stops owned tracks and revokes the photo URL |
| W13 |
controls |
click with preventDefault() |
leaves the camera untouched |
buttons honor preventDefault from click listeners |
| W14 |
missing provider |
setup |
parts fail closed with the shared context diagnostic |
compound parts require a matching root provider |
| W15 |
helpers |
pure functions |
error normalization, constraints, device lists, message merging, and capture geometry are deterministic |
webcam-capture-media.test.ts |
| W16 |
SSR |
isolated requests |
idle markup is byte-identical and autoStart never touches devices on the server |
renders byte-identical idle markup without requesting the camera on the server |
| W17 |
SSR / hydration |
hydrate |
server markup hydrates without warnings or node replacement |
hydrates idle camera markup without warnings or node replacement |
| W18 |
types |
compile |
statuses and codes match the composable, native MediaStream/MediaDevices are structurally accepted, options are closed |
webcam-capture.types.test-d.ts |
Mirroring is published as data-mirrored and --vize-ui-webcam-capture-scale-x
(-1 or 1); apply it with transform: scaleX(var(--vize-ui-webcam-capture-scale-x)).
Browsers only accept native MediaStreams as srcObject; the root binds the raw
(unproxied) stream and skips structural stand-ins.