Bouton retour en haut pour Elementor

Bouton « Retour en haut » avec remplissage progressif en fonction du défilement.
Utilise automatiquement la couleur principale d’Elementor.

Il suffit de copier-coller le code dans un fichier PHP et de le placer dans votre dossier mu-plugins.

				
					<?php
/**
 * Plugin Name: Wycan Back to Top
 * Description: Bouton "Back to Top" avec remplissage progressif basé sur le scroll. Utilise automatiquement la couleur principale d'Elementor.
 * Version: 1.0.0
 * Author: Wycan
 * Author URI: https://wycan.fr
 * License: GPL-2.0+
 *
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class Wycan_Back_To_Top {

    public function __construct() {
        add_action( 'wp_footer', [ $this, 'render_button' ] );
        add_action( 'wp_footer', [ $this, 'inline_styles' ] );
        add_action( 'wp_footer', [ $this, 'inline_scripts' ] );
    }

    /**
     * Récupère la couleur principale définie dans Elementor.
     */
    private function get_primary_color(): string {
        $default = '#000000';

        if ( ! did_action( 'elementor/loaded' ) ) {
            return $default;
        }

        // Elementor stocke le kit (Global Colors) dans un post type dédié.
        $kit = \Elementor\Plugin::$instance->kits_manager->get_active_kit_for_frontend();

        if ( ! $kit ) {
            return $default;
        }

        $settings = $kit->get_settings();

        // Les couleurs globales sont dans system_colors.
        if ( ! empty( $settings['system_colors'] ) && is_array( $settings['system_colors'] ) ) {
            foreach ( $settings['system_colors'] as $color ) {
                if ( isset( $color['_id'] ) && $color['_id'] === 'primary' && ! empty( $color['color'] ) ) {
                    return $color['color'];
                }
            }
        }

        // Fallback : custom_colors si la couleur primaire a été définie là.
        if ( ! empty( $settings['custom_colors'] ) && is_array( $settings['custom_colors'] ) ) {
            foreach ( $settings['custom_colors'] as $color ) {
                if ( isset( $color['_id'] ) && $color['_id'] === 'primary' && ! empty( $color['color'] ) ) {
                    return $color['color'];
                }
            }
        }

        return $default;
    }

    /**
     * Rendu HTML du bouton.
     */
    public function render_button(): void {
        ?>
        <div id="wycan-btt" class="wycan-btt" aria-label="<?php esc_attr_e( 'Retour en haut', 'wycan' ); ?>" role="button" tabindex="0">
            <svg class="wycan-btt-progress" viewBox="0 0 100 100">
                <circle class="wycan-btt-track" cx="50" cy="50" r="46"></circle>
                <circle class="wycan-btt-fill" cx="50" cy="50" r="46"></circle>
            </svg>
            <span class="wycan-btt-icon">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
                    <polyline points="17 11 12 6 7 11"></polyline>
                    <polyline points="17 18 12 13 7 18"></polyline>
                </svg>
            </span>
        </div>
        <?php
    }

    /**
     * Styles inline (aucun fichier externe requis).
     */
    public function inline_styles(): void {
        $color = $this->get_primary_color();
        ?>
        <style id="wycan-btt-css">
            .wycan-btt {
                --wycan-btt-color: <?php echo esc_attr( $color ); ?>;
                --wycan-btt-size: 48px;

                position: fixed;
                bottom: 30px;
                right: 30px;
                width: var(--wycan-btt-size);
                height: var(--wycan-btt-size);
                cursor: pointer;
                z-index: 9999;
                opacity: 0;
                visibility: hidden;
                transform: translateY(12px);
                transition: opacity .3s ease, visibility .3s ease, transform .3s ease;
            }

            .wycan-btt.is-visible {
                opacity: 1;
                visibility: visible;
                transform: translateY(0);
            }

            /* Cercle SVG de progression */
            .wycan-btt-progress {
                position: absolute;
                inset: 0;
                width: 100%;
                height: 100%;
                transform: rotate(-90deg);
            }

            .wycan-btt-track {
                fill: #ffffff;
                stroke: #e0e0e0;
                stroke-width: 4;
                filter: drop-shadow(0 2px 6px rgba(0, 0, 0, .08));
            }

            .wycan-btt-fill {
                fill: none;
                stroke: var(--wycan-btt-color);
                stroke-width: 4;
                stroke-dasharray: 289.027;
                stroke-dashoffset: 289.027;
                stroke-linecap: round;
                transition: stroke-dashoffset .1s linear;
            }

            /* Icône centrale */
            .wycan-btt-icon {
                position: absolute;
                inset: 0;
                display: flex;
                align-items: center;
                justify-content: center;
                color: var(--wycan-btt-color);
            }

            .wycan-btt-icon svg {
                width: 20px;
                height: 20px;
            }

            /* Hover */
            .wycan-btt:hover .wycan-btt-track {
                fill: var(--wycan-btt-color);
                transition: fill .25s ease;
            }

            .wycan-btt:hover .wycan-btt-icon {
                color: #ffffff;
                transition: color .25s ease;
            }

            .wycan-btt:hover .wycan-btt-fill {
                stroke: #ffffff;
            }

            /* Responsive */
            @media (max-width: 768px) {
                .wycan-btt {
                    --wycan-btt-size: 42px;
                    bottom: 20px;
                    right: 20px;
                }

                .wycan-btt-icon svg {
                    width: 17px;
                    height: 17px;
                }
            }
        </style>
        <?php
    }

    /**
     * JavaScript inline pour le scroll tracking et le smooth scroll.
     */
    public function inline_scripts(): void {
        ?>
        <script id="wycan-btt-js">
        (function () {
            var btn = document.getElementById('wycan-btt');
            if (!btn) return;

            var fill = btn.querySelector('.wycan-btt-fill');
            var circumference = 2 * Math.PI * 46; // r = 46
            var showThreshold = 300; // px avant d'afficher le bouton
            var ticking = false;

            function updateProgress() {
                var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
                var docHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight;
                var progress = docHeight > 0 ? Math.min(scrollTop / docHeight, 1) : 0;

                // Remplissage du cercle
                fill.style.strokeDashoffset = circumference * (1 - progress);

                // Afficher / masquer
                if (scrollTop > showThreshold) {
                    btn.classList.add('is-visible');
                } else {
                    btn.classList.remove('is-visible');
                }

                ticking = false;
            }

            window.addEventListener('scroll', function () {
                if (!ticking) {
                    window.requestAnimationFrame(updateProgress);
                    ticking = true;
                }
            }, { passive: true });

            // Scroll to top au clic
            btn.addEventListener('click', function () {
                window.scrollTo({ top: 0, behavior: 'smooth' });
            });

            // Accessibilité clavier
            btn.addEventListener('keydown', function (e) {
                if (e.key === 'Enter' || e.key === ' ') {
                    e.preventDefault();
                    window.scrollTo({ top: 0, behavior: 'smooth' });
                }
            });

            // État initial
            updateProgress();
        })();
        </script>
        <?php
    }
}

new Wycan_Back_To_Top();

				
			
Envie de laisser un commentaire ?

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Tu veux voir d'autres bouts de code ?

Bouton retour en haut pour Elementor

Bouton « Retour en haut » avec remplissage progressif en fonction du défilement. Utilise automatiquement la couleur principale d'Elementor.

Stock Status Widget pour Elementor

Widget Elementor affichant le statut de stock des produits WooCommerce. Conçu pour le Loop Builder (cards produit dans les archives/catégories).

Menu sticky intelligent (hide on scroll)

Ce snippet met en place un menu sticky fixé en haut de l’écran

Méthode d’expédition gratuite avec coupon WooCommerce

Offrir les frais de livraison… mais seulement pour la méthode d’expédition que tu choisis.

WooCommerce Product Buyers List

Ajoute un onglet dans WooCommerce pour afficher les acheteurs d'un produit spécifique avec filtre de date.

Emails additionnels par produits WooCommerce

Ajoute un onglet personnalisé dans les produits WooCommerce pour écrire un e-mail spécifique à chaque produit. Celui-ci sera envoyé dans un e-mail dédié.
ON DISCUTE ?

Une question sur WordPress ?
Un projet web à faire sous traiter par un freelance ?
Je suis votre homme !