index.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import { insertScript } from '../../core'
  2. const PDFJS_BUNDLE = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.0.279/build/pdf.min.js'
  3. const WORKER_BUNDLE = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.0.279/build/pdf.worker.min.js'
  4. class PDFViewer {
  5. constructor (el) {
  6. const {
  7. url,
  8. hidePaginator,
  9. hideLoader,
  10. scale,
  11. pageNum
  12. } = el.dataset
  13. if (url == null) {
  14. throw new Error('Cannot load PDF! Attribute `data-url` is not set.')
  15. }
  16. // props
  17. this.url = url
  18. this.hidePaginator = hidePaginator !== 'false'
  19. this.hideLoader = hideLoader !== 'false'
  20. this.scale = scale || 3
  21. // initial state
  22. this.pageNum = parseInt(pageNum, 10) || 1
  23. this.loaded = false
  24. this.pageRendering = false
  25. this.pageNumPending = null
  26. // DOM elements
  27. this.canvas = el.getElementsByClassName('pdf-canvas')[0]
  28. if (this.canvas == null) {
  29. throw new Error('canvas element not found!')
  30. };
  31. this.paginator = el.getElementsByClassName('paginator')[0]
  32. this.loadingWrapper = el.getElementsByClassName('loading-wrapper')[0]
  33. this.next = el.getElementsByClassName('next')[0]
  34. this.prev = el.getElementsByClassName('prev')[0]
  35. this.pageNum = el.getElementsByClassName('page-num')[0]
  36. this.pageCount = el.getElementsByClassName('page-count')[0]
  37. // context
  38. this.ctx = this.canvas.getContext('2d')
  39. // events
  40. this.next.addEventListener('click', this.handleNextPage.bind(this))
  41. this.prev.addEventListener('click', this.handlePrevPage.bind(this))
  42. this.showPaginator()
  43. this.showLoader()
  44. this.loadPDF()
  45. }
  46. /**
  47. * If we haven't disabled the loader, show loader and hide canvas
  48. */
  49. showLoader () {
  50. if (this.hideLoader) return
  51. this.loadingWrapper.style.display = 'flex'
  52. this.canvas.style.display = 'none'
  53. }
  54. /**
  55. * If we haven't disabled the paginator, show paginator
  56. */
  57. showPaginator () {
  58. if (this.hidePaginator) return
  59. this.paginator.style.display = 'block'
  60. }
  61. /**
  62. * Hides loader and shows canvas
  63. */
  64. showContent () {
  65. this.loadingWrapper.style.display = 'none'
  66. this.canvas.style.display = 'block'
  67. }
  68. /**
  69. * Asynchronously downloads PDF.
  70. */
  71. async loadPDF () {
  72. this.pdfDoc = await window.pdfjsLib.getDocument(this.url).promise
  73. this.pageCount.textContent = this.pdfDoc.numPages
  74. // If the user passed in a number that is out of range, render the last page.
  75. if (this.pageNum > this.pdfDoc.numPages) {
  76. this.pageNum = this.pdfDoc.numPages
  77. }
  78. this.renderPage(this.pageNum)
  79. }
  80. /**
  81. * Get page info from document, resize canvas accordingly, and render page.
  82. * @param num Page number.
  83. */
  84. async renderPage (num) {
  85. this.pageRendering = true
  86. const page = await this.pdfDoc.getPage(num)
  87. const viewport = page.getViewport({ scale: this.scale })
  88. this.canvas.height = viewport.height
  89. this.canvas.width = viewport.width
  90. // Wait for rendering to finish
  91. await page.render({
  92. canvasContext: this.ctx,
  93. viewport
  94. }).promise
  95. this.pageRendering = false
  96. this.showContent()
  97. if (this.pageNumPending !== null) {
  98. // New page rendering is pending
  99. this.renderPage(this.pageNumPending)
  100. this.pageNumPending = null
  101. }
  102. // Update page counters
  103. this.pageNum.textContent = num
  104. }
  105. /**
  106. * If another page rendering in progress, waits until the rendering is
  107. * finished. Otherwise, executes rendering immediately.
  108. */
  109. queueRenderPage (num) {
  110. if (this.pageRendering) {
  111. this.pageNumPending = num
  112. } else {
  113. this.renderPage(num)
  114. }
  115. }
  116. /**
  117. * Displays previous page.
  118. */
  119. handlePrevPage () {
  120. if (this.pageNum <= 1) {
  121. return
  122. }
  123. this.pageNum--
  124. this.queueRenderPage(this.pageNum)
  125. }
  126. /**
  127. * Displays next page.
  128. */
  129. handleNextPage () {
  130. if (this.pageNum >= this.pdfDoc.numPages) {
  131. return
  132. }
  133. this.pageNum++
  134. this.queueRenderPage(this.pageNum)
  135. }
  136. }
  137. insertScript('pdfjs', PDFJS_BUNDLE, () => {
  138. window.pdfjsLib.GlobalWorkerOptions.workerSrc = WORKER_BUNDLE
  139. Array.from(document.getElementsByClassName('pdf-viewer')).forEach(el => new PDFViewer(el))
  140. })