743 lines
23 KiB
Plaintext
743 lines
23 KiB
Plaintext
import { AVCaptureSession, AVCaptureDevice, AVMediaType, AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureDeviceInput, AVCaptureInput, AVCaptureVideoDataOutput, AVCaptureConnection, AVCaptureVideoPreviewLayer, AVLayerVideoGravity, AVCaptureVideoOrientation } from 'AVFoundation';
|
|
import { PHPhotoLibrary, PHAuthorizationStatus } from 'Photos';
|
|
import { CMSampleBuffer, CMSampleBufferGetFormatDescription, CMFormatDescription, CMVideoFormatDescriptionGetDimensions } from 'CoreMedia';
|
|
import { DispatchQueue } from "Dispatch"
|
|
import { UIView, UIImage, UIApplication, UIImageView, UILabel, UIViewController, UIFontDescriptor, UIFont, NSTextAlignment, UITapGestureRecognizer, UIImagePickerController, UIImagePickerControllerDelegate, UINavigationControllerDelegate } from 'UIKit';
|
|
import { CGRect, CGFloat, CGPoint } from 'CoreFoundation';
|
|
import { Barcode, BarcodeScannerOptions, BarcodeFormat, BarcodeScanner, CMVideoDimensions } from "MLKitBarcodeScanning"
|
|
import { VisionImage } from "MLKit"
|
|
import { SCANNING_PHOTO_RESULT, SCANNING_PHOTO_RESULT_BOUND, CALL_RESULT_FUN, AUTH_CALL_BACK_TYPE } from '../interface.uts';
|
|
|
|
import { NotificationCenter, NSNotification, Notification } from 'Foundation';
|
|
import { Selector } from 'ObjectiveC';
|
|
import { Alignment } from 'ARKit';
|
|
import { CABasicAnimation } from 'QuartzCore';
|
|
|
|
type ConverXy = {
|
|
x : number,
|
|
y : number,
|
|
minx : number,
|
|
maxx : number,
|
|
miny : number,
|
|
maxy : number,
|
|
}
|
|
type contaiaSize = {
|
|
width : number,
|
|
height : number
|
|
}
|
|
type contaiaCalseSize = {
|
|
scale : number,
|
|
offsetX : number,
|
|
offsetY : number
|
|
}
|
|
type contaiPointXy = {
|
|
x : number,
|
|
y : number
|
|
}
|
|
// 资料参考:
|
|
// https://github.com/JimmyPun610/BarcodeScanner.Mobile/blob/master/BarcodeScanner.Mobile.Maui/Platforms/iOS/CaptureVideoDelegate.cs
|
|
// 识别结果
|
|
let scanResult = [] as Swift.Array<Barcode>
|
|
let captureSession : null | AVCaptureSession = null;
|
|
|
|
let captureDeviceAllGlobal : null | AVCaptureDevice = null;
|
|
let FlashButton:null|UILabel = null;
|
|
let flashModel = false;
|
|
// 视频宽
|
|
let videoWidth = 0
|
|
// 视频高
|
|
let videoHeight = 0
|
|
// 图像捕捉事件代理。
|
|
let captureOutDelegate : CaptureOutSessionBuffer | null = null;
|
|
let pointsEvents = [] as PointClickEvent[]
|
|
let isCamreaing = false;
|
|
|
|
let pointView = [] as UIView[]
|
|
|
|
let onlyCamera : boolean = false;
|
|
|
|
let barcodeOptions = BarcodeScannerOptions(formats = BarcodeFormat.all)
|
|
let parentView : UIView | null = null
|
|
let camreaViewLayer : UIView | null = null
|
|
let pointsLayerView : UIView | null = null
|
|
let imageUiView : UIImageView | null = null
|
|
let cameraView : AVCaptureVideoPreviewLayer | null = null
|
|
|
|
let backevents : BackEvents | null = null;
|
|
let photosevents : PhotoSelectedEvents | null = null;
|
|
let scanevents : ScanEvents | null = null;
|
|
let flashBtnevents:FlashBtnEvents|null = null;
|
|
let rsultCallBack : CALL_RESULT_FUN = (str : string) => { }
|
|
let delegate : null | useImagePickerDelegate = null;
|
|
let openAnimation = true;
|
|
/** 加上这个标签可以持续响应事件,回调执行时必要的标签. */
|
|
@MainActor
|
|
class useImagePickerDelegate implements UIImagePickerControllerDelegate, UINavigationControllerDelegate {
|
|
call : (image : any | null) => void;
|
|
constructor(call : (image : any | null) => void) {
|
|
this.call = call;
|
|
}
|
|
imagePickerController(controller : UIImagePickerController, @argumentLabel("didFinishPickingMediaWithInfo") info : Map<UIImagePickerController.InfoKey, any>) {
|
|
let image = info.get(UIImagePickerController.InfoKey.originalImage)
|
|
this.call(image)
|
|
controller.dismiss(animated = true, completion = null)
|
|
}
|
|
imagePickerControllerDidCancel(controller : UIImagePickerController) {
|
|
this.call(null)
|
|
controller.dismiss(animated = true, completion = null)
|
|
}
|
|
}
|
|
|
|
|
|
class PointClickEvent {
|
|
private callback : (txt : string) => void
|
|
private text : string;
|
|
constructor(callback : (txt : string) => void, atext : string) {
|
|
this.callback = callback;
|
|
this.text = atext;
|
|
super.init()
|
|
}
|
|
|
|
@objc onclick() {
|
|
|
|
this.callback(this.text)
|
|
}
|
|
}
|
|
|
|
class BackEvents {
|
|
private callback : () => void
|
|
constructor(callback : () => void) {
|
|
this.callback = callback
|
|
super.init()
|
|
}
|
|
@objc onclick() {
|
|
|
|
this.callback()
|
|
}
|
|
}
|
|
class PhotoSelectedEvents {
|
|
private callback : () => void
|
|
constructor(callback : () => void) {
|
|
this.callback = callback
|
|
super.init()
|
|
}
|
|
@objc onclick() {
|
|
|
|
this.callback()
|
|
}
|
|
}
|
|
class ScanEvents {
|
|
private callback : () => void
|
|
constructor(callback : () => void) {
|
|
this.callback = callback
|
|
super.init()
|
|
}
|
|
@objc onclick() {
|
|
|
|
this.callback()
|
|
}
|
|
}
|
|
|
|
class FlashBtnEvents {
|
|
private callback : () => void
|
|
constructor(callback : () => void) {
|
|
this.callback = callback
|
|
super.init()
|
|
}
|
|
@objc onclick() {
|
|
this.callback()
|
|
}
|
|
}
|
|
|
|
class CaptureOutSessionBuffer implements AVCaptureVideoDataOutputSampleBufferDelegate {
|
|
callback : (str : SCANNING_PHOTO_RESULT) => void = (str : SCANNING_PHOTO_RESULT) => { }
|
|
constructor(dv : (str : SCANNING_PHOTO_RESULT) => void) {
|
|
this.callback = dv;
|
|
super()
|
|
}
|
|
captureOutput(output : AVCaptureOutput, @argumentLabel("didOutput") sampleBuffer : CMSampleBuffer, @argumentLabel("from") connection : AVCaptureConnection) {
|
|
if (!isCamreaing) return;
|
|
// console.log('图片捕捉正确')
|
|
|
|
let image = new VisionImage(buffer = sampleBuffer)
|
|
// 这里会直接影响到测量的起始点。下面是镜像,测量是图片左上为起始,但设备是竖向,因此计算时的x,y坐标其实是相对屏幕向右转下,x,y是反向的,因为是镜像
|
|
// 所以计算位置时,x,y是反的,宽和高也是相反的。
|
|
image.orientation = UIImage.Orientation.right
|
|
let barcodeOptions = BarcodeScannerOptions(formats = BarcodeFormat.all)
|
|
// options=barcodeOptions
|
|
let barcodeScanner = new BarcodeScanner.barcodeScanner(options = barcodeOptions)
|
|
|
|
barcodeScanner.process(image, completion = (features : Swift.Array<Barcode> | null, error) => {
|
|
|
|
if (features == null || error != null) {
|
|
return;
|
|
}
|
|
if (features!.length == 0 || scanResult.length > 0) {
|
|
return;
|
|
}
|
|
|
|
// 暂停识别。
|
|
captureSession!.stopRunning()
|
|
|
|
scanResult = features!
|
|
let descVideo : CMFormatDescription | null = CMSampleBufferGetFormatDescription(sampleBuffer);
|
|
if (descVideo != null) {
|
|
let dimesionsize : CMVideoDimensions = CMVideoFormatDescriptionGetDimensions(descVideo!)
|
|
videoWidth = Number(dimesionsize.width)
|
|
videoHeight = Number(dimesionsize.height)
|
|
}
|
|
|
|
|
|
let listpos = [] as number[][][]
|
|
let bounedlist = [] as SCANNING_PHOTO_RESULT_BOUND[]
|
|
let rulstText = [] as string[]
|
|
|
|
for (let i = 0; i < features!.length; i++) {
|
|
let barcode = features![i]
|
|
// 位置信息
|
|
let bounds = barcode.frame
|
|
// 坐标
|
|
let corners = barcode.cornerPoints
|
|
// 解析的内容
|
|
let rawValue = barcode.rawValue
|
|
let iminfo = {
|
|
width: Number(bounds.width),
|
|
height: Number(bounds.height),
|
|
centerX: Number(bounds.midX),
|
|
centerY: Number(bounds.midY)
|
|
} as SCANNING_PHOTO_RESULT_BOUND
|
|
let posinfo = [] as number[][]
|
|
for (pos in corners!) {
|
|
// pos是pointValue类型并返回CGPoint,其中有x,y
|
|
let ponit = pos.cgPointValue
|
|
posinfo.push([Number(ponit.x), Number(ponit.y)] as number[])
|
|
}
|
|
listpos.push(posinfo)
|
|
bounedlist.push(iminfo)
|
|
rulstText.push(rawValue == null ? '' : (rawValue!))
|
|
|
|
}
|
|
let res = {
|
|
url: '',
|
|
position: listpos,
|
|
bounds: bounedlist,
|
|
text: rulstText,
|
|
imgWidth:0,
|
|
imgHeight:0
|
|
} as SCANNING_PHOTO_RESULT
|
|
|
|
this.callback(res)
|
|
})
|
|
|
|
}
|
|
}
|
|
// 检查权限
|
|
export function isCheckPermissions(callFun : null | AUTH_CALL_BACK_TYPE) {
|
|
let realcall : AUTH_CALL_BACK_TYPE = (_ : boolean) => { };
|
|
if (callFun != null) {
|
|
let cllfunFun = callFun!;
|
|
realcall = cllfunFun
|
|
}
|
|
if (!onlyCamera) {
|
|
PHPhotoLibrary.requestAuthorization((isok : PHAuthorizationStatus) => {
|
|
// 拒绝了相册选取权限。
|
|
if (isok != PHAuthorizationStatus.authorized) {
|
|
realcall(false)
|
|
return;
|
|
}
|
|
AVCaptureDevice.requestAccess(for = AVMediaType.video, completionHandler = (isok2 : boolean) => {
|
|
if (!isok2) {
|
|
realcall(false)
|
|
return;
|
|
}
|
|
realcall(true)
|
|
})
|
|
})
|
|
} else {
|
|
AVCaptureDevice.requestAccess(for = AVMediaType.video, completionHandler = (isok2 : boolean) => {
|
|
if (!isok2) {
|
|
realcall(false)
|
|
return;
|
|
}
|
|
realcall(true)
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function xCamreaInit() {
|
|
let t = this;
|
|
let sys = uni.getSystemInfoSync();
|
|
if (parentView != null) {
|
|
parentView!.removeFromSuperview()
|
|
}
|
|
parentView = new UIView();
|
|
let scaleAni = new CABasicAnimation(keyPath = "transform.scale")
|
|
scaleAni.fromValue = 0
|
|
scaleAni.toValue = 1
|
|
scaleAni.duration = 0.25
|
|
|
|
|
|
parentView!.frame = new CGRect(x = 0, y = 0, width = (sys.windowWidth).toInt(), height = (sys.screenHeight).toInt())
|
|
parentView!.backgroundColor = UTSiOS.colorWithString("#000000")
|
|
|
|
let parent = UTSiOS.getCurrentViewController() as UIViewController;
|
|
let windUiview = parent.view as UIView
|
|
windUiview.addSubview(parentView!)
|
|
|
|
|
|
|
|
let safeTop = sys.statusBarHeight + 50
|
|
let safeHeight = sys.screenHeight - 100 - sys.statusBarHeight - 50
|
|
|
|
// 绘制界面
|
|
|
|
// 创建一张图片预览层。
|
|
imageUiView = new UIImageView();
|
|
imageUiView!.frame = new CGRect(x = 0, y = safeTop.toInt(), width = (sys.windowWidth).toInt(), height = (safeHeight).toInt())
|
|
imageUiView!.isHidden = true;
|
|
|
|
parentView!.addSubview(imageUiView!);
|
|
|
|
// 创建小绿点层.
|
|
pointsLayerView = new UIView()
|
|
pointsLayerView!.frame = new CGRect(x = 0, y = safeTop.toInt(), width = (sys.windowWidth).toInt(), height = (safeHeight).toInt())
|
|
|
|
// 创建视频预览层。
|
|
camreaViewLayer = new UIView()
|
|
camreaViewLayer!.frame = new CGRect(x = 0, y = safeTop.toInt(), width = (sys.windowWidth).toInt(), height = (safeHeight).toInt())
|
|
parentView!.addSubview(camreaViewLayer!);
|
|
|
|
cameraView = new AVCaptureVideoPreviewLayer(session = captureSession!)
|
|
// 设置预览层绑定
|
|
// 下面的缩放会居中显示,因此左顶点可能在左上方
|
|
cameraView!.videoGravity = AVLayerVideoGravity.resizeAspectFill
|
|
cameraView!.connection!.videoOrientation = AVCaptureVideoOrientation.portrait
|
|
cameraView!.frame = new CGRect(x = 0, y = 0, width = (sys.windowWidth).toInt(), height = (safeHeight).toInt())
|
|
|
|
DispatchQueue.main.async(execute = () : void => {
|
|
// 绑定渲染层。
|
|
camreaViewLayer!.layer.addSublayer(cameraView!)
|
|
captureSession!.startRunning()
|
|
|
|
|
|
// 创建一个上遮盖板。
|
|
// let toplayer = new UIView()
|
|
// toplayer.frame = new CGRect(x = 0, y = 0, width = (sys.windowWidth).toInt(), height = 100)
|
|
// toplayer.backgroundColor = UTSiOS.colorWithString("rgba(255,0,0,0)")
|
|
// parentView!.addSubview(toplayer);
|
|
|
|
// 创建一个下遮盖板。
|
|
let bottomlayer = new UIView()
|
|
bottomlayer.frame = new CGRect(x = 0, y = 0, width = (sys.windowWidth).toInt(), height = 100)
|
|
bottomlayer.backgroundColor = UTSiOS.colorWithString("#000000")
|
|
parentView!.addSubview(bottomlayer);
|
|
|
|
// 创建一个返回按钮文本。
|
|
let backButton = new UILabel()
|
|
backButton.frame = new CGRect(x = 16, y = 58, width = (sys.windowWidth - 16).toInt(), height = 32)
|
|
backButton.text = String("返回")
|
|
backButton.font = UIFont.systemFont(ofSize = new CGFloat(16))
|
|
backButton.textColor = UTSiOS.colorWithString("#FFFFFF")
|
|
|
|
parentView!.addSubview(backButton);
|
|
|
|
// 创建一个相册选取按钮
|
|
let photoBtn = new UILabel()
|
|
photoBtn.frame = new CGRect(x = 16, y = (sys.screenHeight - 88).toInt(), width = 80, height = 32)
|
|
photoBtn.text = String(onlyCamera ? "" : "相册选取")
|
|
|
|
photoBtn.font = UIFont.systemFont(ofSize = new CGFloat(16))
|
|
photoBtn.textColor = UTSiOS.colorWithString("#FFFFFF")
|
|
parentView!.addSubview(photoBtn);
|
|
|
|
// 创建个闪光灯打开和关闭的文本层.
|
|
let flashBtn = new UILabel()
|
|
flashBtn.frame = new CGRect(x = ((sys.windowWidth - 80)/2).toInt(), y = (sys.screenHeight - 88).toInt(), width = 80, height = 32)
|
|
flashBtn.text = String("开灯")
|
|
flashBtn.font = UIFont.systemFont(ofSize = new CGFloat(16))
|
|
flashBtn.textAlignment = NSTextAlignment.center
|
|
flashBtn.textColor = UTSiOS.colorWithString("#FFFFFF")
|
|
parentView!.addSubview(flashBtn);
|
|
FlashButton = flashBtn;
|
|
|
|
|
|
// 创建一个继续扫一扫的按钮
|
|
let scanBtn = new UILabel()
|
|
scanBtn.frame = new CGRect(x = (sys.windowWidth - 96).toInt(), y = (sys.screenHeight - 88).toInt(), width = 80, height = 32)
|
|
|
|
scanBtn.text = String("扫一扫")
|
|
scanBtn.font = UIFont.systemFont(ofSize = new CGFloat(16))
|
|
scanBtn.textAlignment = NSTextAlignment.right
|
|
scanBtn.textColor = UTSiOS.colorWithString("#FFFFFF")
|
|
parentView!.addSubview(scanBtn);
|
|
|
|
|
|
// 最高层级.
|
|
parentView!.addSubview(pointsLayerView!);
|
|
|
|
|
|
// 添加事件。
|
|
// 返回,关闭弹层。
|
|
backevents = new BackEvents(() => {
|
|
closeScan();
|
|
quitView();
|
|
})
|
|
// 点击了相册选取。
|
|
function setToXiangChe() {
|
|
if (onlyCamera) {
|
|
return;
|
|
}
|
|
let pic = new UIImagePickerController()
|
|
DispatchQueue.main.async(execute = () : void => {
|
|
delegate = new useImagePickerDelegate((image : any | null) => {
|
|
if (image != null) {
|
|
let realimageds = image! as UIImage;
|
|
closeScan();
|
|
imageUiView!.image = realimageds
|
|
imageUiView!.contentMode = UIView.ContentMode.scaleAspectFit
|
|
camreaViewLayer!.isHidden = true;
|
|
imageUiView!.isHidden = false;
|
|
|
|
// 图片识别码.
|
|
let visionImage = new VisionImage(image = realimageds)
|
|
visionImage.orientation = realimageds.imageOrientation
|
|
|
|
let barcodeOptions = BarcodeScannerOptions(formats = BarcodeFormat.all)
|
|
let barcodeScanner = new BarcodeScanner.barcodeScanner(options = barcodeOptions)
|
|
barcodeScanner.process(visionImage, completion = (features : Swift.Array<Barcode> | null, error) => {
|
|
|
|
if (features == null || error != null) {
|
|
return;
|
|
}
|
|
if (features!.length == 0) {
|
|
return;
|
|
}
|
|
|
|
scanResult = features!
|
|
|
|
videoWidth = Number(realimageds.size.width);
|
|
videoHeight = Number(realimageds.size.height);
|
|
|
|
let listpos = [] as number[][][]
|
|
let bounedlist = [] as SCANNING_PHOTO_RESULT_BOUND[]
|
|
let rulstText = [] as string[]
|
|
|
|
for (let i = 0; i < features!.length; i++) {
|
|
let barcode = features![i]
|
|
// 位置信息
|
|
let bounds = barcode.frame
|
|
// 坐标
|
|
let corners = barcode.cornerPoints
|
|
// 解析的内容
|
|
let rawValue = barcode.rawValue
|
|
let iminfo = {
|
|
width: Number(bounds.width),
|
|
height: Number(bounds.height),
|
|
centerX: Number(bounds.midX),
|
|
centerY: Number(bounds.midY)
|
|
} as SCANNING_PHOTO_RESULT_BOUND
|
|
let posinfo = [] as number[][]
|
|
for (pos in corners!) {
|
|
// pos是pointValue类型并返回CGPoint,其中有x,y
|
|
let ponit = pos.cgPointValue
|
|
posinfo.push([Number(ponit.x), Number(ponit.y)] as number[])
|
|
}
|
|
listpos.push(posinfo)
|
|
bounedlist.push(iminfo)
|
|
rulstText.push(rawValue == null ? '' : (rawValue!))
|
|
|
|
}
|
|
let result = {
|
|
url: '',
|
|
position: listpos,
|
|
bounds: bounedlist,
|
|
text: rulstText,
|
|
imgWidth:0,
|
|
imgHeight:0
|
|
} as SCANNING_PHOTO_RESULT
|
|
let deg = 0;
|
|
if (realimageds.imageOrientation == UIImage.Orientation.right) {
|
|
deg = 90
|
|
} else if (realimageds.imageOrientation == UIImage.Orientation.up) {
|
|
deg = 0
|
|
} else if (realimageds.imageOrientation == UIImage.Orientation.left) {
|
|
deg = 270
|
|
} else if (realimageds.imageOrientation == UIImage.Orientation.down) {
|
|
deg = 180
|
|
}
|
|
|
|
console.log(deg)
|
|
createPointView(result, deg)
|
|
uni.setStorageSync("xMlKITScaning", JSON.stringify(result.text))
|
|
// t.$emit("scan")
|
|
if (result.text.length == 1) {
|
|
rsultCallBack(result.text[0])
|
|
closeScan()
|
|
quitView()
|
|
}
|
|
|
|
})
|
|
}
|
|
});
|
|
pic.sourceType = UIImagePickerController.SourceType.photoLibrary
|
|
pic.mediaTypes = ["public.image"]
|
|
pic.delegate = delegate!
|
|
UTSiOS.getCurrentViewController().present(pic, animated = true, completion = nil)
|
|
})
|
|
|
|
|
|
}
|
|
photosevents = new PhotoSelectedEvents(() => {
|
|
|
|
setToXiangChe();
|
|
})
|
|
scanevents = new ScanEvents(() => {
|
|
// 继续扫一扫
|
|
openAnimation = false
|
|
startCamera();
|
|
})
|
|
flashBtnevents = new FlashBtnEvents(() => {
|
|
// 开头灯光
|
|
flashModel = !flashModel
|
|
setFlashModel(flashModel)
|
|
flashBtn.text = flashModel?"关灯":String("开灯")
|
|
})
|
|
|
|
|
|
// 返回的事件。
|
|
backButton.isUserInteractionEnabled = true
|
|
let backevents_rz = new UITapGestureRecognizer()
|
|
backevents_rz.addTarget(backevents, action = new Selector("onclick"))
|
|
backButton.addGestureRecognizer(backevents_rz)
|
|
// 相册 按钮事件。。
|
|
let photosevents_rz = new UITapGestureRecognizer()
|
|
photosevents_rz.addTarget(photosevents, action = new Selector("onclick"))
|
|
photoBtn.addGestureRecognizer(photosevents_rz)
|
|
photoBtn.isUserInteractionEnabled = true
|
|
// 扫一扫 按钮事件。。
|
|
scanBtn.isUserInteractionEnabled = true
|
|
let scanevents_rz = new UITapGestureRecognizer()
|
|
scanevents_rz.addTarget(scanevents, action = new Selector("onclick"))
|
|
scanBtn.addGestureRecognizer(scanevents_rz)
|
|
//打开灯光事件.setFlashModel
|
|
flashBtn.isUserInteractionEnabled = true
|
|
let flashBtnevents_rz = new UITapGestureRecognizer()
|
|
flashBtnevents_rz.addTarget(flashBtnevents, action = new Selector("onclick"))
|
|
flashBtn.addGestureRecognizer(flashBtnevents_rz)
|
|
|
|
|
|
if (openAnimation) {
|
|
// 增加显示动画.
|
|
parentView!.layer.add(scaleAni, forKey = "scaleAnimation")
|
|
}
|
|
// ios识别太快了,图像还没定焦就识别出来了,稍微延迟下,不要太快了。与安卓对齐 下。
|
|
setTimeout(function () {
|
|
isCamreaing = true;
|
|
}, 300);
|
|
})
|
|
|
|
}
|
|
|
|
function calcPoint(c_w : number, c_h : number, image_w : number, image_h : number, image_point_x : number, image_point_y : number, degration : number) : ConverXy {
|
|
function scaleAndCenterContent(
|
|
container : contaiaSize,
|
|
content : contaiaSize
|
|
) : contaiaCalseSize {
|
|
// 计算缩放比例
|
|
const scaleWidth = container.width / content.width;
|
|
const scaleHeight = container.height / content.height;
|
|
const scale = Math.min(scaleWidth, scaleHeight);
|
|
|
|
// 计算居中偏移量
|
|
const offsetX = (container.width - content.width * scale) / 2;
|
|
const offsetY = (container.height - content.height * scale) / 2;
|
|
|
|
return { scale, offsetX, offsetY } as contaiaCalseSize;
|
|
}
|
|
function mapContentCoordinatesToContainer(
|
|
contentX : number,
|
|
contentY : number,
|
|
calcsize : contaiaCalseSize
|
|
) : contaiPointXy {
|
|
// 映射内容层坐标到容器层坐标
|
|
const containerX = contentX * calcsize.scale + calcsize.offsetX;
|
|
const containerY = contentY * calcsize.scale + calcsize.offsetY;
|
|
|
|
return { x: containerX, y: containerY } as contaiPointXy;
|
|
}
|
|
|
|
let rc = scaleAndCenterContent(
|
|
{ width: c_w, height: c_h } as contaiaSize,
|
|
{ width: image_h, height: image_w } as contaiaSize
|
|
)
|
|
if (degration == 0) {
|
|
rc = scaleAndCenterContent(
|
|
{ width: c_w, height: c_h } as contaiaSize,
|
|
{ width: image_w, height: image_h } as contaiaSize
|
|
)
|
|
}
|
|
|
|
let result = mapContentCoordinatesToContainer(image_point_y, image_point_x, rc)
|
|
if (degration == 0) {
|
|
result = mapContentCoordinatesToContainer(image_point_x, image_point_y, rc)
|
|
}
|
|
let nexyxcov = {
|
|
x: c_w - result.x, y: result.y, minx: 0, maxx: 0, miny: 0, maxy: 0
|
|
} as ConverXy
|
|
if (degration == 0) {
|
|
nexyxcov = {
|
|
x: result.x, y: result.y, minx: 0, maxx: 0, miny: 0, maxy: 0
|
|
} as ConverXy
|
|
}
|
|
return nexyxcov
|
|
|
|
}
|
|
|
|
function createPointView(result : SCANNING_PHOTO_RESULT, degration : number) {
|
|
|
|
let realCgSize = camreaViewLayer!.frame as CGRect;
|
|
let realWidth = Number(realCgSize.width);
|
|
let realHeight = Number(realCgSize.height);
|
|
clearPointView();
|
|
|
|
let pointSize = 30
|
|
DispatchQueue.main.async(execute = () : void => {
|
|
|
|
for (let i = 0; i < result.bounds.length; i++) {
|
|
let text = result.text[i]
|
|
let bounds = result.bounds[i]
|
|
let pos = result.position[i];
|
|
|
|
let resutxy = calcPoint(realWidth, realHeight, videoWidth, videoHeight, bounds.centerX, bounds.centerY, degration)
|
|
|
|
let x = resutxy.x - 15
|
|
let y = resutxy.y - 15
|
|
let rect = new CGRect(x = x.toInt(), y = y.toInt(), width = pointSize.toInt(), height = pointSize.toInt())
|
|
let pointViewSelf = new UIView();
|
|
pointViewSelf.layer.frame = rect;
|
|
pointViewSelf.backgroundColor = UTSiOS.colorWithString('#21d429');
|
|
pointViewSelf.layer.borderWidth = 2
|
|
pointViewSelf.layer.borderColor = UTSiOS.colorWithString('#FFFFFF').cgColor;
|
|
pointViewSelf.layer.cornerRadius = new CGFloat(pointSize / 2);
|
|
|
|
let onlick = new PointClickEvent((atext : string) => {
|
|
rsultCallBack(atext)
|
|
closeScan()
|
|
quitView()
|
|
}, text);
|
|
pointsEvents.push(onlick)
|
|
let tapGestureCancel = new UITapGestureRecognizer()
|
|
tapGestureCancel.addTarget(onlick, action = new Selector("onclick"))
|
|
pointViewSelf.addGestureRecognizer(tapGestureCancel)
|
|
|
|
|
|
pointsLayerView!.addSubview(pointViewSelf)
|
|
pointView.push(pointViewSelf)
|
|
|
|
}
|
|
})
|
|
}
|
|
|
|
/** 手动开启和关闭闪光灯 */
|
|
function setFlashModel(flash : boolean) {
|
|
if (captureDeviceAllGlobal != null) {
|
|
let islock = UTSiOS.try(captureDeviceAllGlobal!.lockForConfiguration(), "?")
|
|
if (islock != null) {
|
|
captureDeviceAllGlobal!.torchMode = flash ? AVCaptureDevice.TorchMode.on : AVCaptureDevice.TorchMode.off
|
|
captureDeviceAllGlobal!.unlockForConfiguration()
|
|
}
|
|
}
|
|
}
|
|
function clearPointView() {
|
|
DispatchQueue.main.async(execute = () : void => {
|
|
for (let i = 0; i < pointView.length; i++) {
|
|
let item = pointView[i];
|
|
item.removeFromSuperview();
|
|
}
|
|
pointView = [] as UIView[]
|
|
})
|
|
}
|
|
function closeScan() {
|
|
// 移除点。
|
|
clearPointView()
|
|
if (captureSession != null) {
|
|
captureSession!.stopRunning()
|
|
|
|
for (input in captureSession!.inputs) {
|
|
captureSession!.removeInput(input)
|
|
}
|
|
|
|
for (output in captureSession!.outputs) {
|
|
captureSession!.removeOutput(output)
|
|
}
|
|
captureSession = null;
|
|
}
|
|
scanResult = [] as Swift.Array<Barcode>
|
|
pointsEvents = [] as PointClickEvent[]
|
|
|
|
captureDeviceAllGlobal = nil;
|
|
FlashButton = nil;
|
|
|
|
}
|
|
function startCamera() {
|
|
isCamreaing = false;
|
|
closeScan();
|
|
captureSession = new AVCaptureSession();
|
|
let captureDevice = AVCaptureDevice.default(for = AVMediaType.video)
|
|
let captureOut = new AVCaptureVideoDataOutput();
|
|
captureSession!.sessionPreset = AVCaptureSession.Preset.photo
|
|
captureOutDelegate = new CaptureOutSessionBuffer((result : SCANNING_PHOTO_RESULT) => {
|
|
|
|
createPointView(result, 90)
|
|
|
|
uni.setStorageSync("xMlKITScaning", JSON.stringify(result.text))
|
|
// t.$emit("scan")
|
|
if (result.text.length == 1) {
|
|
rsultCallBack(result.text[0])
|
|
|
|
setTimeout(function () {
|
|
closeScan()
|
|
quitView()
|
|
}, 150);
|
|
}
|
|
})
|
|
|
|
// 新建设备的输入设备
|
|
let inputDevice = UTSiOS.try(new AVCaptureDeviceInput(device = captureDevice!), "?")
|
|
if (inputDevice != null) {
|
|
// 添加输入到会话中。
|
|
captureSession!.addInput(inputDevice!)
|
|
}
|
|
|
|
captureOut.alwaysDiscardsLateVideoFrames = true;
|
|
// 设置输出会话
|
|
captureOut.setSampleBufferDelegate(captureOutDelegate!, queue = DispatchQueue.global())
|
|
if (captureSession!.canAddOutput(captureOut)) {
|
|
captureSession!.addOutput(captureOut)
|
|
// console.log("绑定输出层正确!")
|
|
}
|
|
|
|
|
|
xCamreaInit();
|
|
captureDeviceAllGlobal = captureDevice
|
|
}
|
|
function quitView() {
|
|
if (parentView != null) {
|
|
parentView!.removeFromSuperview()
|
|
}
|
|
}
|
|
|
|
export function openCameraApi(callback : CALL_RESULT_FUN, onlyCameras : boolean) {
|
|
rsultCallBack = callback
|
|
openAnimation = true;
|
|
onlyCamera = onlyCameras
|
|
startCamera();
|
|
}
|
|
export function closeCamera() {
|
|
closeScan();
|
|
quitView();
|
|
} |