SOURCE

console 命令行工具 X clear

                    
>
console
//https://mozilla.github.io/pdf.js/examples/index.html
//https://cdn.staticfile.net/pdf.js/3.9.179/pdf.min.js
//https://cdn.staticfile.net/pdf.js/3.9.179/pdf.worker.js
//https://cdn.staticfile.net/pdf.js/3.9.179/pdf_viewer.min.js
//https://cdn.staticfile.net/pdf.js/3.9.179/pdf_viewer.min.css
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdn.staticfile.net/pdf.js/3.9.179/pdf.worker.js';

(function(){
//----------
var vew = $('#id-page'),
    upl = document.getElementById('i_file'),
    now = document.getElementById('s_now'), v_n = 1,
    bp = document.getElementById('b_pre'),
    bn = document.getElementById('b_next'),
    isc = $('#i_scale'),
    total = document.getElementById('s_total'), v_t = 1;
var pdfData;
function pdf_view(){
    var scale = isc.val(), loadingTask = pdfjsLib.getDocument(pdfData);
    vew.html('<span>加载pdf...</span>');
    upl.disabled = true;
    loadingTask.promise.then(function(pdf) {
        vew.html('');
        total.innerText = v_t = pdf.numPages;//初始化页码
        now.innerText = v_n = 1;
        bp.disabled = true; bn.disabled = v_t == 1;//上下页按钮 是否禁用
        for(let i = 1; i <= v_t; i ++){
            pdf.getPage(i).then(function(page) {
                var canvas = document.createElement('canvas');
                var viewport = page.getViewport({scale: scale});
                canvas.id = 'pdf-v-'+i;
                canvas.height = viewport.height; canvas.width = viewport.width;
                var renderContext = { canvasContext: canvas.getContext('2d'), viewport: viewport };
                var renderTask = page.render(renderContext);
                renderTask.promise.then(function () {
                    vew.append(canvas); if(i>=v_t){ upl.disabled = false; }
                });
            });
        }
    }, function (err) { vew.text(JSON.stringify(err)); });
}
let b_s = false;
vew.on('scroll', function(evn) {
    if(b_s){ return; }
    var el = evn.target, p = parseInt((el.scrollHeight-el.clientHeight)/v_t),
        v = parseInt(el.scrollTop/p)+1;
    if(v_n == v || v > v_t){ return; }
    now.innerText = v_n = v;
});
function nextPage(ind = 1){
    var v = v_n + ind; if(1 > v || v > v_t){ return; }
    now.innerText = v_n = v;
    bp.disabled = v == 1; bn.disabled = v == v_t;
    b_s = true; setTimeout(()=>{ b_s = false; },500);
    setTimeout(()=>{
        document.getElementById('pdf-v-'+v).scrollIntoView({behavior:'smooth'});
    },100)
}
isc.on('change',function(event){ pdf_view(); })
isc.on('wheel',function(event){})
bp.addEventListener('click', function(event) { nextPage(-1); })
bn.addEventListener('click', function(event) { nextPage(1); })
upl.addEventListener('change', function(event) {
    const file = event.target.files[0];
    if (file) {
        const reader = new FileReader();
        reader.readAsDataURL(file); 
        reader.onload = function(e) { pdfData = e.target.result; pdf_view(); };
        reader.onerror = function(err) { vew.text(JSON.stringify(err)); };
    }
});
pdfData = {data: atob(
    'JVBERi0xLjcKCjEgMCBvYmogICUgZW50cnkgcG9pbnQKPDwKICAvVHlwZSAvQ2F0YWxvZwog' +
    'IC9QYWdlcyAyIDAgUgo+PgplbmRvYmoKCjIgMCBvYmoKPDwKICAvVHlwZSAvUGFnZXMKICAv' +
    'TWVkaWFCb3ggWyAwIDAgMjAwIDIwMCBdCiAgL0NvdW50IDEKICAvS2lkcyBbIDMgMCBSIF0K' +
    'Pj4KZW5kb2JqCgozIDAgb2JqCjw8CiAgL1R5cGUgL1BhZ2UKICAvUGFyZW50IDIgMCBSCiAg' +
    'L1Jlc291cmNlcyA8PAogICAgL0ZvbnQgPDwKICAgICAgL0YxIDQgMCBSIAogICAgPj4KICA+' +
    'PgogIC9Db250ZW50cyA1IDAgUgo+PgplbmRvYmoKCjQgMCBvYmoKPDwKICAvVHlwZSAvRm9u' +
    'dAogIC9TdWJ0eXBlIC9UeXBlMQogIC9CYXNlRm9udCAvVGltZXMtUm9tYW4KPj4KZW5kb2Jq' +
    'Cgo1IDAgb2JqICAlIHBhZ2UgY29udGVudAo8PAogIC9MZW5ndGggNDQKPj4Kc3RyZWFtCkJU' +
    'CjcwIDUwIFRECi9GMSAxMiBUZgooSGVsbG8sIHdvcmxkISkgVGoKRVQKZW5kc3RyZWFtCmVu' +
    'ZG9iagoKeHJlZgowIDYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDEwIDAwMDAwIG4g' +
    'CjAwMDAwMDAwNzkgMDAwMDAgbiAKMDAwMDAwMDE3MyAwMDAwMCBuIAowMDAwMDAwMzAxIDAw' +
    'MDAwIG4gCjAwMDAwMDAzODAgMDAwMDAgbiAKdHJhaWxlcgo8PAogIC9TaXplIDYKICAvUm9v' +
    'dCAxIDAgUgo+PgpzdGFydHhyZWYKNDkyCiUlRU9G')};
//测试
pdf_view();

//------------截图
function screenImg(viewId,canvasId){
    const vid = $(viewId), h_up = $('#cross-up'), h_down = $('#cross-down');
    const vsc = $(canvasId), v2d = vsc[0].getContext('2d');
    vid.on('mousemove', function(evn) {//鼠标移动
        // 设置十字线的div的XY坐标位置
        h_up.css({'left': (evn.clientX-55) + 'px','top': (evn.clientY-55) + 'px'});
        h_down.css({'left': evn.clientX + 'px','top': evn.clientY + 'px'});
        h_up.html('x:'+evn.clientX+'<br>y:'+evn.clientY);
    });
    let ot = {x:0, y:0, w:1, h:1, t:0, l:0,ox:0,oy:0}, screening = false, canvas;
    //截图框
    let sc = $('#screen-id'), scp = sc.find('span');
    var clearCanvas = function(){
        if(!canvas){ return; }
        screening = false;
        canvas.off('mousemove');
        //sc.css('display','none');
        canvas == undefined; //清空缓存对象
    }
    vid.on('mouseout', clearCanvas);//鼠标移出
    vid.on('mouseup', clearCanvas);//鼠标左键松开
    //绘制截图区域
    let upd = function (){
        sc.css({width: ot.w + 'px', height: ot.h + 'px', left: ot.l + 'px', top: ot.t + 'px'});
    }
    vid.on('mousedown',function(env){//鼠标左键按下
        let tg = $(env.target);
        if(!tg.is('canvas')){ return; }
        canvas = tg;//获取用于展示pdf的canvas
        screening = true;
        //鼠标在元素内坐标 e.offsetX; 鼠标在页面坐标 e.clientX
        ot.x = env.clientX; ot.y = env.clientY; ot.ox = env.offsetX; ot.oy = env.offsetY;
        tg.on('mousemove',function(e){
            if(!screening){ return; }
            let cx = e.clientX, cy = e.clientY;//鼠标
            if( cx >= ot.x ){ ot.w = cx - ot.x ; ot.l = ot.x; }else{ ot.w = ot.x - cx ; ot.l = cx; }
            if( cy >= ot.y ){ ot.h = cy - ot.y ; ot.t = ot.y; }else{ ot.h = ot.y - cy ; ot.t = cy; }
            upd();//绘制截图区域
            let x = cx >= ot.x?ot.ox:e.offsetX, y = cy >= ot.y?ot.oy:e.offsetY;
            //清空 并截取原画布的一部分
            v2d.clearRect(0, 0, vsc.width(), vsc.height());
            vsc.attr({width: ot.w, height: ot.h});
            v2d.drawImage(canvas[0], x, y, ot.w, ot.h, 0, 0, ot.w, ot.h);
            scp.text('w:'+ot.w+',y:'+ot.h);
        })
        sc.css('display', 'block');
    });
    sc.find('button').on('click',function(){
        var imgURL = vsc[0].toDataURL({format: "image/png", quality:1});
        var dlLink = document.createElement('a');
        dlLink.download = '图片下载-'+ (new Date().getTime()) + '.png';
        dlLink.href = imgURL;
        document.body.appendChild(dlLink);
        dlLink.click();
        document.body.removeChild(dlLink);
    });
}
screenImg('#id-page','#view_screen');
})()
<div>
    <input type="file" id="i_file" accept="application/pdf" placeholder="请选择PDF文件"/>
    <button type="button" id="b_pre">上一页</button>
    当前:<span id="s_now">1</span>/<span id="s_total">1</span>
    <button type="button" id="b_next">下一页</button>
    <input type="number" id="i_scale" min="0.5" max="5" step="0.1" value="1.5">缩放</input>
