在线 demo

如何绘制跨域图片?

比如,m.example.com 网站的 js 想读取 static.example.com 的图片。

首先,在 static.example.com 服务器增加跨域请求头。以 nginx 为例:

location / {
    add_header 'Access-Control-Allow-Origin' 'https://m.example.com' always;
    ...
}

传入截图选项 useCORS: true:

const html2canvas = require('html2canvas')
const options = {
    useCORS: true
}
html2canvas(target, options)
    .then(canvas => {
        document.body.appendChild(canvas)
    })

useCORS

当设定 { useCORS: true } 后,html2canvas 到底做了什么操作?

在源代码 src/ResourceLoader.js 中:

// ...
if (!isSVG(src) || FEATURES.SUPPORT_SVG_DRAWING) {
    if (this.options.allowTaint === true || isInlineImage(src) || this.isSameOrigin(src)) {
        return this.addImage(src, src, false);
    } else if (!this.isSameOrigin(src)) {
        if (typeof this.options.proxy === 'string') {
            this.cache[src] = Proxy(src, this.options).then(src =>
                loadImage(src, this.options.imageTimeout || 0)
            );
            return src;
        } else if (this.options.useCORS === true && FEATURES.SUPPORT_CORS_IMAGES) {
            return this.addImage(src, src, true);
        }
    }
}
// ...

因此,当图片域名和当前页面不一致 isSameOrigin === false 时,并且设置了 useCORS: true 选项,就会调用 addImage(src, src, true) 函数加载图像。

继续跟踪 addImage() 函数:

// ...
addImage(key: string, src: string, useCORS: boolean): string {
    // ...

    const imageLoadHandler = (supportsDataImages: boolean): Promise<Image> =>
        new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = () => resolve(img);
            //ios safari 10.3 taints canvas with data urls unless crossOrigin is set to anonymous
            if (!supportsDataImages || useCORS) {
                img.crossOrigin = 'anonymous';
            }

            img.onerror = reject;
            img.src = src;
            if (img.complete === true) {
                // Inline XML images may fail to parse, throwing an Error later on
                setTimeout(() => {
                    resolve(img);
                }, 500);
            }
            if (this.options.imageTimeout) {
                const timeout = this.options.imageTimeout;
                setTimeout(
                    () =>
                        reject(
                            __DEV__
                                ? `Timed out (${timeout}ms) fetching ${src.substring(0, 256)}`
                                : ''
                        ),
                    timeout
                );
            }
        });

    this.cache[key] =
        isInlineBase64Image(src) && !isSVG(src)
            ? // $FlowFixMe
                FEATURES.SUPPORT_BASE64_DRAWING(src).then(imageLoadHandler)
            : imageLoadHandler(true);
    return key;
}

useCORS 为真,会动态产生图片,并设置其 crossOrigin = "anonymous",让其可以跨域。

REF