Showing
1 changed file
with
67 additions
and
28 deletions
| @@ -35,9 +35,7 @@ class MobileScannerWeb extends MobileScannerPlatform { | @@ -35,9 +35,7 @@ class MobileScannerWeb extends MobileScannerPlatform { | ||
| 35 | StreamSubscription<Object?>? _barcodesSubscription; | 35 | StreamSubscription<Object?>? _barcodesSubscription; |
| 36 | 36 | ||
| 37 | /// The container div element for the camera view. | 37 | /// The container div element for the camera view. |
| 38 | - /// | ||
| 39 | - /// This container element is used by the barcode reader. | ||
| 40 | - HTMLDivElement? _divElement; | 38 | + late HTMLDivElement _divElement; |
| 41 | 39 | ||
| 42 | /// The flag that keeps track of whether a permission request is in progress. | 40 | /// The flag that keeps track of whether a permission request is in progress. |
| 43 | /// | 41 | /// |
| @@ -54,8 +52,14 @@ class MobileScannerWeb extends MobileScannerPlatform { | @@ -54,8 +52,14 @@ class MobileScannerWeb extends MobileScannerPlatform { | ||
| 54 | final StreamController<MediaTrackSettings> _settingsController = | 52 | final StreamController<MediaTrackSettings> _settingsController = |
| 55 | StreamController.broadcast(); | 53 | StreamController.broadcast(); |
| 56 | 54 | ||
| 57 | - /// The view type for the platform view factory. | ||
| 58 | - static const String _viewType = 'mobile-scanner-view'; | 55 | + /// The texture ID for the camera view. |
| 56 | + int _textureId = 1; | ||
| 57 | + | ||
| 58 | + /// The video element for the camera view. | ||
| 59 | + late HTMLVideoElement _videoElement; | ||
| 60 | + | ||
| 61 | + /// Get the view type for the platform view factory. | ||
| 62 | + String _getViewType(int textureId) => 'mobile-scanner-view-$textureId'; | ||
| 59 | 63 | ||
| 60 | static void registerWith(Registrar registrar) { | 64 | static void registerWith(Registrar registrar) { |
| 61 | MobileScannerPlatform.instance = MobileScannerWeb(); | 65 | MobileScannerPlatform.instance = MobileScannerWeb(); |
| @@ -72,6 +76,33 @@ class MobileScannerWeb extends MobileScannerPlatform { | @@ -72,6 +76,33 @@ class MobileScannerWeb extends MobileScannerPlatform { | ||
| 72 | Stream<double> get zoomScaleStateStream => | 76 | Stream<double> get zoomScaleStateStream => |
| 73 | _settingsController.stream.map((_) => 1.0); | 77 | _settingsController.stream.map((_) => 1.0); |
| 74 | 78 | ||
| 79 | + /// Create the [HTMLVideoElement] along with its parent container [HTMLDivElement]. | ||
| 80 | + HTMLVideoElement _createVideoElement(int textureId) { | ||
| 81 | + final HTMLVideoElement videoElement = HTMLVideoElement(); | ||
| 82 | + | ||
| 83 | + videoElement.style | ||
| 84 | + ..height = '100%' | ||
| 85 | + ..width = '100%' | ||
| 86 | + ..objectFit = 'cover' | ||
| 87 | + ..transformOrigin = 'center' | ||
| 88 | + ..pointerEvents = 'none'; | ||
| 89 | + | ||
| 90 | + // Attach the video element to its parent container | ||
| 91 | + // and setup the PlatformView factory for this `textureId`. | ||
| 92 | + _divElement = HTMLDivElement() | ||
| 93 | + ..style.objectFit = 'cover' | ||
| 94 | + ..style.height = '100%' | ||
| 95 | + ..style.width = '100%' | ||
| 96 | + ..append(videoElement); | ||
| 97 | + | ||
| 98 | + ui_web.platformViewRegistry.registerViewFactory( | ||
| 99 | + _getViewType(textureId), | ||
| 100 | + (_) => _divElement, | ||
| 101 | + ); | ||
| 102 | + | ||
| 103 | + return videoElement; | ||
| 104 | + } | ||
| 105 | + | ||
| 75 | void _handleMediaTrackSettingsChange(MediaTrackSettings settings) { | 106 | void _handleMediaTrackSettingsChange(MediaTrackSettings settings) { |
| 76 | if (_settingsController.isClosed) { | 107 | if (_settingsController.isClosed) { |
| 77 | return; | 108 | return; |
| @@ -80,6 +111,32 @@ class MobileScannerWeb extends MobileScannerPlatform { | @@ -80,6 +111,32 @@ class MobileScannerWeb extends MobileScannerPlatform { | ||
| 80 | _settingsController.add(settings); | 111 | _settingsController.add(settings); |
| 81 | } | 112 | } |
| 82 | 113 | ||
| 114 | + /// Flip the [videoElement] horizontally, | ||
| 115 | + /// if the [videoStream] indicates that is facing the user. | ||
| 116 | + void _maybeFlipVideoPreview( | ||
| 117 | + HTMLVideoElement videoElement, | ||
| 118 | + MediaStream videoStream, | ||
| 119 | + ) { | ||
| 120 | + final List<MediaStreamTrack> tracks = videoStream.getVideoTracks().toDart; | ||
| 121 | + | ||
| 122 | + if (tracks.isEmpty) { | ||
| 123 | + return; | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + final MediaStreamTrack videoTrack = tracks.first; | ||
| 127 | + final MediaTrackCapabilities capabilities = videoTrack.getCapabilities(); | ||
| 128 | + | ||
| 129 | + // TODO: this is empty on MacOS, where there is no facing mode, but one, user facing camera. | ||
| 130 | + // Facing mode is not supported by this track, do nothing. | ||
| 131 | + if (capabilities.facingMode.toDart.isEmpty) { | ||
| 132 | + return; | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + if (videoTrack.getSettings().facingMode == 'user') { | ||
| 136 | + videoElement.style.transform = 'scaleX(-1)'; | ||
| 137 | + } | ||
| 138 | + } | ||
| 139 | + | ||
| 83 | /// Prepare a [MediaStream] for the video output. | 140 | /// Prepare a [MediaStream] for the video output. |
| 84 | /// | 141 | /// |
| 85 | /// This method requests permission to use the camera. | 142 | /// This method requests permission to use the camera. |
| @@ -168,7 +225,7 @@ class MobileScannerWeb extends MobileScannerPlatform { | @@ -168,7 +225,7 @@ class MobileScannerWeb extends MobileScannerPlatform { | ||
| 168 | return const SizedBox(); | 225 | return const SizedBox(); |
| 169 | } | 226 | } |
| 170 | 227 | ||
| 171 | - return const HtmlElementView(viewType: _viewType); | 228 | + return HtmlElementView(viewType: _getViewType(_textureId)); |
| 172 | } | 229 | } |
| 173 | 230 | ||
| 174 | @override | 231 | @override |
| @@ -213,18 +270,6 @@ class MobileScannerWeb extends MobileScannerPlatform { | @@ -213,18 +270,6 @@ class MobileScannerWeb extends MobileScannerPlatform { | ||
| 213 | alternateScriptUrl: _alternateScriptUrl, | 270 | alternateScriptUrl: _alternateScriptUrl, |
| 214 | ); | 271 | ); |
| 215 | 272 | ||
| 216 | - // Setup the view factory & container element. | ||
| 217 | - if (_divElement == null) { | ||
| 218 | - _divElement = (document.createElement('div') as HTMLDivElement) | ||
| 219 | - ..style.width = '100%' | ||
| 220 | - ..style.height = '100%'; | ||
| 221 | - | ||
| 222 | - ui_web.platformViewRegistry.registerViewFactory( | ||
| 223 | - _viewType, | ||
| 224 | - (int id) => _divElement!, | ||
| 225 | - ); | ||
| 226 | - } | ||
| 227 | - | ||
| 228 | if (_barcodeReader.isScanning) { | 273 | if (_barcodeReader.isScanning) { |
| 229 | throw const MobileScannerException( | 274 | throw const MobileScannerException( |
| 230 | errorCode: MobileScannerErrorCode.controllerAlreadyInitialized, | 275 | errorCode: MobileScannerErrorCode.controllerAlreadyInitialized, |
| @@ -251,21 +296,15 @@ class MobileScannerWeb extends MobileScannerPlatform { | @@ -251,21 +296,15 @@ class MobileScannerWeb extends MobileScannerPlatform { | ||
| 251 | _handleMediaTrackSettingsChange, | 296 | _handleMediaTrackSettingsChange, |
| 252 | ); | 297 | ); |
| 253 | 298 | ||
| 254 | - final HTMLVideoElement videoElement; | 299 | + _textureId += 1; // Request a new texture. |
| 255 | 300 | ||
| 256 | - // Attach the video element to the DOM, through its parent container. | ||
| 257 | - // If a video element is already present, reuse it. | ||
| 258 | - if (_divElement!.children.length == 0) { | ||
| 259 | - videoElement = document.createElement('video') as HTMLVideoElement; | 301 | + _videoElement = _createVideoElement(_textureId); |
| 260 | 302 | ||
| 261 | - _divElement!.appendChild(videoElement); | ||
| 262 | - } else { | ||
| 263 | - videoElement = _divElement!.children.item(0)! as HTMLVideoElement; | ||
| 264 | - } | 303 | + _maybeFlipVideoPreview(_videoElement, videoStream); |
| 265 | 304 | ||
| 266 | await _barcodeReader.start( | 305 | await _barcodeReader.start( |
| 267 | startOptions, | 306 | startOptions, |
| 268 | - videoElement: videoElement, | 307 | + videoElement: _videoElement, |
| 269 | videoStream: videoStream, | 308 | videoStream: videoStream, |
| 270 | ); | 309 | ); |
| 271 | } catch (error, stackTrace) { | 310 | } catch (error, stackTrace) { |
-
Please register or login to post a comment