头像裁剪和Uncaught DOMException: Failed to execute ‘toDataURL‘ on ‘HTMLCanvasElement‘: Tainted canvases m

本文介绍了在实现头像裁剪功能时遇到的HTML5 Canvas跨域问题,以及如何通过设置crossOrigin属性和添加时间戳解决。详细探讨了浏览器的CORS策略,同时分享了如何在不使用时间戳的情况下,通过禁用浏览器缓存来避免跨域错误。此外,还展示了相关的JavaScript和HTML代码片段,用于展示头像裁剪的实现过程。
摘要由CSDN通过智能技术生成

此文是半原创。

头像裁剪的主要实现是同事找来发我的,用着还不错。

记一下,可以用作以后研究。

此文主要记录一个要点:

当用户上传已上传头像,裁剪头像弹窗获取到图片,当调用canvas的toDataURL方法时可能会报:Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported这个错误。

百度上主要方法有2步:

一是给图片添加crossOrigin(具体添加在下方依赖js里有,此处只是简单说明)

 obj.image.crossOrigin = 'anonymous';

在HTML5中,一些 HTML 元素提供了对 CORS 的支持, 例如 <audio>、<img>、<link>、<script> 和 <video> 均有一个跨域属性 (crossOrigin property),它允许你配置元素获取数据的 CORS 请求。 

二是给请求服务器配置相应的跨域(自行百度搜索吧,服务器我接触不到)

"Access-Control-Allow-Origin: *"

当然,这是网上最多的两种方式。如果还是不行(我的问题是即使经过了上面两个步骤,浏览器仍然提示跨域。),那就试试给请求图片加个时间戳吧。

obj.image.src = options.imgSrc + "?" + new Date().getTime()

我的难题到这一步已经解决了,至于为什么加时间戳可行,目前还不是很清楚。如果大佬愿意解此疑惑,欢迎评论。

。。。。。。。。。。。。。我又来了,自己照旧百度了一下这个问题。

发现有时候我们会在请求静态资源的时候添加时间戳,防止浏览器缓存。

于是我去掉时间戳,在谷歌浏览器控制台------network里面勾选disable cache 后,再次访问,成功获取图片。

也就是说浏览器先前已经缓存了我的图片地址,然而此时我又一次请求了它,并且请求的时候我设置了crossOrigin属性,可是缓存中的图片非CORS请求,所以浏览器直接就拒绝了请求

效果图:

效果图

<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>头像裁剪</title>
    <link rel="stylesheet" type="text/css" href="下面css" />
</head>

