Плагин Кнопка копирования для цитат
Плагин автоматически находит все блоки
на странице.
Для каждого блока создается кнопка «Копировать», которая размещается сразу после цитаты.
При нажатии на кнопку текст цитаты копируется в буфер обмена.
Пользователь получает визуальную обратную связь (изменение цвета кнопки, текста и иконки).
Плагин готов к использованию и не требует дополнительных настроек. Он будет работать на всех страницах вашего сайта, где используются цитаты.
Файл copy-quote-button.php
<?php
/**
* Plugin Name: Кнопка копирования для цитат
* Description: Добавляет эффектную адаптивную кнопку "Копировать" к цитатам, стилизованную под дизайн сайта 1promtai.ru.
* Version: 2.0
* Author: Ведерников Сергей ИИ Дэп и ИИ Алиса
*/
// Защита от прямого доступа
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class CopyQuoteButton {
private static $initialized = false;
public function __construct() {
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
add_action( 'wp_footer', array( $this, 'add_copy_button_to_quotes' ) );
}
public function enqueue_assets() {
if ( ! is_admin() ) {
wp_enqueue_style(
'copy-quote-button-style',
plugin_dir_url( __FILE__ ) . 'style.css',
array(),
'2.0'
);
wp_enqueue_script(
'copy-quote-button-script',
plugin_dir_url( __FILE__ ) . 'script.js',
array(),
'2.0',
true
);
}
}
public function add_copy_button_to_quotes() {
// Предотвращаем множественное добавление
if ( self::$initialized ) {
return;
}
self::$initialized = true;
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Функция для инициализации кнопок
function initCopyButtons() {
const quoteBlocks = document.querySelectorAll('blockquote, .wp-block-quote, .quote, .cited');
quoteBlocks.forEach(function(quote) {
// Проверяем, не добавлена ли уже кнопка
if (quote.dataset.copyButtonAdded === 'true') {
return;
}
// Проверяем, есть ли уже контейнер с кнопкой после цитаты
const nextSibling = quote.nextElementSibling;
if (nextSibling && nextSibling.classList && nextSibling.classList.contains('copy-quote-button-container')) {
return;
}
// Создаем контейнер для кнопки
const buttonContainer = document.createElement('div');
buttonContainer.className = 'copy-quote-button-container';
// Создаем кнопку с эффектным дизайном
const button = document.createElement('button');
button.className = 'copy-quote-button';
button.setAttribute('aria-label', 'Копировать цитату');
button.innerHTML = `
<span class="btn-content">
<span class="btn-icon">
<svg class="copy-icon" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
<svg class="check-icon hidden" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
</span>
<span class="btn-text">Копировать</span>
<span class="btn-shine"></span>
</span>
`;
buttonContainer.appendChild(button);
quote.parentNode.insertBefore(buttonContainer, quote.nextSibling);
quote.dataset.copyButtonAdded = 'true';
// Обработчик клика
button.addEventListener('click', function(e) {
e.preventDefault();
// Получаем текст цитаты
const textToCopy = quote.textContent.trim();
const currentButton = this;
// Эффект нажатия
currentButton.classList.add('pressed');
setTimeout(() => {
currentButton.classList.remove('pressed');
}, 300);
// Копирование
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(textToCopy).then(function() {
showSuccess(currentButton);
}).catch(function() {
showError(currentButton);
});
} else {
// Fallback
try {
const textArea = document.createElement('textarea');
textArea.value = textToCopy;
textArea.style.position = 'fixed';
textArea.style.left = '-9999px';
textArea.style.top = '-9999px';
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
showSuccess(currentButton);
} catch (err) {
showError(currentButton);
}
}
});
});
}
function showSuccess(button) {
button.classList.add('copied');
button.querySelector('.copy-icon').classList.add('hidden');
button.querySelector('.check-icon').classList.remove('hidden');
button.querySelector('.btn-text').textContent = 'Скопировано! ✓';
// Эффект конфетти
createConfetti(button);
setTimeout(function() {
button.classList.remove('copied');
button.querySelector('.copy-icon').classList.remove('hidden');
button.querySelector('.check-icon').classList.add('hidden');
button.querySelector('.btn-text').textContent = 'Копировать';
}, 2500);
}
function showError(button) {
button.querySelector('.btn-text').textContent = 'Ошибка!';
setTimeout(function() {
button.querySelector('.btn-text').textContent = 'Копировать';
}, 2000);
}
function createConfetti(button) {
const colors = ['#ff6b6b', '#ffd93d', '#6bcb77', '#4d96ff', '#ff6bcb', '#ffb347'];
const container = button.parentElement;
for (let i = 0; i < 20; i++) {
const confetti = document.createElement('div');
confetti.className = 'confetti-piece';
confetti.style.cssText = `
position: absolute;
width: ${Math.random() * 8 + 4}px;
height: ${Math.random() * 8 + 4}px;
background: ${colors[Math.floor(Math.random() * colors.length)]};
left: ${Math.random() * 100}%;
top: 50%;
border-radius: ${Math.random() > 0.5 ? '50%' : '2px'};
transform: rotate(${Math.random() * 360}deg);
pointer-events: none;
z-index: 1000;
animation: confettiFall ${Math.random() * 0.8 + 0.7}s ease-out forwards;
animation-delay: ${Math.random() * 0.1}s;
`;
container.appendChild(confetti);
setTimeout(() => {
confetti.remove();
}, 1500);
}
}
// Инициализация
initCopyButtons();
// MutationObserver для динамического контента
if (window.MutationObserver) {
let timeout;
const observer = new MutationObserver(function(mutations) {
clearTimeout(timeout);
timeout = setTimeout(() => {
initCopyButtons();
}, 300);
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
});
</script>
<?php
}
}
new CopyQuoteButton();
Файл 2: style.css
/* style.css - Эффектная кнопка копирования */
/* Контейнер кнопки */
.copy-quote-button-container {
position: relative;
display: flex;
justify-content: flex-end;
margin-top: 20px;
margin-bottom: 25px;
width: 100%;
}
/* Основная кнопка */
.copy-quote-button {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 12px 28px;
background: linear-gradient(135deg, #007cba 0%, #005a8c 50%, #003d66 100%);
color: #ffffff;
border: none;
border-radius: 50px;
font-size: 15px;
font-weight: 600;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
box-shadow:
0 4px 15px rgba(0, 124, 186, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.2);
line-height: 1.2;
letter-spacing: 0.5px;
overflow: hidden;
min-height: 50px;
min-width: 140px;
transform-origin: center;
}
/* Бликовый эффект */
.copy-quote-button .btn-shine {
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.15) 0%, transparent 60%);
pointer-events: none;
transition: all 0.6s ease;
}
.copy-quote-button:hover .btn-shine {
transform: translate(10%, 10%);
background: radial-gradient(circle at 70% 70%, rgba(255, 255, 255, 0.2) 0%, transparent 60%);
}
/* Содержимое кнопки */
.copy-quote-button .btn-content {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
position: relative;
z-index: 1;
}
.copy-quote-button .btn-icon {
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.copy-quote-button .btn-text {
transition: all 0.3s ease;
white-space: nowrap;
}
/* Иконки */
.copy-quote-button .copy-icon,
.copy-quote-button .check-icon {
flex-shrink: 0;
stroke: currentColor;
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.copy-quote-button .hidden {
display: none !important;
}
/* Эффекты при наведении */
.copy-quote-button:hover {
transform: translateY(-3px) scale(1.02);
background: linear-gradient(135deg, #0088d4 0%, #006699 50%, #004a7a 100%);
box-shadow:
0 8px 30px rgba(0, 124, 186, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.25);
}
.copy-quote-button:hover .copy-icon {
transform: rotate(-5deg) scale(1.1);
}
/* Эффект нажатия */
.copy-quote-button.pressed {
transform: scale(0.95) !important;
transition: transform 0.1s ease !important;
}
/* Состояние "Скопировано" */
.copy-quote-button.copied {
background: linear-gradient(135deg, #28a745 0%, #1e7e34 50%, #146c28 100%);
box-shadow:
0 4px 20px rgba(40, 167, 69, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.25);
animation: successPulse 0.6s ease;
}
.copy-quote-button.copied:hover {
background: linear-gradient(135deg, #34ce57 0%, #28a745 50%, #1e7e34 100%);
box-shadow:
0 8px 35px rgba(40, 167, 69, 0.5),
inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
.copy-quote-button.copied .check-icon {
animation: checkPop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* Анимации */
@keyframes checkPop {
0% {
transform: scale(0) rotate(-30deg);
opacity: 0;
}
60% {
transform: scale(1.3) rotate(5deg);
opacity: 1;
}
100% {
transform: scale(1) rotate(0deg);
opacity: 1;
}
}
@keyframes successPulse {
0% {
transform: scale(1);
}
30% {
transform: scale(1.05);
}
60% {
transform: scale(0.98);
}
100% {
transform: scale(1);
}
}
@keyframes confettiFall {
0% {
opacity: 1;
transform: translateY(0) rotate(0deg) scale(1);
}
100% {
opacity: 0;
transform: translateY(80px) rotate(720deg) scale(0);
}
}
/* Поддержка светлой/темной темы */
@media (prefers-color-scheme: dark) {
.copy-quote-button {
background: linear-gradient(135deg, #0a7fc9 0%, #0866a3 50%, #064a7a 100%);
box-shadow:
0 4px 20px rgba(10, 127, 201, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.15);
}
.copy-quote-button:hover {
background: linear-gradient(135deg, #1a8fd9 0%, #0a7fc9 50%, #0866a3 100%);
box-shadow:
0 8px 35px rgba(10, 127, 201, 0.5),
inset 0 1px 0 rgba(255, 255, 255, 0.2);
}
}
/* Адаптивность для мобильных устройств */
@media (max-width: 768px) {
.copy-quote-button {
padding: 10px 20px;
font-size: 14px;
width: 100%;
justify-content: center;
min-height: 44px;
min-width: unset;
border-radius: 40px;
}
.copy-quote-button .btn-content {
gap: 8px;
}
.copy-quote-button-container {
margin-top: 15px;
margin-bottom: 20px;
padding: 0 5px;
}
}
@media (max-width: 480px) {
.copy-quote-button {
padding: 12px 16px;
font-size: 13px;
min-height: 40px;
border-radius: 35px;
}
.copy-quote-button .copy-icon,
.copy-quote-button .check-icon {
width: 16px;
height: 16px;
}
}
/* Для людей с vestibular disorders */
@media (prefers-reduced-motion: reduce) {
.copy-quote-button,
.copy-quote-button * {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
.copy-quote-button .btn-shine {
display: none;
}
}
/* Фокус для доступности */
.copy-quote-button:focus-visible {
outline: 3px solid #007cba;
outline-offset: 3px;
}
/* Дополнительный эффект - градиентная граница */
.copy-quote-button::before {
content: '';
position: absolute;
inset: -2px;
background: linear-gradient(135deg, #007cba, #00d4ff, #007cba);
border-radius: 52px;
z-index: -1;
opacity: 0;
transition: opacity 0.4s ease;
}
.copy-quote-button:hover::before {
opacity: 1;
animation: borderRotate 3s linear infinite;
}
@keyframes borderRotate {
0% {
background: linear-gradient(135deg, #007cba, #00d4ff, #007cba);
}
50% {
background: linear-gradient(225deg, #007cba, #00d4ff, #007cba);
}
100% {
background: linear-gradient(135deg, #007cba, #00d4ff, #007cba);
}
}
Файл 3: script.js
// script.js - Дополнительная логика для кнопки
(function() {
'use strict';
// Все основные функции уже встроены в PHP файл
// Этот файл оставлен для будущих расширений
console.log('Кнопка копирования цитат активирована!');
})();
Особенности обновленной версии
Улучшенная стилизация:
Что было исправлено и улучшено:
1. Исправление двойной кнопки:
Добавлен флаг data-copy-button-added для каждой цитаты
Проверка существующего контейнера перед добавлением
self::$initialized для предотвращения множественной инициализации
2. Эффектный дизайн:
Градиентный фон с плавным переходом
Бликовый эффект при наведении (сдвигающийся свет)
Анимированная граница с вращающимся градиентом
3D-эффект при наведении (подъем + масштаб)
Эффект нажатия (сжатие кнопки)
Пульсация при успешном копировании
3. Эффект "Конфетти":
При успешном копировании из кнопки вылетают разноцветные частицы
Анимация падения с вращением
Случайные цвета и размеры
4. Улучшенная анимация:
Иконка галочки появляется с эффектом "поп"
Плавные переходы всех элементов
Кастомная кривая Безье для более живых анимаций
5. Дополнительные улучшения:
Поддержка темной темы
Улучшенная адаптивность на мобильных
ARIA-атрибуты для доступности
Отключение анимаций для пользователей с vestibular disorders
Теперь кнопка будет выглядеть премиально, без дублирования, с красивыми эффектами!
Скачать плагин в архиве, мжно установить на сайте через установку плагинов: Кнопка копирования цитат





