Snippets Ebook PDF Viewer – Bootstrap 5

🏷️ Extraits & Composants HTML 📅 03/04/2026 17:00:00 👤 Mezgani said
Bootstrap 5 Pdf Viewer Ebook Template Pdf Embed Snippets Html

Exemples de présentation d’eBook PDF avec intégration d’un visualiseur PDF responsive en Bootstrap 5.

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8" />
    <meta name="copyright" content="MEZGANI Said" />
    <meta name="author" content="AngularForAll" />
    <meta name="robots" content="noindex, nofollow" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Snippets Ebook Pdf Bootstrap 5 01 | AngularForAll</title>
<!-- Bootstrap 5 CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    <!-- Bootstrap Icons -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
    <!-- Google Fonts -->
    <link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300;14..32,400;14..32,500;14..32,600;14..32,700&display=swap" rel="stylesheet">

    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Inter', sans-serif;
            background: #f5f5f0;
            overflow-x: hidden;
        }

        /* Navbar Style */
        .navbar {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            box-shadow: 0 2px 20px rgba(0,0,0,0.1);
            padding: 0.8rem 0;
        }

        .navbar-brand {
            font-weight: 700;
            font-size: 1.5rem;
            color: white !important;
        }

        /* Sidebar Bibliothèque */
        .library-sidebar {
            background: white;
            border-right: 1px solid #e0e0e0;
            height: calc(100vh - 65px);
            position: fixed;
            top: 65px;
            left: 0;
            width: 280px;
            overflow-y: auto;
            padding: 1.5rem;
            transition: all 0.3s ease;
            z-index: 999;
            box-shadow: 2px 0 10px rgba(0,0,0,0.05);
        }

        /* Cartes des livres */
        .book-card {
            background: white;
            border-radius: 12px;
            padding: 1rem;
            margin-bottom: 1rem;
            cursor: pointer;
            transition: all 0.3s ease;
            border: 2px solid transparent;
            box-shadow: 0 2px 8px rgba(0,0,0,0.08);
        }

        .book-card:hover {
            transform: translateX(5px);
            box-shadow: 0 5px 15px rgba(0,0,0,0.15);
            background: #f8f9fa;
        }

        .book-card.active {
            border-color: #667eea;
            background: linear-gradient(135deg, rgba(102,126,234,0.1) 0%, rgba(118,75,162,0.1) 100%);
        }

        .book-cover {
            width: 60px;
            height: 80px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            border-radius: 8px;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-size: 1.8rem;
        }

        .book-title {
            font-weight: 600;
            font-size: 0.9rem;
            margin-bottom: 0.25rem;
        }

        .book-author {
            font-size: 0.75rem;
            color: #7f8c8d;
        }

        /* Zone de lecture PDF */
        .pdf-viewer {
            margin-left: 280px;
            padding: 20px;
            min-height: calc(100vh - 65px);
            background: #e8e6e1;
        }

        .pdf-container {
            background: white;
            border-radius: 16px;
            box-shadow: 0 10px 40px rgba(0,0,0,0.1);
            overflow: hidden;
        }

        .pdf-toolbar {
            background: #2c3e50;
            color: white;
            padding: 1rem;
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
            gap: 0.5rem;
        }

        .pdf-controls {
            display: flex;
            gap: 0.5rem;
            align-items: center;
            flex-wrap: wrap;
        }

        .pdf-controls button, .zoom-controls button {
            background: rgba(255,255,255,0.15);
            border: none;
            color: white;
            padding: 0.5rem 1rem;
            border-radius: 8px;
            transition: all 0.3s;
        }

        .pdf-controls button:hover, .zoom-controls button:hover {
            background: rgba(255,255,255,0.3);
            transform: translateY(-2px);
        }

        .page-input {
            width: 70px;
            text-align: center;
            background: rgba(255,255,255,0.15);
            border: 1px solid rgba(255,255,255,0.3);
            color: white;
            border-radius: 8px;
            padding: 0.5rem;
        }

        .page-input:focus {
            outline: none;
            background: rgba(255,255,255,0.25);
        }

        .pdf-canvas-container {
            background: #525659;
            padding: 2rem;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 500px;
            overflow: auto;
        }

        canvas {
            box-shadow: 0 5px 20px rgba(0,0,0,0.2);
            max-width: 100%;
            height: auto;
            display: block;
            margin: 0 auto;
        }

        /* Mode nuit */
        body.night-mode {
            background: #1a1a1a;
        }

        body.night-mode .library-sidebar {
            background: #2c2c2c;
            border-color: #404040;
        }

        body.night-mode .book-card {
            background: #363636;
            color: #e0e0e0;
        }

        body.night-mode .book-card.active {
            background: #2c3e50;
        }

        body.night-mode .book-title {
            color: #ecf0f1;
        }

        body.night-mode .pdf-viewer {
            background: #2c2c2c;
        }

        body.night-mode .pdf-container {
            background: #363636;
        }

        /* Progress bar */
        .reading-progress {
            position: fixed;
            top: 65px;
            left: 0;
            width: 100%;
            height: 3px;
            background: rgba(102,126,234,0.2);
            z-index: 1000;
        }

        .progress-bar-custom {
            height: 100%;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            transition: width 0.3s ease;
            width: 0%;
        }

        /* Loading spinner */
        .loading {
            text-align: center;
            padding: 2rem;
        }

        /* Message d'erreur */
        .error-message {
            background: #dc3545;
            color: white;
            padding: 1rem;
            border-radius: 8px;
            text-align: center;
            margin: 1rem;
        }

        /* Responsive */
        @media (max-width: 768px) {
            .library-sidebar {
                transform: translateX(-100%);
                position: fixed;
                top: 65px;
                left: 0;
                width: 280px;
                height: calc(100vh - 65px);
                z-index: 1000;
            }

            .library-sidebar.show {
                transform: translateX(0);
            }

            .pdf-viewer {
                margin-left: 0;
                padding: 10px;
            }

            .pdf-toolbar {
                flex-direction: column;
            }

            .pdf-controls {
                justify-content: center;
            }
        }

        /* Animations */
        @keyframes fadeIn {
            from {
                opacity: 0;
                transform: translateY(20px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        .book-card {
            animation: fadeIn 0.5s ease forwards;
        }

        /* Scrollbar */
        ::-webkit-scrollbar {
            width: 8px;
            height: 8px;
        }

        ::-webkit-scrollbar-track {
            background: #f1f1f1;
        }

        ::-webkit-scrollbar-thumb {
            background: #888;
            border-radius: 4px;
        }

        ::-webkit-scrollbar-thumb:hover {
            background: #555;
        }

        /* Bouton toggle sidebar mobile */
        .toggle-sidebar-btn {
            background: rgba(255,255,255,0.2);
            border: none;
            color: white;
            padding: 0.5rem 1rem;
            border-radius: 8px;
        }
    </style>
</head>
<body>

    <!-- Progress Bar -->
    <div class="reading-progress">
        <div class="progress-bar-custom" id="readingProgress"></div>
    </div>

    <!-- Navbar -->
    <nav class="navbar navbar-expand-lg fixed-top">
        <div class="container-fluid">
            <a class="navbar-brand" href="#">
                <i class="bi bi-book-half"></i> Ebook Reader Pro
            </a>
            <button class="toggle-sidebar-btn d-md-none" id="toggleLibraryBtn">
                <i class="bi bi-list fs-5"></i> Bibliothèque
            </button>
            <div class="ms-auto d-flex gap-2">
                <button class="btn btn-light btn-sm" id="fullscreenBtn" title="Plein écran">
                    <i class="bi bi-arrows-fullscreen"></i>
                </button>
                <button class="btn btn-light btn-sm" id="nightModeBtn" title="Mode nuit">
                    <i class="bi bi-moon"></i>
                </button>
            </div>
        </div>
    </nav>

    <div class="row g-0">
        <!-- Bibliothèque latérale -->
        <div class="library-sidebar" id="librarySidebar">
            <h5 class="mb-3">
                <i class="bi bi-journal-bookmark-fill"></i> Ma Bibliothèque
            </h5>
            <div id="bookList">
                <!-- Les livres seront chargés ici -->
            </div>
        </div>

        <!-- Zone de lecture -->
        <div class="pdf-viewer" id="pdfViewer">
            <div class="pdf-container">
                <div class="pdf-toolbar">
                    <div class="pdf-controls">
                        <button id="prevPage">
                            <i class="bi bi-chevron-left"></i> Précédent
                        </button>
                        <span>
                            Page <input type="number" id="pageNum" class="page-input" value="1" min="1">
                            / <span id="pageCount">0</span>
                        </span>
                        <button id="nextPage">
                            Suivant <i class="bi bi-chevron-right"></i>
                        </button>
                    </div>
                    <div class="pdf-controls">
                        <button id="zoomOut" title="Zoom arrière">
                            <i class="bi bi-zoom-out"></i>
                        </button>
                        <span id="zoomLevel" style="min-width: 50px;">100%</span>
                        <button id="zoomIn" title="Zoom avant">
                            <i class="bi bi-zoom-in"></i>
                        </button>
                    </div>
                </div>
                <div class="pdf-canvas-container" id="canvasContainer">
                    <div class="loading" id="loadingIndicator">
                        <div class="spinner-border text-light" role="status">
                            <span class="visually-hidden">Chargement...</span>
                        </div>
                        <p class="mt-2 text-light">Chargement du PDF...</p>
                    </div>
                    <canvas id="pdfCanvas" style="display: none;"></canvas>
                </div>
            </div>
        </div>
    </div>

    <!-- PDF.js Library -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.min.js"></script>
    <script>
        pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.worker.min.js';
    </script>
    <script>
        // Configuration - Utilisation d'un PDF de démonstration valide
        const DEMO_PDF_URL = 'public/exemple.pdf';

        // Bibliothèque de livres (avec des PDFs valides)
        const books = [
            {
                id: 1,
                title: "Introduction à JavaScript",
                author: "Mozilla Foundation",
                cover: "📘",
                url: DEMO_PDF_URL,
                progress: 0
            },
            {
                id: 2,
                title: "Guide CSS Moderne",
                author: "Web.dev",
                cover: "🎨",
                url: DEMO_PDF_URL,
                progress: 0
            },
            {
                id: 3,
                title: "HTML5 Avancé",
                author: "W3C",
                cover: "🌐",
                url: DEMO_PDF_URL,
                progress: 0
            },
            {
                id: 4,
                title: "Bootstrap 5 Mastery",
                author: "Twitter",
                cover: "🚀",
                url: DEMO_PDF_URL,
                progress: 0
            },
            {
                id: 5,
                title: "React pour Débutants",
                author: "Facebook",
                cover: "⚛️",
                url: DEMO_PDF_URL,
                progress: 0
            }
        ];

        // Variables globales
        let pdfDoc = null;
        let currentPage = 1;
        let currentScale = 1.2;
        let totalPages = 0;
        let currentBookId = 1;

        // Initialisation
        async function init() {
            loadBookList();
            await loadPDF(DEMO_PDF_URL);
            attachEventListeners();
            loadSavedSettings();
        }

        // Charger la liste des livres
        function loadBookList() {
            const bookListDiv = document.getElementById('bookList');
            bookListDiv.innerHTML = books.map(book => `
                <div class="book-card" data-id="${book.id}" data-url="${book.url}">
                    <div class="d-flex align-items-center">
                        <div class="book-cover me-3">
                            ${book.cover}
                        </div>
                        <div class="flex-grow-1">
                            <div class="book-title">${book.title}</div>
                            <div class="book-author">${book.author}</div>
                            <div class="progress mt-2" style="height: 4px;">
                                <div class="progress-bar bg-success" style="width: ${book.progress}%"></div>
                            </div>
                            <small class="text-muted">${book.progress}% lu</small>
                        </div>
                    </div>
                </div>
            `).join('');

            // Ajouter les événements de clic
            document.querySelectorAll('.book-card').forEach(card => {
                card.addEventListener('click', async () => {
                    document.querySelectorAll('.book-card').forEach(c => c.classList.remove('active'));
                    card.classList.add('active');
                    const url = card.dataset.url;
                    const id = parseInt(card.dataset.id);
                    currentBookId = id;
                    await loadPDF(url);

                    // Fermer la sidebar sur mobile
                    if (window.innerWidth < 768) {
                        document.getElementById('librarySidebar').classList.remove('show');
                    }
                });
            });

            // Activer le premier livre
            if (document.querySelector('.book-card')) {
                document.querySelector('.book-card').classList.add('active');
            }
        }

        // Charger un PDF
        async function loadPDF(url) {
            const loadingIndicator = document.getElementById('loadingIndicator');
            const canvas = document.getElementById('pdfCanvas');

            try {
                // Afficher le chargement
                loadingIndicator.style.display = 'block';
                canvas.style.display = 'none';

                // Charger le PDF
                const loadingTask = pdfjsLib.getDocument(url);
                pdfDoc = await loadingTask.promise;
                totalPages = pdfDoc.numPages;

                document.getElementById('pageCount').textContent = totalPages;
                document.getElementById('pageNum').max = totalPages;
                document.getElementById('pageNum').value = 1;

                currentPage = 1;
                await renderPage(currentPage);

                // Cacher le chargement
                loadingIndicator.style.display = 'none';
                canvas.style.display = 'block';

            } catch (error) {
                console.error('Erreur de chargement PDF:', error);
                loadingIndicator.innerHTML = `
                    <div class="alert alert-danger">
                        <i class="bi bi-exclamation-triangle-fill"></i>
                        Erreur de chargement du PDF. Vérifiez votre connexion internet.
                    </div>
                `;
            }
        }

        // Rendre une page
        async function renderPage(pageNum) {
            if (!pdfDoc) return;

            try {
                const page = await pdfDoc.getPage(pageNum);
                const viewport = page.getViewport({ scale: currentScale });
                const canvas = document.getElementById('pdfCanvas');
                const context = canvas.getContext('2d');

                canvas.height = viewport.height;
                canvas.width = viewport.width;

                const renderContext = {
                    canvasContext: context,
                    viewport: viewport
                };

                await page.render(renderContext).promise;
                updateProgress(pageNum);

            } catch (error) {
                console.error('Erreur de rendu:', error);
            }
        }

        // Mettre à jour la progression
        function updateProgress(pageNum) {
            if (totalPages > 0) {
                const progress = (pageNum / totalPages) * 100;
                document.getElementById('readingProgress').style.width = `${progress}%`;

                // Sauvegarder la progression
                localStorage.setItem(`book_${currentBookId}_page`, pageNum);
                localStorage.setItem(`book_${currentBookId}_progress`, progress);
            }
        }

        // Page suivante
        async function nextPage() {
            if (currentPage < totalPages) {
                currentPage++;
                document.getElementById('pageNum').value = currentPage;
                await renderPage(currentPage);
            }
        }

        // Page précédente
        async function prevPage() {
            if (currentPage > 1) {
                currentPage--;
                document.getElementById('pageNum').value = currentPage;
                await renderPage(currentPage);
            }
        }

        // Zoom avant
        async function zoomIn() {
            if (currentScale < 3) {
                currentScale += 0.2;
                document.getElementById('zoomLevel').textContent = `${Math.round(currentScale * 100)}%`;
                await renderPage(currentPage);
            }
        }

        // Zoom arrière
        async function zoomOut() {
            if (currentScale > 0.6) {
                currentScale -= 0.2;
                document.getElementById('zoomLevel').textContent = `${Math.round(currentScale * 100)}%`;
                await renderPage(currentPage);
            }
        }

        // Mode nuit
        function toggleNightMode() {
            document.body.classList.toggle('night-mode');
            const nightBtn = document.getElementById('nightModeBtn');
            if (document.body.classList.contains('night-mode')) {
                nightBtn.innerHTML = '<i class="bi bi-sun"></i>';
                localStorage.setItem('nightMode', 'true');
            } else {
                nightBtn.innerHTML = '<i class="bi bi-moon"></i>';
                localStorage.setItem('nightMode', 'false');
            }
        }

        // Plein écran
        function toggleFullscreen() {
            const viewer = document.getElementById('pdfViewer');
            if (!document.fullscreenElement) {
                viewer.requestFullscreen();
            } else {
                document.exitFullscreen();
            }
        }

        // Charger les paramètres sauvegardés
        function loadSavedSettings() {
            const nightMode = localStorage.getItem('nightMode');
            if (nightMode === 'true') {
                document.body.classList.add('night-mode');
                document.getElementById('nightModeBtn').innerHTML = '<i class="bi bi-sun"></i>';
            }
        }

        // Attacher les événements
        function attachEventListeners() {
            document.getElementById('prevPage').addEventListener('click', prevPage);
            document.getElementById('nextPage').addEventListener('click', nextPage);
            document.getElementById('zoomIn').addEventListener('click', zoomIn);
            document.getElementById('zoomOut').addEventListener('click', zoomOut);
            document.getElementById('nightModeBtn').addEventListener('click', toggleNightMode);
            document.getElementById('fullscreenBtn').addEventListener('click', toggleFullscreen);

            document.getElementById('pageNum').addEventListener('change', async function() {
                let pageNum = parseInt(this.value);
                if (pageNum >= 1 && pageNum <= totalPages) {
                    currentPage = pageNum;
                    await renderPage(currentPage);
                } else {
                    this.value = currentPage;
                }
            });

            // Toggle sidebar mobile
            document.getElementById('toggleLibraryBtn').addEventListener('click', () => {
                document.getElementById('librarySidebar').classList.toggle('show');
            });

            // Raccourcis clavier
            document.addEventListener('keydown', (e) => {
                if (e.key === 'ArrowLeft') {
                    prevPage();
                } else if (e.key === 'ArrowRight') {
                    nextPage();
                } else if (e.key === '+' || e.key === '=') {
                    zoomIn();
                } else if (e.key === '-') {
                    zoomOut();
                }
            });

            // Fermer la sidebar en cliquant à l'extérieur sur mobile
            document.addEventListener('click', (e) => {
                if (window.innerWidth < 768) {
                    const sidebar = document.getElementById('librarySidebar');
                    const toggleBtn = document.getElementById('toggleLibraryBtn');
                    if (!sidebar.contains(e.target) && !toggleBtn.contains(e.target) && sidebar.classList.contains('show')) {
                        sidebar.classList.remove('show');
                    }
                }
            });
        }

        // Démarrer l'application
        init();
    </script>
</body>
</html>

Ouvrir cet aperçu dans un nouvel onglet du navigateur

🔗 Ouvrir dans le navigateur