<body>
    <div class="container">
        <div class="close" onclick="window.parent.topLayerService.hideTopLayer();">关闭</div>
        <div class="box">
            <div class="imageBox" id="head_portrait">
                <div class="thumbBox"></div>
                <div class="spinner" style="display: none">Loading...</div>
            </div>
            <div class="action">
                <div class="new-contentarea tc">
                    <a href="javascript:void(0)" class="upload-img">
                        <label for="upload-file">上传图像</label>
                    </a>
                    <input type="file" class="" name="upload-file" id="upload-file" />
                </div>
                <input type="button" id="btnZoomIn" class="Btnsty_peyton" value="+" />
                <input type="button" id="btnZoomOut" class="Btnsty_peyton" value="-" />
                <input type="button" id="btnCrop" class="Btnsty_peyton" value="裁切" />
            </div>
        </div>
        <div class="cropped">
            <h2>效果预览</h2>
            <div class="croppedImg"></div>
            <div class="exhibition" id="exhibition"></div>
            <input type="button" id="submit" class="Btnsty_peyton" value="确认修改" />
        </div>
    </div>

    <script type="text/javascript" src="../../../lib/jquery.js"></script>
    <script type="text/javascript" src="下面js"></script>
    <script type="text/javascript">
        $(window).ready(function () {
            var options =
            {
                thumbBox: '.thumbBox',
                spinner: '.spinner',
                imgSrc: ''
            }
            var cropper = $('.imageBox').cropbox(options);
            init();
            function init() {
               //初始获取用户头像图片,成功回调
               callback: function (data) {//渲染内容
                  var nowdata = data[0];
                    if (nowdata.head_portrait != undefined) {
                       options.imgSrc = "XXXXXXXXXX.jpg";
                       // 读取图片是否添加时间戳
                       options.initFlag = true;
                       cropper = $('.imageBox').cropbox(options);
                   }
               }
            }
            // 切换图片
            $('#upload-file').on('change', function () {
                var reader = new FileReader();
                // file 将文件读取为 DataURL
                reader.readAsDataURL(this.files[0]);
                reader.onload = function (e) {
                    options.imgSrc = e.target.result;
                    // 读取图片是否添加时间戳
                    options.initFlag = false;
                    cropper = $('.imageBox').cropbox(options);
                }
                reader.onerror = function (e) {
                    alert('图片上传失败,请检查。');
                }
                this.files = [];
            })
            // 裁切图片
            $('#btnCrop').on('click', function () {
                var img = cropper.getDataURL();
                $('.croppedImg').html('');
                $('.croppedImg').append('<div class="headerUP"><img src="' + img + '" align="absmiddle"></div><div class="headerSize">66px*66px</div>');
                // $('.cropped').append('<img src="' + img + '" align="absmiddle" style="width:128px;margin-top:4px;border-radius:128px;box-shadow:0px 0px 12px #7E7E7E;"><p>128px*128px</p>');
                // $('.cropped').append('<img src="' + img + '" align="absmiddle" style="width:180px;margin-top:4px;border-radius:180px;box-shadow:0px 0px 12px #7E7E7E;"><p>180px*180px</p>');
            })
            // 放大图片
            $('#btnZoomIn').on('click', function () {
                cropper.zoomIn();
            })
            // 缩小图片
            $('#btnZoomOut').on('click', function () {
                cropper.zoomOut();
            })
            // 提交
            $('#submit').on('click', function () {
                var files = new File([cropper.getBlob()], '.jpg', { type: 'image/jpg' });
                upload(files);
            });
            // 上传
            function upload(file) {
                var formData = new FormData();
                formData.append("file", file);
                formData.append("serviceToken", getCookie("serviceToken"));
                formData.append("time", getCookie("time"));
                formData.append("token", getCookie("serverToken"));
                formData.append("userid", getCookie("userid"));
                formData.append("serverToken", getCookie("serverToken"));
                $.ajax({
                    url:"XXXXXXXXXXXXX",//上传服务
                    type: "post",
                    data: formData,
                    async: false,
                    cache: false,
                    contentType: false,
                    processData: false,
                    success: function (data) {
                        //发送获取用户信息成功后的回调
                        callback: function (data1) {//渲染内容
                                alert(data1 == "success" ? "更新头像成功" : "更新头像失败!"
                                $("#exhibition").html("<img src='" + imageServerUrl + data.substr(12) + "'/>");
                            }
                    },
                    error: function (XMLHttpRequest, textStatus, errorThrown) {
                        console.info(XMLHttpRequest.status);
                        console.info(XMLHttpRequest.readyState);
                        console.info(textStatus);
                        alert("文件上传失败!")
                    },
                    complete: function (XMLHttpRequest, textStatus) {
                        this; // 调用本次AJAX请求时传递的options参数
                    }
                });
                return false;
            }
        });
    </script>
</body>

</html>
/**   依赖js文件
 * Created by ezgoing on 14/9/2014.
 */

"use strict";
(function (factory) {
    if (typeof define === 'function' && define.amd) {
        define(['jquery'], factory);
    } else {
        factory(jQuery);
    }
}(function ($) {
    var cropbox = function (options, el) {
        var el = el || $(options.imageBox),
            obj =
            {
                state: {},
                ratio: 1,
                options: options,
                imageBox: el,
                thumbBox: el.find(options.thumbBox),
                spinner: el.find(options.spinner),
                image: new Image(),
                getDataURL: function () {
                    var width = this.thumbBox.width(),
                        height = this.thumbBox.height(),
                        canvas = document.createElement("canvas"),
                        dim = el.css('background-position').split(' '),
                        size = el.css('background-size').split(' '),
                        dx = parseInt(dim[0]) - el.width() / 2 + width / 2,
                        dy = parseInt(dim[1]) - el.height() / 2 + height / 2,
                        dw = parseInt(size[0]),
                        dh = parseInt(size[1]),
                        sh = parseInt(this.image.height),
                        sw = parseInt(this.image.width);

                    canvas.width = width;
                    canvas.height = height;
                    var context = canvas.getContext("2d");
                    context.drawImage(this.image, 0, 0, sw, sh, dx, dy, dw, dh);
                    //toDataURL将canvas对象转换为base64位编码 
                    var imageData = canvas.toDataURL('image/png');
                    return imageData;
                },
                //  将base64转成图片文件
                getBlob: function () {
                    var imageData = this.getDataURL();
                    var b64 = imageData.replace('data:image/png;base64,', '');
                    var binary = atob(b64);
                    var array = [];
                    for (var i = 0; i < binary.length; i++) {
                        array.push(binary.charCodeAt(i));
                    }
                    return new Blob([new Uint8Array(array)], { type: 'image/png' });
                },
                // 图片放大
                zoomIn: function () {
                    this.ratio *= 1.1;
                    setBackground();
                },
                //  图片缩小
                zoomOut: function () {
                    this.ratio *= 0.9;
                    setBackground();
                }
            },
            //  设置图片大小
            setBackground = function () {
                var w = parseInt(obj.image.width) * obj.ratio;
                var h = parseInt(obj.image.height) * obj.ratio;

                var pw = (el.width() - w) / 2;
                var ph = (el.height() - h) / 2;

                el.css({
                    'background-image': 'url(' + obj.image.src + ')',
                    'background-size': w + 'px ' + h + 'px',
                    'background-position': pw + 'px ' + ph + 'px',
                    'background-repeat': 'no-repeat'
                });
            },
            imgMouseDown = function (e) {
                e.stopImmediatePropagation();

                obj.state.dragable = true;
                obj.state.mouseX = e.clientX;
                obj.state.mouseY = e.clientY;
            },
            imgMouseMove = function (e) {
                e.stopImmediatePropagation();

                if (obj.state.dragable) {
                    var x = e.clientX - obj.state.mouseX;
                    var y = e.clientY - obj.state.mouseY;

                    var bg = el.css('background-position').split(' ');

                    var bgX = x + parseInt(bg[0]);
                    var bgY = y + parseInt(bg[1]);

                    el.css('background-position', bgX + 'px ' + bgY + 'px');

                    obj.state.mouseX = e.clientX;
                    obj.state.mouseY = e.clientY;
                }
            },
            imgMouseUp = function (e) {
                e.stopImmediatePropagation();
                obj.state.dragable = false;
            },
            zoomImage = function (e) {
                e.originalEvent.wheelDelta > 0 || e.originalEvent.detail < 0 ? obj.ratio *= 1.1 : obj.ratio *= 0.9;
                setBackground();
            }

        obj.spinner.show();
        obj.image.onload = function () {
            obj.spinner.hide();
            setBackground();

            el.bind('mousedown', imgMouseDown);
            el.bind('mousemove', imgMouseMove);
            $(window).bind('mouseup', imgMouseUp);
            el.bind('mousewheel DOMMouseScroll', zoomImage);
        };
         obj.options.initFlag ? obj.image.src = options.imgSrc + "?" + new Date().getTime() : obj.image.src = options.imgSrc;
        obj.image.crossOrigin = 'anonymous';
        el.on('remove', function () { $(window).unbind('mouseup', imgMouseUp) });
        return obj;
    };
    jQuery.fn.cropbox = function (options) {
        return new cropbox(options, this);
    };
}));
/*
*   依赖css文件
 */
.container {
    width: 660px;
    margin: 40px auto 0 auto;
    position: relative;
    color: #ffffff;
    font-size: 14px;
    box-shadow: -14px 0px 36px 7px rgba(108, 108, 108, 0.08);
    border-radius: 6px;
    overflow: hidden;
}
.close{
    width: 20px;
    height: 20px;
    background: url(../../../image/imageUp/guanbida1.png) no-repeat 0/100%;
    position: absolute;
    right: 8px;
    top: 8px;
    z-index: 3;
    font-size: 0;
    cursor: pointer;
}

.container p {
    line-height: 12px;
    line-height: 0px;
    height: 0px;
    margin: 10px;
    color: #bbb;
}

.action {
    width: 340px;
    height: 36px;
    position: absolute;
    bottom: 28px;
    left: 30px;
}

.cropped {
    width: 270px;
    height: 502px;
    position: absolute;
    right: 0px;
    top: 0;
    box-shadow: 0px 0px 12px #ddd;
    text-align: center;
    background: #ffffff;
}
.cropped h2 {
    margin: 53px 0 34px;
    font-size: 16px;
    font-weight: 500;
    color: #333333;
    line-height: 16px;
}
.box {
    width: 390px;
    height: 502px;
    position: relative;
    overflow: hidden;
    background: rgba(0, 0, 0, 0.7);
}
.imageBox {
    width: 276px;
    height: 356px;
    margin: 44px auto 0;
    position: relative;
    /* overflow: hidden; */
    background-repeat: no-repeat;
    cursor: move;
}

.imageBox .thumbBox {
    width: 200px;
    height: 200px;
    position: absolute;
    top: 50%;
    left: 50%;
    margin-top: -100px;
    margin-left: -100px;
    box-sizing: border-box;
    border: 1px solid rgb(102, 102, 102);
}

.imageBox .spinner {
    text-align: center;
    line-height: 356px;
    background: rgba(255, 255, 255);
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    color: #333333;
}

.Btnsty_peyton {
    display: inline-block;
    *display: inline;
    *zoom: 1;
    vertical-align: top;
    width: 50px;
    height: 36px;
    padding: 0;
    margin-left: 4px;
    background: linear-gradient(0deg, #3377ff, #57a5ff);
    border-radius: 4px;
    outline: none;
    border: none;
    color: #ffffff;
    cursor: pointer;
}
#btnZoomIn {
    margin-left: 55px;
    background: url(../../../image/imageUp/zoomIn.png) no-repeat 0/100%;
    font-size: 0;
}
#btnZoomOut{
    background: url(../../../image/imageUp/zoomOut.png) no-repeat 0/100%;
    font-size: 0;
}
#btnCrop {
    width: 56px;
}
.croppedImg {
    height: 85px;
    text-align: center;
}
.headerUP {
    width: 66px;
    height: 66px;
    margin: 0 auto;
    border-radius: 100%;
    box-shadow: 0px 0px 12px #7e7e7e;
    overflow: hidden;
}
.headerUP img {
    width: 66px;
    height: 66px;
}
.exhibition {
    width: 246px;
    height: 180px;
    margin: 9px auto 67px;
}
.exhibition img{
    width: 246px;
    height: 180px;
}
.cropped .Btnsty_peyton {
    margin: 0 auto;
    width: 110px;
    height: 36px;
}

/*选择文件上传*/
.new-contentarea {
    width: 110px;
    overflow: hidden;
    margin: 0 auto;
    position: relative;
    float: left;
}

.new-contentarea label {
    width: 100%;
    height: 100%;
    display: block;
}

.new-contentarea input[type="file"] {
    width: 110px;
    height: 36px;
    background: #333;
    margin: 0 auto;
    position: absolute;
    top: 0;
    left: 0;
    opacity: 0;
    filter: alpha(opacity=0);
    z-index: 2;
}

a.upload-img {
    display: inline-block;
    *display: inline;
    *zoom: 1;
    width: 110px;
    height: 36px;
    line-height: 36px;
    background: linear-gradient(0deg, #3377ff, #57a5ff);
    border-radius: 4px;
    text-decoration: none;
    color: #ffffff;
    cursor: pointer;
}

a.upload-img:hover {
    background-color: #ec7e70;
}

.tc {
    text-align: center;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值