【在网页中猎取截图数据】Chrome和Firefox下的实战经验

最近在完成一个功用,需求以下:

  • 前提:当前页面无弹窗

  • 页面恣意位置实行粘贴

  • 读取剪切板中的截屏数据

  • 上传截图

起首照样从网上找相干的例子。

找到了SF上的专栏文章《js猎取剪切板内容,js掌握图片粘贴》。

因而基于这个,做出了初版的截图上传功用。

因为项目运用的是angularjs,事前已封装好一套上传图片的要领,只需要挪用 $scope.image = blob,自动就会发送、上传该文件。

我是半路参与项目的。本来为数不多的几个js文件着实太大,一个apiService.js就积累了三四千行,种种效劳都在这个文件里,主视图就一个mainController,也是三四千行。

说实话,我真的惊呆了。

所以照样只管防止修正本来的代码。

依据我本身习气,把功用封装成directive,自力建一个文件。

代码以下:(迥殊道谢本期节目的文章)

/**
 * @description: 截屏上传
 * @author:      angusfu1126@qq.com
 * @date:        2016-03-03 20:59:09
 */
app.directive('screenshotOrDragUpload', /*ngInject*/ function($filter) {
    return {
        restrict: 'A'
        link: function($scope, iElm, iAttrs, controller) {

            var imageRegex = /^image\//i;

            // 粘贴截图事宜
            document.addEventListener('paste', onPasteHandler, false);

            // 作用域烧毁的时刻消除事宜绑定
            $scope.$on('$destroy', function() {
                document.removeEventListener('paste', onPasteHandler);
            });

            /**
             * 全局蒙版显现的时刻
             * 不实行粘贴或许拖拽功用
             * 防止和种种弹层ng-show前提太耦合
             * 此处运用DOM要领推断
             */
            function isMaskShown() {
                // 项目依赖于jquery
                return angular.element('.global-mask').is(':visible');
            }

            /**
             * 依据时候戳定名
             */
            function generateFileName(user) {
                return $filter('date')(new Date(), 'yyyyMMdd_HH:MM:ss');
            }

            /**
             * 处置惩罚 `ctrl + v` 截图粘贴事宜
             */
            function onPasteHandler(e) {
                if (isMaskShown()) return;

                var clipboardData = e.clipboardData;
                var ua = window.navigator.userAgent;

                // 假如没法猎取剪贴板则返回
                if (!clipboardData || !clipboardData.items) {
                    return;
                }

                // Mac平台下Chrome49版本以下
                // 复制Finder中的文件的Bug Hack掉
                // see: https://segmentfault.com/a/1190000004288686
                if (clipboardData.items
                        && clipboardData.items.length === 2
                        && clipboardData.items[0].kind === "string"
                        && clipboardData.items[1].kind === "file"
                        && clipboardData.types
                        && clipboardData.types.length === 2
                        && clipboardData.types[0] === "text/plain"
                        && clipboardData.types[1] === "Files"
                        && ua.match(/Macintosh/i)
                        && Number(ua.match(/Chrome\/(\d{2})/i)[1]) < 49
                 ) {
                    return;
                }

                var len = clipboardData.items.length,
                    item = null,
                    blob = null;

                while (len--) {

                    item = clipboardData.items[len];

                    if (item.kind == "file") {

                        blob = item.getAsFile();

                        if (imageRegex.test(blob.type) && blob.size > 0) {
                            blob.name = generateFileName();

                            // 挪用上传
                            $scope.image = blob;
                            break;
                        }
                    }
                }
            }


        }
    };
});

固然,文章不能够就此结束。。。

分割线歇息少焉

==============================================================

上述功用只要在Chrome和Safari中有用,但到火狐上面就挂掉了啊。。。

测试一下,给document绑定paste事宜,粘贴的时刻压根就读不到数据。

火狐下面,并没有clipboardData.items这一项。

o(╯□╰)o

那怎么办呢?

只能退而求其次。摒弃,或许追求降级的要领。

就在我以为无路可走的时刻,火狐的一个特性让我眼前一亮。。。

分别用chrome和firefox翻开这个demo碰运气,试着用qq截个图或许在文件夹中复制一张图片,粘贴在赤色框框里。

有无发明,只要在火狐下能把图粘贴进来?

嗯,处理要领就在这里了。

实在,demo中的赤色框框是一个有contenteditable属性的div

关于contenteditable,此处有张鑫旭大神的博文两篇,且记在此处备忘:

firefox下面,是能够把剪切板中的图片数据粘贴进去的,而chrome下面则不行了。

而项目的输入框,正好是一个pre标签加上contenteditable属性模仿出来的。圆满~~~(此处应有金星先生脸色包)

好了,在火狐中粘贴截图以后,右键检察一下,是不是是像下图酱紫的?

《【在网页中猎取截图数据】Chrome和Firefox下的实战经验》

有木有看到能干的img标签?

有木有看到能干的data:image/png;base64,

要领有了。处理方案以下:

  • 监听keydown事宜

  • 检测输入框是不是为空

    • 非空:不允许粘贴图片(但我们不能事前推断数据类型,只能敏捷remove掉img元素)

    • 空的:猎取img元素及其src数据,然后敏捷移除元素

固然,此处是有坑的。。。

详细坑在那里呢?看代码吧。实在我以为我能够没完整处理。

if (/firefox/i.test(navigator.userAgent)) {
    var URL = (window.URL || window.mozURL),

        supportTransform = URL && window.Blob && window.atob && window.ArrayBuffer && window.Uint8Array,

        // see http://jsperf.com/blob-base64-conversion
        convertBase64UrlToBlob = function(urlData) {
            //去掉url的头,并转换为byte
            var bytes = window.atob(urlData.split(',')[1]);

            //处置惩罚非常,将ascii码小于0的转换为大于0
            var ab = new ArrayBuffer(bytes.length);
            var ia = new Uint8Array(ab);
            for (var i = 0; i < bytes.length; i++) {
                ia[i] = bytes.charCodeAt(i);
            }
            return new Blob([ab], {
                type: 'image/png'
            });
        };

    $('pre').on('keydown', function(e) {

        var isCtrlV = (e.ctrlKey && e.keyCode == '86');

        if (!supportTransform || !isCtrlV) return;

        var $this = $(this),
            html = $this.html(),
            canPasteImage = false;

        // Notice
        // 火狐的坑在这里啊啊啊啊
        // 只要空的时刻才粘贴图片
        if (!html || html === '<br>') {
            canPasteImage = true;
        }

        setTimeout(function() {
            var $imgs = $this.find('img').remove(),
                data = $imgs.eq(0).attr('src');

            if (canPasteImage && data) {
                var blob = convertBase64UrlToBlob(data);
                blob.name = generateFileName();
                // 挪用上传
                $scope.image = blob;
            }
        }, 0);

    });
}

做个笔记: Blob对象和base64字符串的转换, http://jsperf.com/blob-base64-conversion

现在还没在IE上测试过,不知道效果怎样。

    原文作者:文蔺
    原文地址: https://segmentfault.com/a/1190000004584071
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