fix(widgets): align slider media combo with the Zibll reference
- 渲染器:幻灯片按参考补回 span/a 包裹、lazyload swiper-lazy 懒加载契约、 视频背景与图层视差属性条件;组合模块补回 is_show 门控、超管缺省提示、 widget-graphic-cover mb 类与内层样式容器、卡片列入场动画类与 lazy_cover 开关 - Schema:幻灯片/图层/卡片字段补齐 library、unit、参考描述文案与分组按钮标题 - 后台表单:分组按钮文案(添加幻灯片/添加卡片/添加图层)、行编号格式、 视频上传不渲染图片预览(FormRenderer + admin.js) - 契约测试:新增 slider media combo 前后台参考契约断言 验证:php -l 全部通过;run.php 133 断言;node --test 243 项(仅主干既有 toast-contract 失败);validate-widget-golden 通过
This commit is contained in:
@@ -193,7 +193,8 @@ final class FormRenderer {
|
||||
private static function upload(string $id, string $name, string $value, string $library = ''): void {
|
||||
$library = in_array($library, ['image', 'video'], true) ? $library : '';
|
||||
echo '<div class="aksrt-widget-upload" data-media-library="' . esc_attr($library) . '">';
|
||||
if ($value !== '') {
|
||||
// 参考主题:视频上传字段不展示预览图(视频 URL 作为 img src 会得到破图)。
|
||||
if ($value !== '' && $library !== 'video') {
|
||||
echo '<img src="' . esc_url($value) . '" alt="">';
|
||||
}
|
||||
echo '<input class="widefat" type="url" id="' . esc_attr($id) . '" name="' . esc_attr($name) . '" value="' . esc_attr($value) . '">';
|
||||
@@ -240,19 +241,20 @@ final class FormRenderer {
|
||||
|
||||
private static function group(SchemaWidget $widget, array $field, array $rows, string $key): void {
|
||||
$token = '__index_' . substr(md5($key), 0, 8) . '__';
|
||||
$button_title = (string) ($field['button_title'] ?? __('添加项目', 'aksrt'));
|
||||
echo '<div class="aksrt-widget-repeater" data-index-token="' . esc_attr($token) . '" data-max-items="' . esc_attr((string) ($field['max_items'] ?? 50)) . '"><div class="aksrt-widget-repeater__rows">';
|
||||
foreach ($rows as $index => $row) {
|
||||
self::group_row($widget, $field, is_array($row) ? $row : [], $key, (string) $index);
|
||||
}
|
||||
echo '</div><template class="aksrt-widget-repeater__template">';
|
||||
self::group_row($widget, $field, [], $key, $token);
|
||||
echo '</template><button type="button" class="button aksrt-widget-repeater__add">' . esc_html__('添加项目', 'aksrt') . '</button></div>';
|
||||
echo '</template><button type="button" class="button aksrt-widget-repeater__add">' . esc_html($button_title) . '</button></div>';
|
||||
}
|
||||
|
||||
private static function group_row(SchemaWidget $widget, array $field, array $row, string $key, string $index): void {
|
||||
$prefix = (string) ($field['item_label'] ?? $field['label'] ?? __('项目', 'aksrt'));
|
||||
$number = ctype_digit($index) ? ((int) $index + 1) : 0;
|
||||
$title = $prefix . ($number ? ' ' . $number : '');
|
||||
$title = $prefix . $number;
|
||||
if (!empty($row['title'])) $title .= ':' . strip_tags((string) $row['title']);
|
||||
echo '<div class="aksrt-widget-repeater__row" data-row-index="' . esc_attr(ctype_digit($index) ? $index : '0') . '"><div class="aksrt-widget-repeater__bar"><span class="dashicons dashicons-move" aria-hidden="true"></span><strong>' . esc_html($title) . '</strong><button type="button" class="button-link-delete aksrt-widget-repeater__remove">' . esc_html__('删除', 'aksrt') . '</button></div><div class="aksrt-widget-repeater__fields">';
|
||||
foreach ((array) ($field['fields'] ?? []) as $child) {
|
||||
|
||||
@@ -10,101 +10,179 @@ defined('ABSPATH') || exit;
|
||||
final class SliderRenderer {
|
||||
public static function render(string $slug, array $instance, array $context = []): string {
|
||||
unset($context);
|
||||
return $slug === 'slider_media_combo' ? self::combo($instance) : self::slider((array) ($instance['slides'] ?? []), (array) ($instance['option'] ?? []), true);
|
||||
if ($slug === 'slider_media_combo') {
|
||||
return self::combo($instance);
|
||||
}
|
||||
$slides = (array) ($instance['slides'] ?? []);
|
||||
if (!$slides) {
|
||||
// 参考主题幻灯片(新):无可显示内容时仅向超级管理员提示。
|
||||
if (function_exists('is_super_admin') && is_super_admin()) {
|
||||
return '<div class="c-red muted-box mb20"><b>[' . esc_html__('幻灯片(新)', 'aksrt') . ']' . esc_html__('幻灯片模块:', 'aksrt') . '</b>' . esc_html__('当前配置下没有可显示的内容,请正确配置或移出该模块', 'aksrt') . '</div>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
return self::slider($slides, (array) ($instance['option'] ?? []), true);
|
||||
}
|
||||
|
||||
private static function slider(array $slides, array $option, bool $with_margin = false): string {
|
||||
$option = array_merge(['direction' => 'horizontal', 'loop' => true, 'button' => true, 'pagination' => true, 'effect' => 'slide', 'scale_height' => true, 'scale' => 35, 'auto_height' => false, 'max_height' => 500, 'min_height' => 180, 'pc_height' => 400, 'm_height' => 200, 'spacebetween' => 15, 'speed' => 0, 'autoplay' => true, 'interval' => 4], $option);
|
||||
$is_mobile = function_exists('wp_is_mobile') && wp_is_mobile();
|
||||
$lazy = aksrt_is_lazy('lazy_sider');
|
||||
$lazy_src = aksrt_get_lazy_thumb('lg');
|
||||
$items = ''; $count = 0;
|
||||
foreach ($slides as $slide) {
|
||||
if (!is_array($slide)) continue;
|
||||
$hide = (string) ($slide['hide'] ?? '');
|
||||
if (($is_mobile && $hide === 'm') || (!$is_mobile && $hide === 'pc')) continue;
|
||||
$background = esc_url((string) ($slide['background'] ?? '')); $video = esc_url((string) ($slide['background_video'] ?? ''));
|
||||
if ($background === '' && ($video === '' || $is_mobile)) continue;
|
||||
$alt = (string) (($slide['text']['title'] ?? '') ?: __('幻灯片', 'aksrt'));
|
||||
$slide_class = '';
|
||||
if ($video !== '' && !$is_mobile) { $slide_class = ' slide-background-video'; $media = '<video autoplay loop muted playsinline class="fit-cover radius8" src="' . $video . '"' . ($background !== '' ? ' poster="' . $background . '"' : '') . '></video><div class="absolute"></div>'; }
|
||||
else $media = '<img class="radius8 swiper-lazy" src="' . esc_url(defined('AKSRT_URI') ? AKSRT_URI . '/assets/img/thumbnail-lg.svg' : $background) . '" data-src="' . $background . '" alt="' . esc_attr($alt) . '">';
|
||||
$slide_class = (string) ($slide['class'] ?? '');
|
||||
$background = (string) ($slide['background'] ?? '');
|
||||
$video = (string) ($slide['background_video'] ?? '');
|
||||
$media = '';
|
||||
if ($video !== '' && !$is_mobile) {
|
||||
$slide_class .= ' slide-background-video';
|
||||
$poster = $background !== '' ? ' poster="' . esc_url($background) . '"' : '';
|
||||
$media = '<video autoplay="" loop="" muted="" class="fit-cover radius8' . ($lazy ? ' lazyload' : '') . '"' . ($lazy ? ' data-src="' . esc_url($video) . '" src=""' : ' src="' . esc_url($video) . '"') . $poster . '></video><div class="absolute"></div>';
|
||||
}
|
||||
if ($media === '' && $background !== '') {
|
||||
$alt = (string) (($slide['text']['title'] ?? '') ?: ($slide['link']['text'] ?? '') ?: __('幻灯片', 'aksrt'));
|
||||
$media = '<img class="radius8' . ($lazy ? ' lazyload swiper-lazy' : '') . '" ' . ($lazy ? 'data-src="' . esc_url($background) . '" src="' . esc_url($lazy_src) . '"' : 'src="' . esc_url($background) . '"') . ' alt="' . esc_attr($alt) . '">';
|
||||
}
|
||||
if ($media === '') continue;
|
||||
$layers = '';
|
||||
foreach ((array) ($slide['image_layer'] ?? []) as $layer) {
|
||||
if (!is_array($layer) || empty($layer['image'])) continue;
|
||||
$attrs = ''; $parallax = (int) ($layer['parallax'] ?? 0); $scale = (float) ($layer['parallax_scale'] ?? 100) / 100; $opacity = (float) ($layer['parallax_opacity'] ?? 100) / 100;
|
||||
if ($parallax) $attrs .= ' data-swiper-parallax="' . $parallax . '%"'; if ($scale !== 1.0) $attrs .= ' data-swiper-parallax-scale="' . esc_attr((string) $scale) . '"'; if ($opacity !== 1.0) $attrs .= ' data-swiper-parallax-opacity="' . esc_attr((string) $opacity) . '"';
|
||||
$parallax = (int) ($layer['parallax'] ?? 0);
|
||||
$parallax_attr = $parallax ? ' data-swiper-parallax="' . $parallax . '%"' : '';
|
||||
$opacity = (float) ($layer['parallax_opacity'] ?? 100) / 100;
|
||||
$parallax_attr .= ($parallax && $opacity && $opacity !== 1.0) ? ' data-swiper-parallax-opacity="' . esc_attr((string) $opacity) . '"' : '';
|
||||
$scale = (float) ($layer['parallax_scale'] ?? 100) / 100;
|
||||
$parallax_attr .= ($scale && $scale !== 1.0) ? ' data-swiper-parallax-scale="' . esc_attr((string) $scale) . '"' : '';
|
||||
$class = !empty($layer['free_size']) ? ' slide-layer text-' . esc_attr((string) ($layer['align'] ?? 'center')) : '';
|
||||
$layers .= '<div class="absolute' . $class . '"' . $attrs . '><img class="radius8 swiper-lazy" src="' . esc_url(defined('AKSRT_URI') ? AKSRT_URI . '/assets/img/thumbnail-lg.svg' : (string) $layer['image']) . '" data-src="' . esc_url((string) $layer['image']) . '" alt="' . esc_attr($alt) . '"></div>';
|
||||
$layers .= '<div class="absolute' . $class . '"' . $parallax_attr . '><img class="radius8' . ($lazy ? ' lazyload swiper-lazy' : '') . '" ' . ($lazy ? 'data-src="' . esc_url((string) $layer['image']) . '" src="' . esc_url($lazy_src) . '"' : 'src="' . esc_url((string) $layer['image']) . '"') . ' alt="' . esc_attr((string) (($slide['text']['title'] ?? '') ?: __('幻灯片', 'aksrt'))) . '"></div>';
|
||||
}
|
||||
$text = (array) ($slide['text'] ?? []); $copy = '';
|
||||
if (!empty($text['title'])) { $style = ''; if (!empty($text['text_size_pc'])) $style .= '--text-size-pc:' . (int) $text['text_size_pc'] . 'px;'; if (!empty($text['text_size_m'])) $style .= '--text-size-m:' . (int) $text['text_size_m'] . 'px;'; $copy = '<div class="abs-center slide-text ' . esc_attr((string) ($text['text_align'] ?? 'left-bottom')) . '"' . ($style ? ' style="' . esc_attr($style) . '"' : '') . '><div class="slide-title">' . wp_kses_post((string) $text['title']) . '</div>' . (!empty($text['desc']) ? '<div class="slide-desc">' . wp_kses_post((string) $text['desc']) . '</div>' : '') . '</div>'; if (!empty($text['parallax'])) $copy = '<div class="absolute" data-swiper-parallax="' . (int) $text['parallax'] . '">' . $copy . '</div>'; }
|
||||
$body = $media . $layers . $copy; $link = RendererHelpers::link((array) ($slide['link'] ?? [])); if ($link['url'] !== '') $body = '<a href="' . $link['url'] . '"' . RendererHelpers::link_attrs($link) . '>' . $body . '</a>';
|
||||
$items .= '<div class="swiper-slide' . esc_attr($slide_class) . '">' . $body . '</div>'; $count++;
|
||||
$body = $media . $layers . $copy;
|
||||
$link = RendererHelpers::link((array) ($slide['link'] ?? []));
|
||||
$body = $link['url'] !== ''
|
||||
? '<a' . ($link['target'] !== '' ? ' target="_blank"' : '') . ' href="' . $link['url'] . '">' . $body . '</a>'
|
||||
: '<span>' . $body . '</span>';
|
||||
$items .= '<div class="swiper-slide' . ($slide_class !== '' ? ' ' . esc_attr(trim($slide_class)) : '') . '">' . $body . '</div>'; $count++;
|
||||
}
|
||||
if (!$count) return RendererHelpers::empty_state(__('请配置幻灯片', 'aksrt'));
|
||||
$class = 'new-swiper slide-widget' . ($with_margin ? ' mb20' : '') . ($option['scale_height'] && $option['direction'] !== 'vertical' ? ' scale-height' : '') . ($count === 1 ? ' swiper-container-initialized' : ''); $attrs = ' data-direction="' . esc_attr((string) $option['direction']) . '" data-effect="' . esc_attr((string) $option['effect']) . '"';
|
||||
if (!empty($option['loop']) && $count > 1) $attrs .= ' data-loop="true"'; if (!empty($option['autoplay']) && $count > 1) $attrs .= ' data-autoplay="1" data-interval="' . ((int) $option['interval'] * 1000) . '"'; if ((int) $option['speed'] > 0) $attrs .= ' data-speed="' . (int) $option['speed'] . '"'; $attrs .= ' data-spaceBetween="' . (int) $option['spacebetween'] . '"';
|
||||
if ($option['auto_height']) $attrs .= ' auto-height="true" style="--max-height:' . (int) $option['max_height'] . 'px;--min-height:' . (int) $option['min_height'] . 'px;"'; else $attrs .= ' style="' . esc_attr($option['scale_height'] && $option['direction'] !== 'vertical' ? '--scale-height:' . (int) $option['scale'] . '%;' : '--pc-height:' . (int) $option['pc_height'] . 'px;--m-height:' . (int) $option['m_height'] . 'px;') . '"';
|
||||
$html = '<div class="' . esc_attr($class) . '"' . $attrs . '><div class="swiper-wrapper">' . $items . '</div>'; if (!empty($option['button']) && $count > 1) $html .= '<div class="swiper-button-prev"></div><div class="swiper-button-next"></div>'; if (!empty($option['pagination']) && $count > 1) $html .= '<div class="swiper-pagination kaoyou"></div>'; return '<div class="relative zib-slider">' . $html . '</div>';
|
||||
if (!$count) return '';
|
||||
$class = 'new-swiper slide-widget' . ($with_margin ? ' mb20' : '') . ($option['scale_height'] && $option['direction'] !== 'vertical' ? ' scale-height' : '') . ($count === 1 ? ' swiper-container-initialized' : '');
|
||||
$attrs = ' data-direction="' . esc_attr((string) $option['direction']) . '" data-effect="' . esc_attr((string) $option['effect']) . '"';
|
||||
if (!empty($option['loop'])) $attrs .= ' data-loop="true"';
|
||||
$interval = (int) $option['interval'];
|
||||
if ($interval < 999) $interval *= 1000;
|
||||
if (!empty($option['autoplay'])) $attrs .= ' data-autoplay="1" data-interval="' . $interval . '"';
|
||||
if ((int) $option['speed'] > 299) $attrs .= ' data-speed="' . (int) $option['speed'] . '"';
|
||||
$attrs .= ' data-spaceBetween="' . (int) $option['spacebetween'] . '"';
|
||||
if ($option['auto_height'] && $option['direction'] !== 'vertical') {
|
||||
$attrs .= ' auto-height="true" style="--max-height:' . (int) $option['max_height'] . 'px;--min-height:' . (int) $option['min_height'] . 'px;"';
|
||||
} elseif ($option['scale_height'] && $option['direction'] !== 'vertical') {
|
||||
$attrs .= ' style="--scale-height:' . (int) $option['scale'] . '%;"';
|
||||
} else {
|
||||
$attrs .= ' style="--pc-height:' . (int) $option['pc_height'] . 'px;--m-height:' . (int) $option['m_height'] . 'px;"';
|
||||
}
|
||||
$html = '<div class="' . esc_attr($class) . '"' . $attrs . '><div class="swiper-wrapper">' . $items . '</div>';
|
||||
if (!empty($option['button']) && $count > 1) $html .= '<div class="swiper-button-prev"></div><div class="swiper-button-next"></div>';
|
||||
if (!empty($option['pagination']) && $count > 1) $html .= '<div class="swiper-pagination kaoyou"></div>';
|
||||
return '<div class="relative zib-slider">' . $html . '</div>';
|
||||
}
|
||||
|
||||
private static function combo(array $instance): string {
|
||||
$option = (array) ($instance['option'] ?? []); $option['direction'] = 'horizontal'; $option['scale_height'] = true; $option['scale'] = (int) ($instance['slide_scale'] ?? 50);
|
||||
$slide_list = (array) ($instance['slides'] ?? []);
|
||||
$slider = !empty($slide_list[0]) ? self::slider($slide_list, $option) : '';
|
||||
$has_slider = !empty($instance['slides'][0]);
|
||||
$has_covers = !empty($instance['covers'][0]['image']) || !empty($instance['covers'][0]['video']);
|
||||
// 参考主题 is_show 门控:幻灯片需含背景媒体才视为有内容。
|
||||
$show_slider = $has_slider && (!empty($instance['slides'][0]['background']) || !empty($instance['slides'][0]['background_video']));
|
||||
if (!$show_slider && !$has_covers) return '';
|
||||
|
||||
$slide_scale = max(0, (int) ($instance['slide_scale'] ?? 50));
|
||||
$slider = '';
|
||||
if ($has_slider && is_array($instance['option'])) {
|
||||
$option = $instance['option'];
|
||||
$option['class'] = 'slide-widget';
|
||||
$option['direction'] = 'horizontal';
|
||||
$option['scale_height'] = true;
|
||||
$option['scale'] = $slide_scale;
|
||||
$slider = self::slider((array) $instance['slides'], $option);
|
||||
}
|
||||
|
||||
$m_layout_type = (string) ($instance['m_layout_type'] ?? '');
|
||||
$layout_ratio = max(0, (int) ($instance['layout_ratio'] ?? 60));
|
||||
$cover_row_count = max(1, (int) ($instance['pc_row'] ?? 2));
|
||||
$cover_col_count = max(1, (int) ($instance['pc_col'] ?? 2));
|
||||
$cover_gap = 3;
|
||||
|
||||
// 右侧卡片整体长宽比由左侧幻灯片宽度占比与长宽比推导,gap 与参考主题一致。
|
||||
$cover_base_ratio = $layout_ratio < 100 ? (($layout_ratio * $slide_scale) / (100 - $layout_ratio)) : 0;
|
||||
$cover_ratio = (($cover_base_ratio - ($cover_col_count - 1) * $cover_gap) / $cover_col_count) * $cover_row_count;
|
||||
|
||||
$cover_m_row = max(1, (int) ($instance['m_row'] ?? 2));
|
||||
$m_cover_ratio = (int) ($instance['m_cover_ratio'] ?? 45);
|
||||
if ($m_cover_ratio < 0) $m_cover_ratio = (int) $cover_ratio;
|
||||
if ($m_layout_type === 'default') $cover_m_row = $cover_row_count;
|
||||
|
||||
$style_attr = ' --layout-ratio:' . $layout_ratio . '%; --cover-ratio:' . $cover_ratio . '%; --m-cover-ratio:' . $m_cover_ratio . '%;';
|
||||
|
||||
$is_mobile = function_exists('wp_is_mobile') && wp_is_mobile();
|
||||
$cover_list = array_values((array) ($instance['covers'] ?? []));
|
||||
$cover_list = (array) ($instance['covers'] ?? []);
|
||||
$is_row = count($cover_list) > 1;
|
||||
$layout_type = (string) ($instance['m_layout_type'] ?? 'vertical');
|
||||
$pc_row = max(1, min(4, (int) ($instance['pc_row'] ?? 2)));
|
||||
$m_row = $layout_type === 'default' ? $pc_row : max(1, min(6, (int) ($instance['m_row'] ?? 2)));
|
||||
$row_class = 'col-sm-' . (int) (12 / $pc_row) . ($m_row > 1 ? ' col-xs-' . (int) (12 / $m_row) : '');
|
||||
$m_cover_ratio = max(0, (int) ($instance['m_cover_ratio'] ?? 45));
|
||||
$mask = max(0, min(90, (int) ($instance['mask_opacity'] ?? 0))) / 100;
|
||||
$cover_style_vars = '--font-size:' . (int) ($instance['font_size_pc'] ?? 16) . 'px;--font-size-sm:' . (int) ($instance['font_size_m'] ?? 14) . 'px;';
|
||||
if (!empty($instance['font_bold'])) $cover_style_vars .= '--font-weight:bold;--font-weight-sm:bold;';
|
||||
$row_class = 'col-sm-' . (int) (12 / $cover_row_count);
|
||||
$row_class .= $cover_m_row > 1 ? ' col-xs-' . (int) (12 / $cover_m_row) : '';
|
||||
$animation_class = self::animation_class($instance);
|
||||
|
||||
$font_size_pc = (int) ($instance['font_size_pc'] ?? 16);
|
||||
$font_size_m = (int) ($instance['font_size_m'] ?? 14);
|
||||
$font_color = (string) ($instance['font_color'] ?? '');
|
||||
if ($font_color !== '') $cover_style_vars .= '--color:' . $font_color . ';--color-sm:' . $font_color . ';';
|
||||
$font_style = '';
|
||||
$font_style .= $font_size_pc && 14 !== $font_size_pc ? '--font-size:' . $font_size_pc . 'px;' : '';
|
||||
$font_style .= $font_size_m && 14 !== $font_size_m ? '--font-size-sm:' . $font_size_m . 'px;' : '';
|
||||
$font_style .= !empty($instance['font_bold']) ? '--font-weight:bold;--font-weight-sm:bold;' : '';
|
||||
$font_style .= $font_color !== '' ? '--color:' . $font_color . ';--color-sm:' . $font_color . ';' : '';
|
||||
$font_style_attr = $font_style !== '' ? ' style="' . esc_attr($font_style) . '"' : '';
|
||||
|
||||
$covers = '';
|
||||
foreach ($cover_list as $cover) {
|
||||
if (!is_array($cover)) continue;
|
||||
$hide = (string) ($cover['hide'] ?? '');
|
||||
if (($is_mobile && $hide === 'm') || (!$is_mobile && $hide === 'pc')) continue;
|
||||
$image = esc_url((string) ($cover['image'] ?? ''));
|
||||
$video = esc_url((string) ($cover['video'] ?? ''));
|
||||
$title = (string) ($cover['title'] ?? '');
|
||||
$link = RendererHelpers::link((array) ($cover['link'] ?? []));
|
||||
$card = aksrt_graphic_card([
|
||||
'class' => 'noshadow mb10',
|
||||
'img' => $image,
|
||||
'video' => $video,
|
||||
'img' => (string) ($cover['image'] ?? ''),
|
||||
'video' => (string) ($cover['video'] ?? ''),
|
||||
'alt' => wp_strip_all_tags($title),
|
||||
'link' => ['url' => $link['url'], 'target' => $link['target']],
|
||||
'lazy' => aksrt_is_lazy('lazy_cover'),
|
||||
'more' => trim($title) !== '' ? '<div class="abs-center text-center graphic-text this-font">' . wp_kses_post($title) . '</div>' : '',
|
||||
'height_scale' => 0,
|
||||
'mask_opacity' => (int) ($instance['mask_opacity'] ?? 0),
|
||||
]);
|
||||
if ($card === '') continue;
|
||||
$covers .= '<div class="' . ($is_row ? esc_attr($row_class) : '') . '">' . $card . '</div>';
|
||||
$col_class = trim($animation_class . ($is_row ? ' ' . $row_class : ''));
|
||||
$covers .= '<div class="' . esc_attr($col_class) . '">' . $card . '</div>';
|
||||
}
|
||||
if ($covers !== '') {
|
||||
$covers = '<div class="widget-graphic-cover" style="' . esc_attr($cover_style_vars) . '">' . ($is_row ? '<div class="row gutters-5">' : '') . $covers . ($is_row ? '</div>' : '') . '</div>';
|
||||
$cover_html = '<div class="widget-graphic-cover ' . ($is_row ? 'mb10' : 'mb20') . '"><div' . $font_style_attr . '>' . ($is_row ? '<div class="row gutters-5">' : '') . $covers . ($is_row ? '</div>' : '') . '</div></div>';
|
||||
|
||||
$class = 'flex widget-slider-graphic m-' . sanitize_html_class($m_layout_type);
|
||||
$html = '<div class="' . esc_attr($class) . '" style="' . esc_attr($style_attr) . '" data-row="' . $cover_row_count . '" data-col="' . $cover_col_count . '">';
|
||||
if ($slider !== '') {
|
||||
$html .= '<div class="slider-col mb20' . $animation_class . '">' . $slider . '</div>';
|
||||
} elseif (function_exists('is_super_admin') && is_super_admin()) {
|
||||
$html .= '<div class="slider-col mb20"><div class="alert alert-warning">' . esc_html__('当前模块下没有可显示的幻灯片,请添加幻灯片或移除此模块', 'aksrt') . '</div></div>';
|
||||
}
|
||||
if ($slider === '' && $covers === '') return RendererHelpers::empty_state(__('请配置幻灯片或卡片', 'aksrt'));
|
||||
$layout = max(0, min(100, (int) ($instance['layout_ratio'] ?? 60)));
|
||||
$slide_scale = max(0, (int) ($instance['slide_scale'] ?? 50));
|
||||
$rows = $pc_row;
|
||||
$columns = max(1, (int) ($instance['pc_col'] ?? 2));
|
||||
$base = $layout < 100 ? (($layout * $slide_scale) / (100 - $layout)) : 0;
|
||||
$cover_ratio = $columns > 0 ? ((($base - (($columns - 1) * 3)) / $columns) * $rows) : 0;
|
||||
$style = '--layout-ratio:' . $layout . '%;--cover-ratio:' . $cover_ratio . '%;--m-cover-ratio:' . $m_cover_ratio . '%;';
|
||||
$animation = '';
|
||||
if (!empty($instance['obs_animation'])) {
|
||||
$animation = ' obs-animate ani-' . sanitize_html_class((string) $instance['obs_animation']) . (!empty($instance['animation_repeat']) ? ' obs-animate-repeat' : '');
|
||||
}
|
||||
$class = 'flex widget-slider-graphic m-' . sanitize_html_class($layout_type);
|
||||
$html = '<div class="' . esc_attr($class) . '" style="' . esc_attr($style) . '" data-row="' . $rows . '" data-col="' . $columns . '">';
|
||||
if ($slider !== '') $html .= '<div class="slider-col mb20' . esc_attr($animation) . '">' . $slider . '</div>';
|
||||
if ($covers !== '') $html .= '<div class="cover-col mb10">' . $covers . '</div>';
|
||||
$html .= '<div class="cover-col mb10">' . $cover_html . '</div>';
|
||||
return $html . '</div>';
|
||||
}
|
||||
|
||||
/** 参考主题 obs_animation 入场动画类(作用于 slider-col 与每个卡片列)。 */
|
||||
private static function animation_class(array $instance): string {
|
||||
$animation = (string) ($instance['obs_animation'] ?? '');
|
||||
if ($animation === '') return '';
|
||||
$class = ' obs-animate ani-' . sanitize_html_class($animation);
|
||||
if (!empty($instance['animation_repeat'])) $class .= ' obs-animate-repeat';
|
||||
return $class;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,25 +396,25 @@ final class WidgetSchemas {
|
||||
$asset_uri = function_exists('get_template_directory_uri') ? get_template_directory_uri() . '/assets/img/' : '';
|
||||
$align_uri = function_exists('get_template_directory_uri') ? get_template_directory_uri() . '/assets/images/slider-align/' : '';
|
||||
$slide_fields = [
|
||||
['id' => 'background', 'type' => 'upload', 'label' => '图片背景', 'default' => ''],
|
||||
['id' => 'background_video', 'type' => 'upload', 'label' => '视频背景', 'default' => '', 'description' => '图片背景、视频背景至少二选一;PC 端视频优先,移动端只显示图片。'],
|
||||
['id' => 'background', 'type' => 'upload', 'label' => '图片背景', 'default' => '', 'library' => 'image'],
|
||||
['id' => 'background_video', 'type' => 'upload', 'label' => '视频背景', 'default' => '', 'library' => 'video', 'description' => '(必填)图片背景、视频背景至少二选一,如果同时设置则PC端视频优先(PC端视频加载失败则显示图片)<div class="c-yellow">如果需要在移动端显示,则必须设置图片背景,移动端只显示图片</div>'],
|
||||
['id' => 'hide', 'type' => 'radio', 'label' => '显示规则', 'default' => '', 'options' => ['' => '全部显示', 'pc' => 'PC端不显示', 'm' => '移动端不显示']],
|
||||
['id' => 'link', 'type' => 'link', 'label' => '跳转链接', 'default' => []],
|
||||
['id' => 'image_layer', 'type' => 'group', 'label' => '叠加图层', 'max_items' => 12, 'fields' => [
|
||||
['id' => 'image', 'type' => 'upload', 'label' => '图层图片', 'default' => ''],
|
||||
['id' => 'free_size', 'type' => 'switcher', 'label' => '自由尺寸', 'default' => false],
|
||||
['id' => 'image_layer', 'type' => 'group', 'label' => '叠加图层', 'item_label' => '图层', 'button_title' => '添加图层', 'max_items' => 12, 'description' => '添加额外的幻灯片图层,配合图层设置及幻灯片其它设置,可轻松制作出漂亮无比的幻灯片', 'fields' => [
|
||||
['id' => 'image', 'type' => 'upload', 'label' => '图层图片', 'default' => '', 'library' => 'image'],
|
||||
['id' => 'free_size', 'type' => 'switcher', 'label' => '自由尺寸', 'default' => false, 'description' => '如果图层的尺寸和背景图的尺寸不一致,可开启此项以自定义图层对齐方向'],
|
||||
['id' => 'align', 'type' => 'radio', 'label' => '图层对齐', 'default' => 'center', 'options' => ['left' => '靠左显示', 'center' => '居中显示', 'right' => '靠右显示'], 'dependency' => ['image|free_size', '!=|!=', '|']],
|
||||
['id' => 'parallax', 'type' => 'spinner', 'label' => '视差滚动', 'default' => 0, 'min' => -200, 'max' => 200, 'step' => 5, 'dependency' => ['image', '!=', '']],
|
||||
['id' => 'parallax_scale', 'type' => 'spinner', 'label' => '视差缩放', 'default' => 100, 'min' => 1, 'max' => 200, 'step' => 5, 'dependency' => ['image', '!=', '']],
|
||||
['id' => 'parallax_opacity', 'type' => 'spinner', 'label' => '视差透明', 'default' => 100, 'min' => 1, 'max' => 100, 'step' => 5, 'dependency' => ['image|parallax', '!=|!=', '|']],
|
||||
['id' => 'parallax', 'type' => 'spinner', 'label' => '视差滚动', 'default' => 0, 'min' => -200, 'max' => 200, 'step' => 5, 'unit' => '%', 'description' => '提前或延后进入视线,负值为延后,正值为提前,0为关闭[-200~200]', 'dependency' => ['image', '!=', '']],
|
||||
['id' => 'parallax_scale', 'type' => 'spinner', 'label' => '视差缩放', 'default' => 100, 'min' => 1, 'max' => 200, 'step' => 5, 'unit' => '%', 'description' => '放大或缩小进入视线,原图大小的百分之多少[1~200]', 'dependency' => ['image', '!=', '']],
|
||||
['id' => 'parallax_opacity', 'type' => 'spinner', 'label' => '视差透明', 'default' => 100, 'min' => 1, 'max' => 100, 'step' => 5, 'unit' => '%', 'description' => '以百分之多少的透明度进入视线[1~100]<br>视差功能对浏览器性能有一定影响,如果图层较多,不建议全部开启', 'dependency' => ['image|parallax', '!=|!=', '|']],
|
||||
]],
|
||||
['id' => 'text', 'type' => 'accordion', 'label' => '叠加文案', 'fields' => [
|
||||
['id' => 'title', 'type' => 'textarea', 'label' => '幻灯片文案', 'default' => '', 'allow_html' => true],
|
||||
['id' => 'desc', 'type' => 'textarea', 'label' => '幻灯片简介', 'default' => '', 'allow_html' => true, 'dependency' => ['title', '!=', '']],
|
||||
['id' => 'text_align', 'type' => 'image_select', 'label' => '显示位置', 'default' => 'left-bottom', 'options' => ['left-bottom' => $align_uri . 'left-bottom.jpg', 'left-conter' => $align_uri . 'left-conter.jpg', 'conter-conter' => $align_uri . 'conter-conter.jpg', 'conter-bottom' => $align_uri . 'conter-bottom.jpg', 'right-conter' => $align_uri . 'right-conter.jpg', 'right-bottom' => $align_uri . 'right-bottom.jpg']],
|
||||
['id' => 'text_size_pc', 'type' => 'spinner', 'label' => 'PC端字体大小', 'default' => 30, 'min' => 12, 'max' => 50],
|
||||
['id' => 'text_size_m', 'type' => 'spinner', 'label' => '移动端字体大小', 'default' => 20, 'min' => 12, 'max' => 50],
|
||||
['id' => 'parallax', 'type' => 'spinner', 'label' => '文案视差滚动', 'default' => 40, 'min' => -200, 'max' => 200, 'step' => 10],
|
||||
['id' => 'title', 'type' => 'textarea', 'label' => '幻灯片文案', 'default' => '', 'allow_html' => true, 'attributes' => ['rows' => 1]],
|
||||
['id' => 'desc', 'type' => 'textarea', 'label' => '幻灯片简介', 'default' => '', 'allow_html' => true, 'attributes' => ['rows' => 1], 'description' => '标题、简介均支持HTML代码,请注意代码规范及标签闭合', 'dependency' => ['title', '!=', '']],
|
||||
['id' => 'text_align', 'type' => 'image_select', 'label' => '显示位置', 'default' => 'left-bottom', 'description' => '前景图显示位置及文案位置需合理搭配', 'options' => ['left-bottom' => $align_uri . 'left-bottom.jpg', 'left-conter' => $align_uri . 'left-conter.jpg', 'conter-conter' => $align_uri . 'conter-conter.jpg', 'conter-bottom' => $align_uri . 'conter-bottom.jpg', 'right-conter' => $align_uri . 'right-conter.jpg', 'right-bottom' => $align_uri . 'right-bottom.jpg']],
|
||||
['id' => 'text_size_pc', 'type' => 'spinner', 'label' => 'PC端字体大小', 'default' => 30, 'min' => 12, 'max' => 50, 'unit' => 'PX'],
|
||||
['id' => 'text_size_m', 'type' => 'spinner', 'label' => '移动端字体大小', 'default' => 20, 'min' => 12, 'max' => 50, 'unit' => 'PX', 'description' => '在此设置标题的字体大小,简介的大小为标题大小的60%,最小12px<br>字体越大,文案周边的间距也越大!字体大小请根据内容合理调整,避免在某些设备显示不全'],
|
||||
['id' => 'parallax', 'type' => 'spinner', 'label' => '文案视差滚动', 'default' => 40, 'min' => -200, 'max' => 200, 'step' => 10, 'unit' => '%', 'description' => '视差滚动功能为较背景滚动的时间差,为负数则滚动慢一拍,为正数则滚动快一拍,为0则关闭'],
|
||||
]],
|
||||
];
|
||||
$fields = [
|
||||
@@ -466,10 +466,10 @@ final class WidgetSchemas {
|
||||
['id' => 'm_row', 'type' => 'button_set', 'label' => '移动端卡片单行数量', 'default' => 2, 'dependency' => ['m_layout_type', 'any', 'hide_slide,vertical'], 'options' => [1 => '1个', 2 => '2个', 3 => '3个', 4 => '4个', 6 => '6个']],
|
||||
['id' => 'm_cover_ratio', 'type' => 'spinner', 'label' => '移动端卡片长宽比', 'default' => 45, 'min' => 5, 'max' => 300, 'step' => 5, 'unit' => '%', 'dependency' => ['m_layout_type', 'any', 'hide_slide,vertical']],
|
||||
['type' => 'subheading', 'content' => '左侧幻灯片'],
|
||||
['id' => 'slides', 'type' => 'group', 'label' => '幻灯片内容', 'item_label' => '幻灯片', 'max_items' => 30, 'min_items' => 1, 'default' => $combo_slides, 'fields' => $slide_fields, 'description' => '注意:当添加的幻灯片内容只有一个的时候,则不会以幻灯片的形式展示(不能滑动切换,相当于单个图片或视频)<div class="c-yellow">由于移动端多数浏览器不支持视频背景功能,所以移动端不会显示视频!</div>'],
|
||||
['id' => 'slides', 'type' => 'group', 'label' => '幻灯片内容', 'item_label' => '幻灯片', 'button_title' => '添加幻灯片', 'max_items' => 30, 'min_items' => 1, 'default' => $combo_slides, 'fields' => $slide_fields, 'description' => '注意:当添加的幻灯片内容只有一个的时候,则不会以幻灯片的形式展示(不能滑动切换,相当于单个图片或视频)<div class="c-yellow">由于移动端多数浏览器不支持视频背景功能,所以移动端不会显示视频!</div>'],
|
||||
['id' => 'option', 'type' => 'accordion', 'label' => '幻灯片设置', 'default' => ['button' => true, 'pagination' => true, 'effect' => 'slide', 'spacebetween' => 20, 'speed' => 0, 'interval' => 4, 'autoplay' => true, 'loop' => true], 'fields' => $combo_option],
|
||||
['type' => 'subheading', 'content' => '右侧图文视频卡片'],
|
||||
['id' => 'covers', 'type' => 'group', 'label' => '右侧图文视频卡片', 'item_label' => '卡片', 'max_items' => 30, 'default' => array_fill(0, 4, ['image' => $asset_uri . 'user_t.jpg', 'title' => '']), 'fields' => self::cover_fields(), 'description' => '<div class="c-yellow">注意:由于移动端多数浏览器不支持视频背景功能,所以移动端不会显示视频!</div>'],
|
||||
['id' => 'covers', 'type' => 'group', 'label' => '右侧图文视频卡片', 'item_label' => '卡片', 'button_title' => '添加卡片', 'max_items' => 30, 'default' => array_fill(0, 4, ['image' => $asset_uri . 'user_t.jpg', 'title' => '']), 'fields' => self::cover_fields(), 'description' => '<div class="c-yellow">注意:由于移动端多数浏览器不支持视频背景功能,所以移动端不会显示视频!</div>'],
|
||||
['type' => 'subheading', 'content' => '卡片样式配置'],
|
||||
['id' => 'mask_opacity', 'type' => 'slider', 'label' => '遮罩透明度', 'default' => 0, 'min' => 0, 'max' => 90, 'step' => 1, 'unit' => '%', 'description' => '图片上显示的黑色遮罩层的透明度'],
|
||||
['id' => 'font_size_pc', 'type' => 'spinner', 'label' => 'PC端字体大小', 'default' => 16, 'min' => 10, 'max' => 80, 'step' => 2, 'unit' => 'px'],
|
||||
@@ -589,7 +589,7 @@ final class WidgetSchemas {
|
||||
private static function cover_fields(): array {
|
||||
return [
|
||||
['id' => 'image', 'type' => 'upload', 'label' => '图片背景', 'default' => '', 'library' => 'image'],
|
||||
['id' => 'video', 'type' => 'upload', 'label' => '视频背景', 'default' => '', 'library' => 'video', 'description' => '(必填)图片背景、视频背景至少二选一,如果同时设置则PC端视频优先(PC端视频加载失败则显示图片),移动端只显示图片'],
|
||||
['id' => 'video', 'type' => 'upload', 'label' => '视频背景', 'default' => '', 'library' => 'video', 'description' => '(必填)图片背景、视频背景至少二选一,如果同时设置则PC端视频优先(PC端视频加载失败则显示图片),<span class="c-yellow">移动端只显示图片</span>'],
|
||||
['id' => 'hide', 'type' => 'radio', 'label' => '显示规则', 'default' => '', 'options' => ['' => '全部显示', 'pc' => 'PC端不显示', 'm' => '移动端不显示']],
|
||||
['id' => 'title', 'type' => 'textarea', 'label' => '文字', 'default' => '', 'allow_html' => true, 'attributes' => ['rows' => 1], 'description' => '支持HTML代码'],
|
||||
['id' => 'link', 'type' => 'link', 'label' => '跳转链接', 'default' => []],
|
||||
|
||||
Reference in New Issue
Block a user