完成
This commit is contained in:
321
README.md
321
README.md
@@ -1,8 +1,317 @@
|
|||||||
# scan-code-jssdk
|
# scan-code-jssdk
|
||||||
桥接扫一扫/微信扫一扫/扫码枪 JSSDK
|
|
||||||
|
|
||||||
## Development
|
统一扫码 JSSDK,支持桥接扫码、微信 JSSDK 扫码、Web 摄像头扫码、选择图片识别和扫码枪输入。
|
||||||
* make sure node and npm installed;
|
|
||||||
* clone the repo to local;
|
## 功能
|
||||||
* run `npm install` to install node modules;
|
|
||||||
* run `npm run build` to get build file at `dist` folder;
|
- App 桥接环境:优先调用原生桥接 `startScan` / `stopScan`。
|
||||||
|
- 微信浏览器:初始化微信 JSSDK 后调用 `wx.scanQRCode`。
|
||||||
|
- Web 浏览器:使用 `BarcodeDetector` 识别二维码和条形码。
|
||||||
|
- 图片识别:选择本地图片后识别二维码/条形码。
|
||||||
|
- 扫码枪:监听键盘快速输入并统一走扫码监听回调。
|
||||||
|
- 监听规则:支持按 `match` 正则和 `level` 优先级分发扫码结果。
|
||||||
|
|
||||||
|
## 引入
|
||||||
|
|
||||||
|
构建后使用 `dist/index.js`:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="./dist/index.js"></script>
|
||||||
|
<script>
|
||||||
|
IScan.config().then(function () {
|
||||||
|
console.log("IScan ready");
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
如果脚本是后加载的,可以监听 `IScanReady`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function init() {
|
||||||
|
IScan.config().then(function () {
|
||||||
|
// ready
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.IScan) {
|
||||||
|
init();
|
||||||
|
} else {
|
||||||
|
window.addEventListener("IScanReady", init, { once: true });
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
```js
|
||||||
|
IScan.config({
|
||||||
|
webCanvasEnabled: true,
|
||||||
|
webScanBeepEnabled: true,
|
||||||
|
initWechatJssdk: {
|
||||||
|
apiUrl: "https://your-domain.com/wechat/jssdk-config"
|
||||||
|
}
|
||||||
|
}).then(function () {
|
||||||
|
IScan.setStatusListener(function (status) {
|
||||||
|
console.log("status:", status);
|
||||||
|
});
|
||||||
|
|
||||||
|
IScan.onScanListener(function (res) {
|
||||||
|
console.log("scan result:", res.result, res.key);
|
||||||
|
}, "scan", null, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 自动选择可用扫码方式:桥接 -> 微信 -> Web 摄像头 -> 图片识别
|
||||||
|
IScan.startScan();
|
||||||
|
|
||||||
|
// 仅打开 Web 视频扫码
|
||||||
|
IScan.scanVideo();
|
||||||
|
|
||||||
|
// 仅选择图片识别
|
||||||
|
IScan.scanImage();
|
||||||
|
|
||||||
|
// 停止当前扫码
|
||||||
|
IScan.stopScan();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 配置项
|
||||||
|
|
||||||
|
通过 `IScan.config(options)` 配置。
|
||||||
|
|
||||||
|
```ts
|
||||||
|
interface ScanConfigOptions {
|
||||||
|
bridgeEnabled?: boolean;
|
||||||
|
bridgeName?: string;
|
||||||
|
webCanvasEnabled?: boolean;
|
||||||
|
webScanCanvasStyle?: string;
|
||||||
|
webScanType?: ("qrCode" | "barCode")[];
|
||||||
|
webScanVideoMirror?: boolean;
|
||||||
|
webScanVideoMirrorVertical?: boolean;
|
||||||
|
webScanBeepAudio?: string;
|
||||||
|
webScanBeepEnabled?: boolean;
|
||||||
|
initWechatJssdk?: {
|
||||||
|
apiUrl: string;
|
||||||
|
sdkConfig?: {
|
||||||
|
debug?: boolean;
|
||||||
|
appId: string;
|
||||||
|
timestamp: number;
|
||||||
|
nonceStr: string;
|
||||||
|
signature: string;
|
||||||
|
};
|
||||||
|
sdkUrl?: string;
|
||||||
|
jsApiList?: string[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| 配置 | 说明 | 默认值 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `bridgeEnabled` | 是否启用桥接扫码 | `true` |
|
||||||
|
| `bridgeName` | 挂载在 `window` 上的桥接对象名称 | `__bridge_client__` |
|
||||||
|
| `webCanvasEnabled` | 是否显示 Web 扫码 canvas;关闭后仍会用隐藏 canvas 识别 | `true` |
|
||||||
|
| `webScanCanvasStyle` | Web 扫码 canvas 样式 | `position: fixed; width: 300px; height: 300px; top: 0; left: 0; z-index: 9999;` |
|
||||||
|
| `webScanType` | Web 扫码类型 | `["qrCode", "barCode"]` |
|
||||||
|
| `webScanVideoMirror` | Web 视频是否水平镜像;不配置时自动判断:前置/PC 镜像,后置不镜像 | 自动 |
|
||||||
|
| `webScanVideoMirrorVertical` | Web 视频是否垂直镜像 | `false` |
|
||||||
|
| `webScanBeepAudio` | Web 扫码成功提示音地址 | 内置提示音 |
|
||||||
|
| `webScanBeepEnabled` | Web 扫码成功是否播放提示音 | `true` |
|
||||||
|
| `initWechatJssdk` | 微信 JSSDK 初始化配置,仅微信环境生效 | 无 |
|
||||||
|
|
||||||
|
## 桥接接入
|
||||||
|
|
||||||
|
桥接对象需要挂载到 `window[bridgeName]`,并实现 `call(method, data)`。
|
||||||
|
|
||||||
|
SDK 会调用:
|
||||||
|
|
||||||
|
- `startScan`
|
||||||
|
- `stopScan`
|
||||||
|
|
||||||
|
异步回调方法名为 `${bridgeName}_handle_callback`。
|
||||||
|
|
||||||
|
```js
|
||||||
|
window.__bridge_client__ = {
|
||||||
|
call: function (method, data) {
|
||||||
|
var requestId = data.request_id;
|
||||||
|
|
||||||
|
if (method === "startScan") {
|
||||||
|
// 调用原生扫码后回调
|
||||||
|
window.__bridge_client___handle_callback({
|
||||||
|
code: 0,
|
||||||
|
method: method,
|
||||||
|
request_id: requestId,
|
||||||
|
payload: {
|
||||||
|
result: "https://example.com"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method === "stopScan") {
|
||||||
|
window.__bridge_client___handle_callback({
|
||||||
|
code: 0,
|
||||||
|
method: method,
|
||||||
|
request_id: requestId,
|
||||||
|
payload: {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 微信 JSSDK 接入
|
||||||
|
|
||||||
|
配置接口方式:
|
||||||
|
|
||||||
|
```js
|
||||||
|
IScan.config({
|
||||||
|
initWechatJssdk: {
|
||||||
|
apiUrl: "https://your-domain.com/wechat/jssdk-config"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
接口会收到当前页面 URL 参数:`url=location.href.split("#")[0]`。
|
||||||
|
|
||||||
|
也可以直接传入微信签名配置:
|
||||||
|
|
||||||
|
```js
|
||||||
|
IScan.config({
|
||||||
|
initWechatJssdk: {
|
||||||
|
apiUrl: "",
|
||||||
|
sdkConfig: {
|
||||||
|
appId: "wx_app_id",
|
||||||
|
timestamp: 123456,
|
||||||
|
nonceStr: "nonce",
|
||||||
|
signature: "signature"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
`jsApiList` 会默认追加 `scanQRCode`。
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### `config(options?): Promise<any>`
|
||||||
|
|
||||||
|
配置并初始化 SDK。
|
||||||
|
|
||||||
|
```js
|
||||||
|
IScan.config({
|
||||||
|
webCanvasEnabled: true
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### `setStatusListener(callback): void`
|
||||||
|
|
||||||
|
监听扫码状态,状态为:
|
||||||
|
|
||||||
|
- `scanning`
|
||||||
|
- `closed`
|
||||||
|
|
||||||
|
```js
|
||||||
|
IScan.setStatusListener(function (status) {
|
||||||
|
console.log(status);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### `onScanListener(callback, key, match?, level?): ScanListenerInfo`
|
||||||
|
|
||||||
|
添加扫码结果监听。
|
||||||
|
|
||||||
|
```js
|
||||||
|
var listener = IScan.onScanListener(function (res) {
|
||||||
|
console.log(res.result, res.key);
|
||||||
|
}, "order", "^https://", 100);
|
||||||
|
```
|
||||||
|
|
||||||
|
参数说明:
|
||||||
|
|
||||||
|
- `callback`:扫码结果回调。
|
||||||
|
- `key`:监听 key,同 key 会覆盖旧监听。
|
||||||
|
- `match`:可选正则字符串;扫码结果匹配后才回调。
|
||||||
|
- `level`:优先级,数值越大越先匹配。
|
||||||
|
|
||||||
|
### `offScanListener(callbackOrKey): void`
|
||||||
|
|
||||||
|
移除监听,可传 callback 或 key。
|
||||||
|
|
||||||
|
```js
|
||||||
|
IScan.offScanListener("order");
|
||||||
|
listener.cancel();
|
||||||
|
```
|
||||||
|
|
||||||
|
### `getStatus(): "scanning" | "closed"`
|
||||||
|
|
||||||
|
获取当前扫码状态。
|
||||||
|
|
||||||
|
```js
|
||||||
|
console.log(IScan.getStatus());
|
||||||
|
```
|
||||||
|
|
||||||
|
### `startScan(): void`
|
||||||
|
|
||||||
|
开启扫码。SDK 会按以下顺序选择可用能力:
|
||||||
|
|
||||||
|
1. 桥接扫码
|
||||||
|
2. 微信扫码
|
||||||
|
3. Web 摄像头扫码
|
||||||
|
4. 图片识别
|
||||||
|
|
||||||
|
扫码结果通过 `onScanListener` 回调。
|
||||||
|
|
||||||
|
```js
|
||||||
|
IScan.startScan();
|
||||||
|
```
|
||||||
|
|
||||||
|
### `scanVideo(): void`
|
||||||
|
|
||||||
|
直接开启 Web 摄像头扫码。扫码结果通过 `onScanListener` 回调。
|
||||||
|
|
||||||
|
```js
|
||||||
|
IScan.scanVideo();
|
||||||
|
```
|
||||||
|
|
||||||
|
### `scanImage(): void`
|
||||||
|
|
||||||
|
直接选择图片进行识别。识别结果通过 `onScanListener` 回调。
|
||||||
|
|
||||||
|
```js
|
||||||
|
IScan.scanImage();
|
||||||
|
```
|
||||||
|
|
||||||
|
### `stopScan(): void`
|
||||||
|
|
||||||
|
停止当前扫码。
|
||||||
|
|
||||||
|
```js
|
||||||
|
IScan.stopScan();
|
||||||
|
```
|
||||||
|
|
||||||
|
### `clear(): void`
|
||||||
|
|
||||||
|
清空全部扫码监听。
|
||||||
|
|
||||||
|
```js
|
||||||
|
IScan.clear();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 类型
|
||||||
|
|
||||||
|
```ts
|
||||||
|
interface ScanResult {
|
||||||
|
result: string;
|
||||||
|
key: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScanStatus = "scanning" | "closed";
|
||||||
|
|
||||||
|
type ScanResultCallback = (result: ScanResult) => any;
|
||||||
|
type ScanStatusCallback = (status: ScanStatus) => any;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 开发
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
构建产物输出到 `dist` 目录。
|
||||||
|
|||||||
27
dist/index.d.ts
vendored
27
dist/index.d.ts
vendored
@@ -2,6 +2,33 @@
|
|||||||
* 扫码初始化选项
|
* 扫码初始化选项
|
||||||
*/
|
*/
|
||||||
interface ScanConfigOptions {
|
interface ScanConfigOptions {
|
||||||
|
/**
|
||||||
|
* 桥接是否启用,默认启用
|
||||||
|
*/
|
||||||
|
bridgeEnabled?: boolean,
|
||||||
|
/**
|
||||||
|
* 桥接名称,默认:__bridge_client__
|
||||||
|
* 桥接需实现call方法,并暴露在window上,并通过${bridgeName}_handle_callback方法处理回调
|
||||||
|
* 示例:
|
||||||
|
* window.${bridgeName} = {
|
||||||
|
* call: function (method, { data, request_id }): any {
|
||||||
|
* // 处理请求
|
||||||
|
* // 异步返回结果
|
||||||
|
* ${bridgeName}_handle_callback({
|
||||||
|
* code: 0, // 0成功,其他失败
|
||||||
|
* method, // 方法名
|
||||||
|
* payload: result, // 返回结果
|
||||||
|
* request_id // 请求id
|
||||||
|
* });
|
||||||
|
* // 同步返回结果
|
||||||
|
* return result;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* 需要实现以下方法:
|
||||||
|
* 1. 发起扫码的方法名称为:startScan
|
||||||
|
* 2. 结束扫码的方法名称为:stopScan
|
||||||
|
*/
|
||||||
|
bridgeName?: string,
|
||||||
/**
|
/**
|
||||||
* 网页扫码canvas是否启用,默认启用
|
* 网页扫码canvas是否启用,默认启用
|
||||||
*/
|
*/
|
||||||
|
|||||||
2
dist/index.js
vendored
2
dist/index.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,7 +1,9 @@
|
|||||||
import './polyfill';
|
import './polyfill';
|
||||||
|
import {
|
||||||
|
onScanListener, offScanListener, setStatusListener, getStatus,
|
||||||
|
startScan, stopScan, scanVideo, scanImage, clear
|
||||||
|
} from './services/provider/scan';
|
||||||
import { setConfig, getVersion } from './services/config';
|
import { setConfig, getVersion } from './services/config';
|
||||||
import { onScanListener, offScanListener, setStatusListener, getStatus,
|
|
||||||
startScan, stopScan, scanVideo, scanImage, clear } from './services/provider/scan';
|
|
||||||
import { initWxJssdk } from './services/wx';
|
import { initWxJssdk } from './services/wx';
|
||||||
import { printDebug } from './utils/logger';
|
import { printDebug } from './utils/logger';
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ let _events = {};
|
|||||||
let _callbacks = {};
|
let _callbacks = {};
|
||||||
let _bridge = "__bridge_client__";
|
let _bridge = "__bridge_client__";
|
||||||
|
|
||||||
|
function getBridgeName() {
|
||||||
|
return getConfig("bridgeName") ? getConfig("bridgeName") : _bridge;
|
||||||
|
}
|
||||||
|
|
||||||
function _callRuntime(func, ...options) {
|
function _callRuntime(func, ...options) {
|
||||||
let funcs = func.split('.');
|
let funcs = func.split('.');
|
||||||
let instant = window;
|
let instant = window;
|
||||||
@@ -26,7 +30,7 @@ function onCallback(name, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _checkInit() {
|
function _checkInit() {
|
||||||
let methodName = `${_bridge}_handle_callback`;
|
let methodName = `${getBridgeName()}_handle_callback`;
|
||||||
if (!window[methodName]) {
|
if (!window[methodName]) {
|
||||||
window[methodName] = (res) => {
|
window[methodName] = (res) => {
|
||||||
let { method, payload, code, request_id } = toAny(res, {});
|
let { method, payload, code, request_id } = toAny(res, {});
|
||||||
@@ -43,7 +47,8 @@ function _checkInit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function inRuntime() {
|
export function inRuntime() {
|
||||||
return !!window[_bridge]
|
return !!window[getBridgeName()]
|
||||||
|
&& getConfig("bridgeEnabled") !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onEvent(method, callback) {
|
export function onEvent(method, callback) {
|
||||||
@@ -70,7 +75,7 @@ export function offEvent(method, callback) {
|
|||||||
|
|
||||||
export function bridgeSync(method, data) {
|
export function bridgeSync(method, data) {
|
||||||
_checkInit();
|
_checkInit();
|
||||||
let result = toAny(_callRuntime(`${_bridge}.call`, method, toAny(data, "")));
|
let result = toAny(_callRuntime(`${getBridgeName()}.call`, method, toAny(data, "")));
|
||||||
let options = [method];
|
let options = [method];
|
||||||
if (data) {
|
if (data) {
|
||||||
options.push("params:")
|
options.push("params:")
|
||||||
|
|||||||
@@ -224,7 +224,8 @@ export function isSupportWebScan() {
|
|||||||
return typeof navigator !== 'undefined'
|
return typeof navigator !== 'undefined'
|
||||||
&& navigator.mediaDevices
|
&& navigator.mediaDevices
|
||||||
&& navigator.mediaDevices.getUserMedia
|
&& navigator.mediaDevices.getUserMedia
|
||||||
&& !!getBarcodeDetectorClass();
|
&& !!getBarcodeDetectorClass()
|
||||||
|
&& getConfig("webCanvasEnabled") !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isSupportImageScan() {
|
export function isSupportImageScan() {
|
||||||
|
|||||||
27
types/index.d.ts
vendored
27
types/index.d.ts
vendored
@@ -2,6 +2,33 @@
|
|||||||
* 扫码初始化选项
|
* 扫码初始化选项
|
||||||
*/
|
*/
|
||||||
interface ScanConfigOptions {
|
interface ScanConfigOptions {
|
||||||
|
/**
|
||||||
|
* 桥接是否启用,默认启用
|
||||||
|
*/
|
||||||
|
bridgeEnabled?: boolean,
|
||||||
|
/**
|
||||||
|
* 桥接名称,默认:__bridge_client__
|
||||||
|
* 桥接需实现call方法,并暴露在window上,并通过${bridgeName}_handle_callback方法处理回调
|
||||||
|
* 示例:
|
||||||
|
* window.${bridgeName} = {
|
||||||
|
* call: function (method, { data, request_id }): any {
|
||||||
|
* // 处理请求
|
||||||
|
* // 异步返回结果
|
||||||
|
* ${bridgeName}_handle_callback({
|
||||||
|
* code: 0, // 0成功,其他失败
|
||||||
|
* method, // 方法名
|
||||||
|
* payload: result, // 返回结果
|
||||||
|
* request_id // 请求id
|
||||||
|
* });
|
||||||
|
* // 同步返回结果
|
||||||
|
* return result;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* 需要实现以下方法:
|
||||||
|
* 1. 发起扫码的方法名称为:startScan
|
||||||
|
* 2. 结束扫码的方法名称为:stopScan
|
||||||
|
*/
|
||||||
|
bridgeName?: string,
|
||||||
/**
|
/**
|
||||||
* 网页扫码canvas是否启用,默认启用
|
* 网页扫码canvas是否启用,默认启用
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user