EddyVerbruggen / nativescript-plugin-firebase

:fire: NativeScript plugin for Firebase
https://firebase.google.com
MIT License
1.01k stars 444 forks source link

Feature Request: zoom scanner functionality #1575

Open bradmartin opened 4 years ago

bradmartin commented 4 years ago

We've implemented this directly accessing the camera and camerView members of the scanner for android/iOS.

Was simple enough to just modify for specific scenarios instead of making a PR without thinking out the public API to make possible.

Below is for a vue app:

iOS has a max zoom value that is accessed from a camera property

    <Slider
                    @valueChange="onValueChanged"
                    minValue="1"
                    android:maxValue="100"
                    :ios:maxValue="iOSMaxZoom"
                    padding="6"
                    width="100%"
                />
    onScannerLoaded() {
        // using a timeout to ensure the camera is loaded and opened or it'll be null
        setTimeout(() => {
            // get the native cameras
            if (isAndroid) {
                // https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/27a0ef2ec54726d31854689353f1f45eefcc2f23/src/mlkit/mlkit-cameraview.android.ts#L147
                this.camera = (this.$refs.scanner as any).nativeView.camera;
            } else {
                // https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/27a0ef2ec54726d31854689353f1f45eefcc2f23/src/mlkit/mlkit-cameraview.ios.ts#L94
                this.camera = (this.$refs.scanner as any).nativeView.captureDevice;
                this.iOSMaxZoom = this.camera.maxAvailableVideoZoomFactor;
                // to adjust the camera zoom we must lock the configuration before changing the AVCaptureDevice zoom
                // https://developer.apple.com/documentation/avfoundation/avcapturedevice/1624611-videozoomfactor
                this.camera.lockForConfiguration();
            }
        }, 1000);
    }

So this gives us the native cameras for each platform.

Then we have:

    private _setZoom(level = 0) {
        if (isAndroid) {
            try {
                const params = this.camera.getParameters();
                const zoomLevel = Math.round(
                    scale(level, 0, 100, 0, params.getMaxZoom())
                );
                params.setZoom(zoomLevel);
                this.camera.setParameters(params);
            } catch (err) {
                console.log(err);
            }
        } else {
            try {
                const zoomLevel = Math.round(scale(level, 1, 100, 1, this.iOSMaxZoom));
                this.camera.rampToVideoZoomFactorWithRate(zoomLevel, 2.0);
            } catch (err) {
                console.log(err);
            }
        }
    }

      // bound to a slider in the UI that passes the slider value into the zooming calls.
    onValueChanged(args) {
        this._setZoom(args.value);
    }

// to calculate the scaling zoom
const scale = (
    num: number,
    in_min: number,
    in_max: number,
    out_min: number,
    out_max: number
) => {
    return ((num - in_min) * (out_max - out_min)) / (in_max - in_min) + out_min;
};

NOTE: iOS has two method calls that are critical: one before you make changes to zoom and one method when done. It's simplest to do the prior call before you make changes, keep it unlocked for more zoom changes, and then close the config when you're leaving the component/view/page that is showing the scanner. Not sure best approach to handle this with the plugin, maybe destroyNativeView or similar event would work.

@EddyVerbruggen - if you have any thoughts on making into public API I'd be happy to help work something in to the plugin source, just didn't have time working on client project to abstract it into something well thought of.