JS實現(xiàn)網(wǎng)頁選取截屏并保存或打印

使用JS實現(xiàn)網(wǎng)頁中類似QQ截圖的效果,并將截圖打印或者保存腮郊。這是幫朋友解決的一個小需求轧飞,跟大家分享一下。體驗地址:測試截取打印保存

1. JS網(wǎng)頁選取截屏實現(xiàn)思路

1.1 確定截圖選取范圍

用戶在開始截圖后大渤,需要在頁面上選取一個截圖范圍掸绞,并且可以直觀的看到衔掸,類似如下效果:


我們的選取范圍就是鼠標(biāo)開始按下的那個點到鼠標(biāo)拖動然后松開的那個點之間所組成的矩形敞映。
為了能直觀看到我們選取的范圍,我們將這個矩形框隨著鼠標(biāo)拖動給畫出來捷犹,利用canvas即可冕末,為了方便繪制档桃,這里使用了jcanvas

1.2 將選取范圍內(nèi)的網(wǎng)頁生成截圖

如何將選取框范圍內(nèi)的網(wǎng)頁內(nèi)容變成圖像呢销凑,我們可以使用html2canvas.js仅炊,html2canvas可以將頁面中的DOM元素生成canvas抚垄,是將網(wǎng)頁生成圖像的非常好的一個選擇。使用非常簡單:

html2canvas(document.body).then(function(canvas) {
    document.body.appendChild(canvas);
});

上面的代碼就可以將body轉(zhuǎn)成canvas桐经。html2canvas使用到了Promise阴挣,得確保你的瀏覽器支持纺腊。
html2canvas雖然可以將指定元素轉(zhuǎn)成canvas茎芭,有了canvas我們就可以輕易的生成圖像梅桩。但是并不能滿足將選取框范圍內(nèi)的內(nèi)容轉(zhuǎn)成canvas的需求拜隧,選取框內(nèi)可能有多個元素洪添,并且可能是多個不完整的元素,元素只有部分外臂。
我們可以先將body整個轉(zhuǎn)成canvas律胀,然后將這個canvas進行剪裁(或生成image后剪裁)炭菌,將選取框范圍的內(nèi)容剪裁出來。很簡單赘艳,使用drawImage即可蕾管。
drawImage 方法允許在 canvas 中插入其他圖像( img 和 canvas 元素) 菩暗。drawImage函數(shù)有三種函數(shù)原型:

drawImage(image, dx, dy)
drawImage(image, dx, dy, dw, dh)
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)

其中image可以是canvas停团,image,video三種元素秒梅。

參數(shù) 描述
image 要使用的圖像舌胶、畫布或視頻
sx 可選 開始剪切的 x 坐標(biāo)位置
sy 可選 開始剪切的 y 坐標(biāo)位置
sw 可選 被剪切圖像的寬度
sh 可選 被剪切圖像的高度
dx 在畫布上放置圖像的 x 坐標(biāo)位置
dy 在畫布上放置圖像的 y 坐標(biāo)位置
dw 可選 要使用的圖像的寬度
dh 可選 要使用的圖像的高度

2 打印

打印使用 jquery.print.js jquery.print.js是一款可以實現(xiàn)網(wǎng)頁打印的插件誊薄,實用的方法非常簡單娩井。類似html2canvas可以轉(zhuǎn)換元素為canvas這樣洞辣,它可以選取元素進行部分打印昙衅。

$(".print").click(function(){
    $(".need_print").print();
});

2 完整實現(xiàn)

創(chuàng)建screenshotsPrint.js,內(nèi)容如下:

/**
 * 默認(rèn)畫筆線寬
 * @type {number}
 */
var defaultStrokeWidth = 1; //畫矩形選取框的線寬

/**
 * 選取劃線的canvasExt
 * @type {{drawRect: canvasExt.drawRect}}
 */
