流光年华|VUE 实现高性能的 PDF 在线预览( 三 )

<= pdf.numPages; i++) {pdf.getPage(i).then((page) => {const pagination = num * 5 + i;if (!this.files.paginations[pagination]) {/** 存储分页信息(宽/高/ID等 - ID可用于判断是否已经渲染及清除DOM操作) */this.files.paginations[pagination] = {} as any;}this.files.paginations[pagination].id = Utils.uid();const renderFinish = this.renderPage(pagination, page, speaker);if (renderFinish) {renderFinish.then(() => {if (i === pdf.numPages) {/*** 1.当前为最后一页或倒数第2页, 请求下一分片* 2. 当前为第一页或第2页, 请求上一分片*/const left = showPage % 5;if (left === 0 ||left === 4) {/** 回调 - 请求下一个分片 */if (num + 1 <= this.files.total) {this.getFragmentation(fid,md5,token,num + 1,showPage,speaker).then(() => {/*** 下一个分片请求成功后的处理* 根据剩余个数, 决定继续渲染下一分片的1页还是2页*/this.getFragmentationSuccess(showPage,left ? 1 : 0);});}} else if (left === 1 ||left === 2) {/** 回调 - 请求上一个分片 */if (num - 1 >= 0) {this.getFragmentation(fid,md5,token,num - 1,showPage,speaker).then(() => {this.getFragmentationSuccess(showPage,left === 2 ? 1 : 0,'prev');});}}/*** 渲染完成后返回.* 因为我需要5页全部渲染完成后 , 初始化每一页上面的涂鸦功能,* 所以我在最后才返回 Promise, 以保证时序的正确性.* 若仅仅是展示, 没有其它功能的话, 无需返回 Promise.*/resolve();}});}});}});}); } return promise;}

  1. 渲染页面
/** * 渲染分页内容.* @param pagination 第N页 * @param page 分页属性 * @param speaker 是否为主讲人 * @param type 类型(上下页区分) */protected renderPage( pagination: number, page: any, speaker = false, type = 'next'): Promise | void { const documents = this.getContainer() as HTMLDivElement; if (documents) {const item = this.createPage(pagination),pageView = page.view,scale = this.getScale(pageView, speaker, item),viewport = page.getViewport({scale});if (this.files.page !== pagination) item.style.display = 'none';/** 这个就是保存一些用得到的属性, 具体实现代码就不贴出来了. */this.setPaginationAttrs(pagination,viewport,pageView,scale);/** 创建元素 */const canvas = document.createElement('canvas'),context = canvas.getContext('2d');canvas.width = viewport.width;canvas.height = viewport.height;item.appendChild(canvas);/** 判断是要插入还是追加元素 */if (type === 'next') documents.appendChild(item);else if (documents.firstChild) documents.insertBefore(item, documents.firstChild);/** 渲染文档 */const renderContext = {canvasContext: context,viewport};return page.render(renderContext).promise; }}
  1. 获取缩放比
/** * 获取缩放比. * @param origin 文档原始尺寸 * @param speaker 是否为主讲人(主讲人默认以宽为基准) * @param wrapper 画布容器(超出宽度的话, 需要手动设置高度) * @param width 待变更元素的宽度 */protected getScale( origin: any, speaker: boolean, wrapper?: HTMLDivElement, width?: number): number { width = width ?? 0; const documents = this.getContainer() as HTMLDivElement; if (documentsif (speaker) {/*** 左右两边增加了一些偏移量(主讲人与普通用户大小不一样, 这个函数的代码也没啥好贴)* 主讲人默认是以屏幕宽度为基准进行文档缩放的.*/const offsetWidth = this.getOffsetWidth(width);return Math.round(offsetWidth / origin[2] * 100) / 100; } else {/*** 非主讲人为了与主讲人显示内容一致, 默认采用高度为基准, 但有特殊情况,* 就是高度保证一致的情况下, 宽度却超出了屏幕的可视区域, 这时候就要将* 文档显示区域所在的容器高度进一步缩短, 保证宽度是在可视区域内, 具体* 实现就看 getHeightAndScale 这个方法了.*/const heightAndScale = this.getHeightAndScale(documents, origin),scale = heightAndScale.scale;if (wrapperreturn scale; }}


推荐阅读