Build a Viewfinder Web Component using Stencil.js
In tasks like barcode scanning and text recognition, we often need to limit the scan region so that only the desired info is extracted. In this article, we are going to build a viewfinder web component as the following which shows which area will be processed. Stencil.js is used to build such a component.
Build a Viewfinder Web Component using Stencil.js
Let’s do this in steps.
Start a New Project
Run the following command:
npm init stencil
You will be prompted to create a component project or an app project. Here, we choose component.
? Select a starter project.
Starters marked as [community] are developed by the Stencil
Community, rather than Ionic. For more information on the
Stencil Community, please see github.com/stencil-community
› - Use arrow-keys. Return to submit.
❯ component Collection of web components that can be
used anywhere
app [community] Minimal starter for building a Stencil
app or website
Generate a New Component named Viewfinder
In the project, create a new component named viewfinder.
npx stencil g view-finder
To test the component, we can modify the src\index.html
:
- <my-component first="Stencil" last="'Don't call me a framework' JS"></my-component>
+ <view-finder>
+ <p>Inner elements</p>
+ </view-finder>
Then run the following command to test it:
npm run start
Draw the Viewfinder with SVG
We are going to draw the viewfinder with SVG.
-
Define props of the viewfinder component.
@Prop() width: number; @Prop() height: number; @Prop() left: number; @Prop() top: number; @Prop() right: number; @Prop() bottom: number; @Prop() preserveAspectRatio?: string;
width
: width for theviewBox
of the SVG.height
: height for theviewBox
of the SVG.left
: left of the scan region.top
: top of the scan region.right
: right of the scan region.bottom
: bottom of the scan region.preserveAspectRatio
: thepreserveAspectRatio
attribute of the SVG.
-
Draw a mask rect which covers the whole SVG.
<svg xmlns="http://www.w3.org/2000/svg" viewBox={"0 0 "+this.width+" "+this.height}> <rect width={this.width} height={this.height} class="mask-rect"> </rect> </svg>
The style:
:host { --mask-color: rgba(0,0,0,0.7); } .mask-rect { fill:var(--mask-color); stroke-width:0; }
-
Reveal the scan region using masking. Masking is a feature of SVG that has the ability to fully or partially hide portions of an object through the use of simple or complex shapes. 1 Shapes with white color show the object while shapes with black color hide the object.
Add the following in the SVG element:
<defs> <mask id="myMask"> <rect x="0" y="0" width={this.width} height={this.height} fill="white" /> <rect x={this.left} y={this.top} width={this.right - this.left} height={this.bottom - this.top} fill="black" /> </mask> </defs>
Then use it in the previous rectangle.
<rect width={this.width} height={this.height} class="mask-rect" + mask="url(#myMask)"> </rect>
-
Draw a rect around the scan region as the border.
<rect x={this.left} y={this.top} width={this.right - this.left} height={this.bottom - this.top} class="scan-rect"/>
The style:
:host { --scan-rect-color:green; --scan-rect-stroke-width:2; } .scan-rect { stroke:var(--scan-rect-color); stroke-width:var(--scan-rect-stroke-width); fill-opacity:0.0; }
-
Draw a scan line which travels the scan region to indicate the status. The animation is done using the
animate
feature of SVG.<line x1={this.left} y1={this.top} x2={this.right} y2={this.top} class="scan-line"> <animate attributeName="y1" to={this.bottom} begin="0s" dur="2s" repeatCount="indefinite" /> <animate attributeName="y2" to={this.bottom} begin="0s" dur="2s" repeatCount="indefinite" /> </line>
The style:
:host { --scan-line-color: red; --scan-line-stroke-width:2; } .scan-line { stroke:var(--scan-line-color); stroke-width:var(--scan-line-stroke-width); }
All right, we’ve now finished writing the component.
Use the Component in a React Barcode Scanner
The component can be used with vanilla JavaScript or with frameworks. We are going to use it in a previous React barcode scanner project using Dynamsoft Barcode Reader.
-
Install the component.
Open
public/index.html
and add the following so that we can use the component:<script type="module"> import { defineCustomElements } from 'https://cdn.jsdelivr.net/npm/viewfinder-component@0.2.0/dist/esm/loader.js'; defineCustomElements(); </script>
-
Create a React wrapper for the component.
import React from 'react'; export const ViewFinder = (props) => { return ( <view-finder left={props.left} top={props.top} right={props.right} bottom={props.bottom} height={props.height} width={props.width} preserve-aspect-ratio={props.preserveAspectRatio} style={props.style} > </view-finder> ); }
-
Then, we can add the viewfinder component above the barcode scanner’s camera.
<BarcodeScanner isActive={isActive} drawOverlay={true} desiredCamera={desiredCamera} desiredResolution={desiredResolution} onScanned={onScanned} onOpened={onOpened} onClosed={onClosed} onClicked={onClicked} onDeviceListLoaded={onDeviceListLoaded} onInitialized={onInitialized} > + {((initialized && opened) && isActive) && + <ViewFinder + ref={viewFinder} + width={currentVideoWidth} + height={currentVideoHeight} + left={left} + top={top} + right={right} + bottom={bottom} + preserveAspectRatio="xMidYMid slice" + style={{"--scan-line-color":"red","--scan-rect-color":"green"}} + > + </ViewFinder> + } {((!initialized || !opened) && isActive) && <img src={loading} className="loading" alt="loading" /> } </BarcodeScanner>
-
We can take a step further to update the Dynamsoft Barcode Reader’s runtime settings so that only the scan region will be processed.
const settings = await reader.current.getRuntimeSettings(); settings.region.regionLeft = left; settings.region.regionTop = top; settings.region.regionBottom = bottom; settings.region.regionRight = right; settings.region.regionMeasuredByPercentage = 0; await reader.current.updateRuntimeSettings(settings);
You can check out the online demo to have a try.
Source Code
- The component: https://github.com/xulihang/viewfinder-web-component
- The React barcode scanner demo: https://github.com/xulihang/react-barcode-qrcode-scanner