Unify TOC ownership, real video episode list, slider overlay, single-query related list, Zibll user-card after-box, and new-route subtitle/meta contracts.
234 lines
9.1 KiB
PHP
234 lines
9.1 KiB
PHP
<?php
|
|
/**
|
|
* AKSRT Slider Helper — 轮播渲染函数
|
|
*
|
|
* 对标 zibll 的 zib_new_slider(),提供服务端轮播 HTML 生成。
|
|
* 支持 data-* 属性驱动配置、视差、懒加载、缩略图分页。
|
|
*
|
|
* @package AKSRT
|
|
*/
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
/**
|
|
* 渲染 Swiper 轮播
|
|
*
|
|
* @param array $args {
|
|
* @type array $slides 幻灯片数据数组
|
|
* @type string $effect 动画效果: slide, fade, cube, coverflow, flip (默认 slide)
|
|
* @type bool $loop 是否循环 (默认 true)
|
|
* @type bool $autoplay 是否自动播放 (默认 true)
|
|
* @type int $interval 自动播放间隔 ms (默认 5000)
|
|
* @type int $speed 动画速度 ms (0=自动计算)
|
|
* @type string $direction 方向: horizontal, vertical (默认 horizontal)
|
|
* @type int $spaceBetween 幻灯片间距 px (默认 0)
|
|
* @type string $pagination_type 分页器类型: bullets, fraction, progressbar, thumbnail (默认 bullets)
|
|
* @type string $pc_height PC 端高度 CSS 值 (默认 auto)
|
|
* @type string $m_height 移动端高度 CSS 值 (默认 auto)
|
|
* @type bool $scale_height 是否使用比例高度 (默认 false)
|
|
* @type float $scale_ratio 比例值 (默认 0.45 = 45%)
|
|
* @type string $wrapper_class 额外 wrapper 类
|
|
* @type string $slider_class 额外 slider 类
|
|
* @type bool $show_nav 是否显示导航箭头 (默认 true)
|
|
* @type bool $show_pagination 是否显示分页器 (默认 true)
|
|
* }
|
|
*
|
|
* slide 数据格式:
|
|
* [
|
|
* 'image' => 'https://...',
|
|
* 'link' => 'https://...',
|
|
* 'title' => '标题',
|
|
* 'desc' => '描述',
|
|
* 'alt' => '图片 alt',
|
|
* 'bg_color' => '#f0f0f0', // 背景色
|
|
* 'target' => '_blank',
|
|
* ]
|
|
*
|
|
* @return string HTML
|
|
*/
|
|
function aksrt_render_slider(array $args = []): string {
|
|
$defaults = [
|
|
'slides' => [],
|
|
'effect' => 'slide',
|
|
'loop' => true,
|
|
'autoplay' => true,
|
|
'interval' => 5000,
|
|
'speed' => 0,
|
|
'direction' => 'horizontal',
|
|
'spaceBetween' => 0,
|
|
'pagination_type' => 'bullets',
|
|
'pc_height' => 'auto',
|
|
'm_height' => 'auto',
|
|
'scale_height' => false,
|
|
'scale_ratio' => 0.45,
|
|
'wrapper_class' => '',
|
|
'slider_class' => '',
|
|
'show_nav' => true,
|
|
'show_pagination' => true,
|
|
// Optional absolute overlay HTML (e.g. single cover title/meta). Not escaped.
|
|
'overlay_html' => '',
|
|
];
|
|
|
|
$args = wp_parse_args($args, $defaults);
|
|
$slides = $args['slides'];
|
|
|
|
if (empty($slides) || !is_array($slides)) {
|
|
return '';
|
|
}
|
|
|
|
// 构建 data 属性
|
|
$data_attrs = [];
|
|
$data_attrs[] = 'data-effect="' . esc_attr($args['effect']) . '"';
|
|
if ($args['loop']) $data_attrs[] = 'data-loop';
|
|
if ($args['autoplay']) {
|
|
$data_attrs[] = 'data-autoplay';
|
|
$data_attrs[] = 'data-interval="' . esc_attr((string) $args['interval']) . '"';
|
|
}
|
|
if ($args['speed'] > 0) $data_attrs[] = 'data-speed="' . esc_attr((string) $args['speed']) . '"';
|
|
if ($args['direction'] !== 'horizontal') $data_attrs[] = 'data-direction="' . esc_attr($args['direction']) . '"';
|
|
if ($args['spaceBetween'] > 0) $data_attrs[] = 'data-spaceBetween="' . esc_attr((string) $args['spaceBetween']) . '"';
|
|
if ($args['pagination_type'] !== 'bullets') $data_attrs[] = 'data-pagination-type="' . esc_attr($args['pagination_type']) . '"';
|
|
|
|
// 高度样式
|
|
$height_style = '';
|
|
if ($args['scale_height']) {
|
|
// padding-bottom percentage for ratio box (Zibll graphic cover style).
|
|
$ratio_pct = max(10, min(100, (float) $args['scale_ratio'] * 100));
|
|
$height_style = sprintf(
|
|
'--pc-height:auto;--m-height:auto;--scale-ratio:%s;padding-bottom:%s%%;',
|
|
esc_attr((string) $args['scale_ratio']),
|
|
esc_attr((string) $ratio_pct)
|
|
);
|
|
} else {
|
|
$height_style = sprintf('--pc-height:%s;--m-height:%s;', esc_attr($args['pc_height']), esc_attr($args['m_height']));
|
|
}
|
|
|
|
// CSS 类
|
|
$slider_classes = ['new-swiper', 'slide-index'];
|
|
if ($args['slider_class']) $slider_classes[] = $args['slider_class'];
|
|
$slider_class_str = implode(' ', $slider_classes);
|
|
|
|
$wrapper_classes = ['aksrt-slider-wrapper'];
|
|
if ($args['wrapper_class']) $wrapper_classes[] = $args['wrapper_class'];
|
|
if ($args['scale_height']) $wrapper_classes[] = 'aksrt-slider-wrapper--scale';
|
|
if ((string) $args['overlay_html'] !== '') $wrapper_classes[] = 'aksrt-slider-wrapper--has-overlay';
|
|
$wrapper_class_str = implode(' ', $wrapper_classes);
|
|
|
|
// 渲染 HTML
|
|
$html = '<div class="' . esc_attr($wrapper_class_str) . '"' . ($args['scale_height'] ? ' style="position:relative;' . esc_attr(sprintf('padding-bottom:%s%%;', (string) max(10, min(100, (float) $args['scale_ratio'] * 100)))) . '"' : '') . '>';
|
|
$html .= '<div class="' . esc_attr($slider_class_str) . '" ' . implode(' ', $data_attrs) . ' style="' . esc_attr($height_style) . ($args['scale_height'] ? 'position:absolute;inset:0;width:100%;height:100%;' : '') . '">';
|
|
$html .= '<div class="swiper-wrapper">';
|
|
|
|
foreach ($slides as $slide) {
|
|
$image = esc_url($slide['image'] ?? '');
|
|
$link = esc_url($slide['link'] ?? '');
|
|
$title = sanitize_text_field($slide['title'] ?? '');
|
|
$desc = sanitize_text_field($slide['desc'] ?? '');
|
|
$alt = sanitize_text_field($slide['alt'] ?? $title);
|
|
$bg_color = sanitize_hex_color($slide['bg_color'] ?? '');
|
|
$target = ($slide['target'] ?? '') === '_blank' ? '_blank' : '_self';
|
|
|
|
$slide_style = $bg_color ? 'background-color:' . esc_attr($bg_color) . ';' : '';
|
|
|
|
$html .= '<div class="swiper-slide"' . ($slide_style ? ' style="' . esc_attr($slide_style) . '"' : '') . '>';
|
|
|
|
if ($link) {
|
|
$html .= '<a href="' . esc_url($link) . '" target="' . esc_attr($target) . '">';
|
|
}
|
|
|
|
if ($image) {
|
|
$html .= '<img class="swiper-lazy fit-cover" data-src="' . esc_url($image) . '" src="' . esc_url($image) . '" alt="' . esc_attr($alt) . '" loading="lazy">';
|
|
$html .= '<div class="swiper-lazy-preloader"></div>';
|
|
}
|
|
|
|
if ($link) {
|
|
$html .= '</a>';
|
|
}
|
|
|
|
// 文字覆盖层
|
|
if ($title || $desc) {
|
|
$html .= '<div class="aksrt-slide-text">';
|
|
if ($title) $html .= '<div class="aksrt-slide-text__title">' . esc_html($title) . '</div>';
|
|
if ($desc) $html .= '<div class="aksrt-slide-text__desc">' . esc_html($desc) . '</div>';
|
|
$html .= '</div>';
|
|
}
|
|
|
|
$html .= '</div>';
|
|
}
|
|
|
|
$html .= '</div>'; // .swiper-wrapper
|
|
|
|
if ($args['show_nav']) {
|
|
$html .= '<div class="swiper-button-prev"></div>';
|
|
$html .= '<div class="swiper-button-next"></div>';
|
|
}
|
|
|
|
if ($args['show_pagination']) {
|
|
$html .= '<div class="swiper-pagination"></div>';
|
|
}
|
|
|
|
$html .= '</div>'; // .new-swiper
|
|
|
|
// Cover title/meta overlay (trusted HTML from single cover renderer).
|
|
if ((string) $args['overlay_html'] !== '') {
|
|
$html .= '<div class="aksrt-slider-overlay">' . $args['overlay_html'] . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
|
}
|
|
|
|
$html .= '</div>'; // .aksrt-slider-wrapper
|
|
|
|
return $html;
|
|
}
|
|
|
|
/**
|
|
* 渲染水平滚动器(Tab 导航、横向内容列表)
|
|
*
|
|
* @param array $args {
|
|
* @type array $items 内容数组
|
|
* @type string $wrapper_class 额外 wrapper 类
|
|
* @type bool $autoplay 是否自动播放
|
|
* @type int $interval 自动播放间隔
|
|
* @type string $render_item 自定义渲染回调 (callable)
|
|
* }
|
|
* @return string HTML
|
|
*/
|
|
function aksrt_render_scroll_slider(array $args = []): string {
|
|
$defaults = [
|
|
'items' => [],
|
|
'wrapper_class' => '',
|
|
'autoplay' => false,
|
|
'interval' => 4000,
|
|
'render_item' => null,
|
|
];
|
|
|
|
$args = wp_parse_args($args, $defaults);
|
|
$items = $args['items'];
|
|
|
|
if (empty($items)) return '';
|
|
|
|
$data_attrs = ['class="swiper-scroll' . ($args['wrapper_class'] ? ' ' . esc_attr($args['wrapper_class']) : '') . '"'];
|
|
if ($args['autoplay']) {
|
|
$data_attrs[] = 'data-autoplay';
|
|
$data_attrs[] = 'data-interval="' . esc_attr((string) $args['interval']) . '"';
|
|
}
|
|
|
|
$html = '<div ' . implode(' ', $data_attrs) . '>';
|
|
$html .= '<div class="swiper-wrapper">';
|
|
|
|
foreach ($items as $i => $item) {
|
|
$html .= '<div class="swiper-slide">';
|
|
if (is_callable($args['render_item'])) {
|
|
$html .= call_user_func($args['render_item'], $item, $i);
|
|
} else {
|
|
$html .= '<div class="aksrt-scroll-item">' . esc_html($item['label'] ?? '') . '</div>';
|
|
}
|
|
$html .= '</div>';
|
|
}
|
|
|
|
$html .= '</div>';
|
|
$html .= '<div class="swiper-button-prev"></div>';
|
|
$html .= '<div class="swiper-button-next"></div>';
|
|
$html .= '</div>';
|
|
|
|
return $html;
|
|
}
|