</div>
<div id="id-page" class="page-view">预览区域</div>
<div>
    <!-- canvas中 width="300" 与 style="width:300px;" 效果不一样,style调整会造成截图变形 -->
    <canvas id="view_screen" width="300" height="200" style="border: 1px solid black;"/>
</div>
<div>
    <div id="cross-up" class="crosshair hair-up" style="left: -45px;top: 30px;"></div>
    <div id="cross-down" class="crosshair hair-down" style="left: 10px;top: 85px;"></div>
    <div id="screen-id" class="screen-div" style="width: 0px;height: 0px;">
        <span style="top:-22px;position:absolute;color:red"></span>
        <div style="bottom:-25px;position:absolute;min-width:50px">
            <button type="button">下载</button>
        </div>
    </div>
</div>
html,
body {
    height: 100%;
    margin: 0;
    padding: 0;
}

.page-view {
    border: 1px solid black;
    width: 95%;
    height: 60%;
    overflow: auto;
    padding: 5px;
}

canvas {
    margin: 5px 0;
}

.crosshair {
    position: absolute;
    width: 55px;
    /* 更宽一些以便看到十字线效果 */
    height: 55px;
    /* 更宽一些以便看到十字线效果 */
    background-color: transparent;
    /* 背景透明 */
    pointer-events: none;
    /* 防止十字线挡住鼠标事件 */
    z-index: 1000;
    /* 确保十字线在最上层 */
}

.hair-up {
    color: gray;
    text-align-last: right;
    border-right: 1px dashed gray;
    /* 左边的虚线 */
    border-bottom: 1px dashed gray;
    /* 上边的虚线 */
}

.hair-down {
    border-left: 1px dashed gray;
    /* 左边的虚线 */
    border-top: 1px dashed gray;
    /* 上边的虚线 */
}

.screen-div {
    position: absolute;
    width: 0px;
    height: 0px;
    display: none;
    z-index: 1000;
    background-color: transparent;
    pointer-events: none;
    border: 1px dashed red;
}

本项目引用的自定义外部资源