var canvasExt = {
    /**
     *  畫矩形
     * @param canvasId canvasId
     * @param penColor 畫筆顏色
     * @param strokeWidth 線寬
     */
    drawRect: function (canvasId, penColor, strokeWidth) {
        var that = this;

        that.penColor = penColor;
        that.penWidth = strokeWidth;
        var canvas = document.getElementById(canvasId);
        //canvas 的矩形框
        var canvasRect = canvas.getBoundingClientRect();
        //canvas 矩形框的左上角坐標(biāo)
        var canvasLeft = canvasRect.left;
        var canvasTop = canvasRect.top;

        // 要畫的矩形的起點 xy
        var x = 0;
        var y = 0;

        //鼠標(biāo)點擊按下事件著瓶,畫圖準(zhǔn)備
        canvas.onmousedown = function(e) {

            //設(shè)置畫筆顏色和寬度
            var color = that.penColor;
            var penWidth = that.penWidth;
            // 確定起點
            x = e.clientX - canvasLeft;
            y = e.clientY - canvasTop;
            // 添加layer
            $("#"+canvasId).addLayer({
                type: 'rectangle',
                strokeStyle: color,
                strokeWidth: penWidth,
                name:'areaLayer',
                fromCenter: false,
                x: x, y: y,
                width: 1,
                height: 1
            });
            // 繪制
            $("#"+canvasId).drawLayers();
            $("#"+canvasId).saveCanvas();

            //鼠標(biāo)移動事件材原,畫圖
            canvas.onmousemove = function(e){

                // 要畫的矩形的寬高
                var width = e.clientX-canvasLeft - x;
                var height = e.clientY-canvasTop - y;

                // 清除之前畫的
                $("#"+canvasId).removeLayer('areaLayer');

                $("#"+canvasId).addLayer({
                    type: 'rectangle',
                    strokeStyle: color,
                    strokeWidth: penWidth,
                    name:'areaLayer',
                    fromCenter: false,
                    x: x, y: y,
                    width: width,
                    height: height
                });

                $("#"+canvasId).drawLayers();
            }
        };
        //鼠標(biāo)抬起
        canvas.onmouseup=function(e){

            var color = that.penColor;
            var penWidth = that.penWidth;

            canvas.onmousemove = null;

            var width = e.clientX - canvasLeft - x;
            var height = e.clientY- canvasTop - y;

            $("#"+canvasId).removeLayer('areaLayer');

            $("#"+canvasId).addLayer({
                type: 'rectangle',
                strokeStyle: color,
                strokeWidth: penWidth,
                name:'areaLayer',
                fromCenter: false,
                x: x, y: y,
                width: width,
                height: height
            });

            $("#"+canvasId).drawLayers();
            $("#"+canvasId).saveCanvas();

            // 把body轉(zhuǎn)成canvas
            html2canvas(document.body, {
                scale: 1,
                // allowTaint: true,
                useCORS: true  //跨域使用
            }).then(canvas => {
                var capture_x, capture_y
                if (width > 0) {
                    //從左往右畫
                    capture_x = x + that.penWidth
                }else {
                    //從右往左畫
                    capture_x = x + width + that.penWidth
                }
                if (height > 0) {
                    //從上往下畫
                    capture_y = y + that.penWidth
                }else {
                    //從下往上畫
                    capture_y = y + height + that.penWidth
                }
                printClip(canvas, capture_x, capture_y, Math.abs(width), Math.abs(height))
            });
            // 移除畫的選取框
            $("#"+canvasId).removeLayer('areaLayer');
            // 隱藏用于華畫取框的canvas
            $("#"+canvasId).hide()
        }
    }
};

/**
 * 選取截屏
 * @param canvasId
 */
function clipScreenshots(canvasId){
    canvasExt.drawRect(canvasId, "red", defaultStrokeWidth);
}

/**
 * 打印截取區(qū)域
 * @param canvas 截取的canvas
 * @param capture_x 截取的起點x
 * @param capture_y 截取的起點y
 * @param capture_width 截取的起點寬
 * @param capture_height 截取的起點高
 */
function printClip(canvas, capture_x, capture_y, capture_width, capture_height) {
    // 創(chuàng)建一個用于截取的canvas
    var clipCanvas = document.createElement('canvas')
    clipCanvas.width = capture_width
    clipCanvas.height = capture_height
    // 截取
    clipCanvas.getContext('2d').drawImage(canvas, capture_x, capture_y, capture_width, capture_height, 0, 0, capture_width, capture_height)
    var clipImgBase64 = clipCanvas.toDataURL()
    // 生成圖片
    var clipImg = new Image()
    clipImg.src = clipImgBase64
    var con = confirm('打印截圖嗎?取消則保存截圖')
    if (con) {
        $(clipImg).print()
    }else {
        downloadIamge(clipImgBase64)
    }
}

/**
 * 下載保存圖片
 * @param imgUrl 圖片地址
 */
function downloadIamge(imgUrl) {
    // 圖片保存有很多方式,這里使用了一種投機的簡單方法威酒。
    // 生成一個a元素
    var a = document.createElement('a')
    // 創(chuàng)建一個單擊事件
    var event = new MouseEvent('click')
    // 生成文件名稱
    var timestamp = new Date().getTime();
    var name = imgUrl.substring(22, 30) + timestamp + '.png';
    a.download = name
    // 將生成的URL設(shè)置為a.href屬性
    a.href = imgUrl;
    // 觸發(fā)a的單擊事件 開始下載
    a.dispatchEvent(event);
}

