Reputation: 11
I’m building a PDF viewer on a webpage using PDF.js, and I’m facing an issue with implementing both scrolling and zooming features. The user should be able to:
Scroll through the PDF pages (already working). Zoom in/out with pinch gestures (mobile) and double tap (mobile). The zoom should happen centered around the position where the user taps or pinches on the canvas.
Here is my code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable= no">
<title>Pdf reader</title>
</head>
<body class="mobile">
<style type="text/css">
body{margin: 0; padding: 0; outline: none !important; font-family: sans-serif; -webkit-user-select: none !important; -ms-user-select: none !important; user-select: none !important; background: #181212;}
#pdf-container { display: flex; flex-direction: column; gap: 20px; margin-top: 40px;}
#pdf-container canvas { display: block; margin: 0 auto;}
div.container_tool_bar{position: fixed; top: 0; left: 0px; z-index: 100; padding: 10px; border: none; width: calc(100svw - 20px); display: flex; justify-content: space-between; background: #181212;}
div.container_tool_bar button#next{position: relative; border: 1px solid #9F7540; padding: 6px 10px; transition: all 0.3s ease; font-weight: 600; color: #9F7540; font-size: 14px; text-transform: uppercase; font-family: "Montserrat", Arial, sans-serif; background: transparent; cursor: pointer;}
div.container_tool_bar button#prev{position: relative; border: 1px solid #9F7540; padding: 6px 10px; transition: all 0.3s ease; font-weight: 600; color: #9F7540; font-size: 14px; text-transform: uppercase; font-family: "Montserrat", Arial, sans-serif; background: transparent; cursor: pointer;}
div.container_tool_bar div.container_count{position: relative; padding: 6px 10px; transition: all 0.3s ease; font-weight: 600; color: #9F7540 !important; font-size: 14px; text-transform: uppercase; font-family: "Montserrat", Arial, sans-serif; }
div.container_tool_bar div.container_count a{color: #9F7540 !important;}
body.mobile #pdf-container canvas{width: 100%;}
</style>
<script src="//mozilla.github.io/pdf.js/build/pdf.mjs" type="module"></script>
<script type="module">
var url ="https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf";
// Loaded via <script> tag, create shortcut to access PDF.js exports.
var { pdfjsLib } = globalThis;
// The workerSrc property shall be specified.
pdfjsLib.GlobalWorkerOptions.workerSrc = '//mozilla.github.io/pdf.js/build/pdf.worker.mjs';
var pdfDoc = null;
var pages = []; // To store the references to each canvas for navigation
var pageNum = 1; // Initial page number
/**
* Get page info from document, resize canvas accordingly, and render page.
* @param num Page number.
*/
function renderPage(num) {
pdfDoc.getPage(num).then(function(page) {
let viewport;
// Si mobile, calculer l'échelle pour que la largeur corresponde à l'écran
if (document.body.classList.contains('mobile')) {
const screenWidth = window.innerWidth; // Largeur de l'écran
viewport = page.getViewport({ scale: 2 }); // Double échelle sur desktop
} else {
viewport = page.getViewport({ scale: 2 }); // Double échelle sur desktop
}
// Créer un élément canvas pour chaque page et l'ajouter au conteneur
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.height = viewport.height;
canvas.width = viewport.width;
document.getElementById("pdf-container").appendChild(canvas);
// Rendre la page PDF dans le contexte du canvas
var renderContext = {
canvasContext: ctx,
viewport: viewport,
};
var renderTask = page.render(renderContext);
// Stocker la référence du canvas pour une utilisation ultérieure
pages.push(canvas);
// Attendre la fin du rendu
renderTask.promise.then(function () {
console.log("Page rendue : " + num);
});
});
}
/**
* Asynchronously downloads PDF.
*/
pdfjsLib.getDocument(url).promise.then(function(pdfDoc_) {
pdfDoc = pdfDoc_;
// document.getElementById('page_count').textContent = pdfDoc.numPages;
// Render all pages
for (var i = 1; i <= pdfDoc.numPages; i++) {
renderPage(i);
}
});
/**
* Function to scroll to a specific page
*/
function scrollToPage(pageNum) {
var canvas = pages[pageNum - 1]; // Get the canvas for the selected page
if (canvas) {
// Scroll the page to the top of the selected canvas
window.scrollTo({
top: canvas.offsetTop, // Adjust if you want space above the page
behavior: 'smooth' // Smooth scroll
});
}
}
/**
* Function to determine the current visible page based on scroll position
*/
function getCurrentPage() {
var currentPage = 1;
for (var i = 0; i < pages.length; i++) {
console.log("Element", pages[i], "is in viewport:", isElementInViewport(pages[i]));
if (isElementInViewport(pages[i])) {
currentPage = i + 1;
break;
}
}
return currentPage;
}
/**
* Check if an element is currently in the viewport
*/
function isElementInViewport(el) {
var rect = el.getBoundingClientRect();
// Vérifier si l'élément est visible même partiellement dans la fenêtre
return rect.top < window.innerHeight && rect.bottom >= 0 && rect.left < window.innerWidth && rect.right >= 0;
}
// Add event listeners to navigation buttons
document.getElementById('prev').addEventListener('click', function() {
// Get the current visible page number
var currentPage = getCurrentPage();
console.log(currentPage);
pageNum = currentPage - 1;
scrollToPage(pageNum);
});
document.getElementById('next').addEventListener('click', function() {
// Get the current visible page number
var currentPage = getCurrentPage();
console.log(currentPage);
pageNum = currentPage + 1;
scrollToPage(pageNum);
});
</script>
<div class="container_tool_bar">
<button id="prev">Précédente</button>
<div class="container_count">
<a href="https://www.google.com/">Retour au site</a>
<!-- <span>Total Pages: <span id="page_count"></span></span> -->
</div>
<button id="next">Suivante</button>
</div>
<div id="pdf-container"></div>
</body>
</html>
I’ve used the getBoundingClientRect method to track the canvas position and tried adjusting the scale when pinch gestures or double tap events occur, but I haven't been able to achieve the correct zooming behavior.
I also explored event listeners for touch and mouse events, but my implementation doesn't seem to properly scale the canvas from the pointer’s location.
Upvotes: 0
Views: 39