Solved my self, I will post the solution for future use :
first of all I add an id for each created canvas just after createElement.
canvas.id = "unique_id"
I create 3 button :
<button id="rotation" onClick={rotateCurrentPage} className="pdf-rot">
<button id="zoominbutton" type="button" onClick={zoomIn}>
<button id="zoomoutbutton" type="button" onClick={zoomOut}>
ZoomIn / ZoomOut are juste simple function that update a state for scale
function zoomIn() {
setPdfScale(pdfScale + 0.5);
}
function zoomOut() {
if (pdfScale <= 1) {
return;
}
setPdfScale(pdfScale - 0.5);
}
D'ont dorget to :
const [pdfScale, setPdfScale] = useState(0.5);
The interesting part : rotateCurrentPage :
function rotateCurrentPage() {
if (pdfScale >= 2) {
const visibleCanvas = [];
// Get All created canvas
document.querySelectorAll("[id^=page-]").forEach((canvas) => {
// check if part of canvas is shown in the visible browser
// area
const vc = isVisible(canvas);
// if it is visible we we store visible canvas in an array
if (vc.visible) visibleCanvas.push(vc);
});
// sort visible canvas by percent visibility and get the first
// one
const currentCanvasVisible = visibleCanvas.sort(
(c1, c2) => c2.percent - c1.percent)[0];
// select the canvas with that id
const currentCanvas = document.querySelector(
"#" + currentCanvasVisible.id);
if (currentCanvas) {
// check the current canvas rotation, then add a degree for
// his transform and store that value in state
const deg = getRotationDegrees(currentCanvas) + 90;
rotateCanvas(currentCanvas, deg);
// Push new canvas deg in an array, aka ({page-1 : 90},
// {page-2: 180}...)
setCanvasRotates((canvasRotates) => [...canvasRotates,
{ [currentCanvas.id]: deg },]);
}
}
}
The others functions :
isVisible
function isVisible(canvas) {
const elementRect = canvas.getBoundingClientRect();
let visibilityPercentage = 0;
// Obtenez les dimensions de la fenêtre visible du navigateur
const viewportWidth =
window.innerWidth || document.documentElement.clientWidth;
const viewportHeight =
window.innerHeight || document.documentElement.clientHeight;
// Obtenez le niveau de zoom (ratio entre la largeur de la fenêtre et la largeur intérieure de l'élément)
const zoomLevel = viewportWidth / elementRect.width;
// Calculez les coordonnées de l'élément dans la fenêtre du navigateur avec le niveau de zoom pris en compte
const elementX = elementRect.left * zoomLevel;
const elementY = elementRect.top * zoomLevel;
// Vérifiez si une partie de l'élément est visible en comparant ses coordonnées avec les dimensions de la fenêtre visible
const visible =
elementX < viewportWidth &&
elementX + elementRect.width > 0 &&
elementY < viewportHeight &&
elementY + elementRect.height > 0;
if (visible) {
const visibleWidth =
Math.min(elementX + elementRect.width, viewportWidth) -
Math.max(elementX, 0);
const visibleHeight =
Math.min(elementY + elementRect.height, viewportHeight) -
Math.max(elementY, 0);
const visibleArea = visibleWidth * visibleHeight;
const totalArea = elementRect.width * elementRect.height;
visibilityPercentage = (visibleArea / totalArea) * 100;
}
return { id: canvas.id, visible: visible, percent: visibilityPercentage };
}
getRotationDegrees
function getRotationDegrees(canvas) {
const styles = window.getComputedStyle(canvas);
const transform = styles.transform;
if (transform === "none") {
return 0;
}
const matrix = styles.transform;
let angle = 0;
if (matrix !== "none") {
const values = matrix.split("(")[1].split(")")[0].split(",");
const a = values[0];
const b = values[1];
angle = Math.round(Math.atan2(b, a) * (180 / Math.PI));
}
return angle < 0 ? angle + 360 : angle;
}
rotateCanvas
function rotateCanvas(canvas, deg) {
canvas.style.transform = "rotate(" + deg + "deg)";
}
To mixing business with pleasure, I do clean my component specially useEffect and at the end, I add a function to get canvas rotations degrees so I can rotate my canvas even after zomming in/out
THE COMPLETE COMPONENT
import React, { useEffect, useState } from "react";
import "./suiviOF.css";
let numPages = 0;
export const SuiviOF = () => {
const [pdfScale, setPdfScale] = useState(0.5);
const [canvasRotates, setCanvasRotates] = useState([]);
function canvasWithRotate() {
canvasRotates.forEach((canvas) =>
rotateCanvas(document.querySelector("#" + Object.keys(canvas)
[0]),Object.values(canvas)[0]));
}
function zoomIn() {
setPdfScale(pdfScale + 0.5);
}
function zoomOut() {
if (pdfScale <= 1) {
return;
}
setPdfScale(pdfScale - 0.5);
}
function isVisible(canvas) {
const elementRect = canvas.getBoundingClientRect();
let visibilityPercentage = 0;
// Obtenez les dimensions de la fenêtre visible du navigateur
const viewportWidth =
window.innerWidth || document.documentElement.clientWidth;
const viewportHeight =
window.innerHeight || document.documentElement.clientHeight;
// Obtenez le niveau de zoom (ratio entre la largeur de la fenêtre
// et la largeur intérieure de l'élément)
const zoomLevel = viewportWidth / elementRect.width;
// Calculez les coordonnées de l'élément dans la fenêtre du
// navigateur avec le niveau de zoom pris en compte
const elementX = elementRect.left * zoomLevel;
const elementY = elementRect.top * zoomLevel;
// Vérifiez si une partie de l'élément est visible en comparant
// ses coordonnées avec les dimensions de la fenêtre visible
const visible =
elementX < viewportWidth &&
elementX + elementRect.width > 0 &&
elementY < viewportHeight &&
elementY + elementRect.height > 0;
if (visible) {
const visibleWidth = Math.min(elementX + elementRect.width,
viewportWidth) - Math.max(elementX, 0);
const visibleHeight = Math.min(elementY + elementRect.height,
viewportHeight) - Math.max(elementY, 0);
const visibleArea = visibleWidth * visibleHeight;
const totalArea = elementRect.width * elementRect.height;
visibilityPercentage = (visibleArea / totalArea) * 100;
}
return { id: canvas.id, visible: visible, percent:
visibilityPercentage };
}
function getRotationDegrees(canvas) {
const styles = window.getComputedStyle(canvas);
const transform = styles.transform;
if (transform === "none") {
return 0;
}
const matrix = styles.transform;
let angle = 0;
if (matrix !== "none") {
const values = matrix.split("(")[1].split(")")[0].split(",");
const a = values[0];
const b = values[1];
angle = Math.round(Math.atan2(b, a) * (180 / Math.PI));
}
return angle < 0 ? angle + 360 : angle;
}
function rotateCanvas(canvas, deg) {
canvas.style.transform = "rotate(" + deg + "deg)";
}
function rotateCurrentPage() {
if (pdfScale >= 2) {
const visibleCanvas = [];
// Get All created canvas
document.querySelectorAll("[id^=page-]").forEach((canvas) => {
// check if part of canvas is shown in the visible browser area
const vc = isVisible(canvas);
// if it is visible we we store visible canvas in an array
if (vc.visible) visibleCanvas.push(vc);
});
// sort visible canvas by percent visibility and get the first one
const currentCanvasVisible = visibleCanvas.sort(
(c1, c2) => c2.percent - c1.percent)[0];
// select the canvas with that id
const currentCanvas = document.querySelector("#" +
currentCanvasVisible.id);
if (currentCanvas) {
// check the current canvas rotation, then add a degree for his
// transform and store that value in state
const deg = getRotationDegrees(currentCanvas) + 90;
rotateCanvas(currentCanvas, deg);
// Push new canvas deg in an array, aka ({page-1 : 90}, {page-
// 2:180}...)
setCanvasRotates((canvasRotates) => [...canvasRotates,
{ [currentCanvas.id]: deg },]);
}
}
}
function handlePage(viewer, page, pdfScale, index) {
//This gives us the page's dimensions at full scale
const viewport = page.getViewport({ scale: pdfScale });
//We'll create a canvas for each page to draw it on
const canvas = document.createElement("canvas");
const id = "page-" + index;
canvas.id = id;
canvas.classList.add("page");
const context = canvas.getContext("2d");
canvas.height = viewport.height;
canvas.width = viewport.width;
//Draw it on the canvas
page.render({ canvasContext: context, viewport: viewport });
//Add it to the web page
viewer.appendChild(canvas);
}
async function handlePages(pdfScale) {
const pdfJS = await import("pdfjs-dist/build/pdf");
pdfJS.GlobalWorkerOptions.workerSrc =
window.location.origin + "/pdf.worker.min.js";
const pdf = await pdfJS.getDocument("example.pdf").promise;
numPages = pdf.numPages;
const loader = document.querySelector("#load-wrapper");
const viewer = document.querySelector("#pdf-viewer");
loader.classList.remove("d-none");
// Empty viewer wrapper everytime to update viewport
viewer.replaceChildren();
for (let i = 1; i <= pdf.numPages; i++) {
await pdf.getPage(i).then((page) => handlePage(viewer, page,
pdfScale, i));
}
canvasWithRotate();
loader.classList.add("d-none");
}
useEffect(() => {(async function () {await handlePages(pdfScale);})();
}, [pdfScale]);
return (
<>
<div id="load-wrapper" className="d-none">
<div id="loader">
<div className="lds-roller">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
</div>
<div className="pdf-nav-wrapper">
<div className="pdf-nav">
<button id="rotation" onClick={rotateCurrentPage}
className="pdf-rot">Rotate</button>
<button id="zoominbutton" type="button" onClick={zoomIn}>
zoom in</button>
<button id="zoomoutbutton" type="button" onClick={zoomOut}>
zoom out</button>
</div>
</div>
<div className="pdf-viewer-wrapper">
<div id="pdf-viewer" />
</div>
<canvas style={{ height: "100vh" }} id="canvas" className="d-
none" />
</>);
};
Tested and works fine in chrome, it may be some adaptation if you use other browsers