demo.html中進行使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>測試截取保存或打印</title>
    <script type="text/javascript" src="libs/jquery-2.1.0.js"></script>
    <script type="text/javascript" src="libs/html2canvas.js"></script>
    <script type="text/javascript" src="libs/jQuery.print.js"></script>
    <script type="text/javascript" src="libs/jcanvas.min.js"></script>
    <script type="text/javascript" src="js/screenshotsPrint.js"></script>
    <style>
        body, html {
            width: 100%;
            height: 100%;
        }
        .print {
            position: relative;
            z-index: 100;
        }
        h1 {
            color: orangered;
        }
        h2 {
            color: darkblue;
        }
        h2 {
            color: forestgreen;
        }
        #bg_canvas {
            position: absolute;
            z-index: 500;
            left: 0;
            top: 0;
        }
    </style>
</head>
<body>
<button class="print">開始截圖</button>
<div>
    <h1>哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈</h1>
    <h2>嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿</h2>
    <h3>呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵</h3>
    <br/>
    <p>這是一張跨域圖片</p>
    <img src="http://p7.qhimg.com/t01ceede0272d4b5a8b.png" alt="來個跨區(qū)的圖片">
</div>
<!-- 用于畫選取框的canvas -->
<canvas id="bg_canvas" width="100%" height="100%" />
<script>
    $(function(){
        var clientWidth = document.documentElement.clientWidth || document.body.clientWidth
        var clientHeight = document.documentElement.clientHeight || document.body.clientHeight
        // 更新canvas寬高
        $("#bg_canvas").attr("width", clientWidth);
        $("#bg_canvas").attr("height", clientHeight);
        $("#bg_canvas").hide();
        $(".print").click(function(){
            $("#bg_canvas").show()
            alert('現(xiàn)在你可以使用鼠標(biāo)拖拽選取打印區(qū)域,松開后完成')
            //調(diào)用選取截屏
            clipScreenshots("bg_canvas");
        });


    });
</script>
</body>
</html>

演示地址: 測試截取打印保存

源碼地址: web_screenshots_print

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市吓著,隨后出現(xiàn)的幾起案子送挑,更是在濱河造成了極大的恐慌惕耕,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件欺缘,死亡現(xiàn)場離奇詭異谚殊,居然都是意外死亡,警方通過查閱死者的電腦和手機丛肢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門蜂怎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來置尔,“玉大人,你說我怎么就攤上這事幽歼∶危” “怎么了设褐?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵助析,是天一觀的道長。 經(jīng)常有香客問我外冀,道長雪隧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任藕畔,我火速辦了婚禮注服,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘女淑。我一直安慰自己辜御,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著菜拓,像睡著了一般笛厦。 火紅的嫁衣襯著肌膚如雪裳凸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天逗宁,我揣著相機與錄音梦湘,去河邊找鬼捌议。 笑死,一個胖子當(dāng)著我的面吹牛倦逐,可吹牛的內(nèi)容都是我干的宫补。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼贫贝!你這毒婦竟也來了荞雏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎凤优,沒想到半個月后悦陋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡筑辨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年俺驶,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棍辕。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡暮现,死狀恐怖楚昭,靈堂內(nèi)的尸體忽然破棺而出栖袋,到底是詐尸還是另有隱情,我是刑警寧澤抚太,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布塘幅,位于F島的核電站,受9級特大地震影響尿贫,放射性物質(zhì)發(fā)生泄漏电媳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一庆亡、第九天 我趴在偏房一處隱蔽的房頂上張望匾乓。 院中可真熱鬧,春花似錦又谋、人聲如沸拼缝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咧七。三九已至,卻和暖如春剩愧,著一層夾襖步出監(jiān)牢的瞬間猪叙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工仁卷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留穴翩,地道東北人。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓锦积,卻偏偏與公主長得像芒帕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子丰介,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,756評論 25 707
  • 這張照片就是我本人 我是一個做專業(yè)美容養(yǎng)生的背蟆,目前已從事美容養(yǎng)生行業(yè)八個年頭鉴分。我很喜歡我的事業(yè)。我只做美和健康的事...
    河南人在深圳閱讀 265評論 0 0
  • 在一家培訓(xùn)中心做了陣補習(xí)教師带膀,很有意思的工作志珍,最大收獲是了解了一至三年級孩子們普遍的學(xué)習(xí)水平和學(xué)習(xí)習(xí)慣,平撫了我教...
    花絮滿天閱讀 291評論 0 4