feat(widgets): Highlights animate + ComboInfo gutters/style depth

Add scroll-in animation and lazy covers for highlights; combo cards
use row gutters, zibll CSS vars, and optional obs animation.
This commit is contained in:
2026-07-21 09:46:16 +08:00
parent c5b431c15d
commit 8b18459dda
2 changed files with 76 additions and 29 deletions
+29 -4
View File
@@ -44,16 +44,29 @@ class WidgetComboInfo extends \WP_Widget {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
$style = sprintf('--aksrt-zci-radius:%dpx;--aksrt-zci-band-h:%dpx;--aksrt-zci-pc:%d;--aksrt-zci-m:%d;', $radius, $band_h, $pc_row, $m_row);
$wrap = 'aksrt-combo-info' . ($top_band ? ' has-band' : '');
$style = sprintf('--aksrt-zci-radius:%dpx;--aksrt-zci-band-h:%dpx;--aksrt-zci-pc:%d;--aksrt-zci-m:%d;--zci-radius:%dpx;--zci-band-h:%dpx;', $radius, $band_h, $pc_row, $m_row, $radius, $band_h);
$wrap = 'aksrt-combo-info zib-combo-info-card row gutters-10' . ($top_band ? ' has-band zci-has-band' : '');
$anim = sanitize_key((string) ($instance['obs_animation'] ?? ''));
$anim_repeat = !empty($instance['animation_repeat']);
$pc_span = max(1, (int) floor(12 / $pc_row));
$m_span = max(1, (int) floor(12 / $m_row));
$col = 'col-sm-' . $pc_span . ' col-xs-' . $m_span;
echo '<div class="' . esc_attr($wrap) . '" style="' . esc_attr($style) . '">';
$auto_idx = 0;
$ci = 0;
foreach ($cards as $card) {
$html = $this->render_card($card, $cta_default, $auto_idx, $go_link, $top_band);
if ($html !== '') {
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
if ($html === '') {
continue;
}
$pack = function_exists('aksrt_obs_animate_attrs')
? aksrt_obs_animate_attrs($anim, $anim_repeat, $ci * 0.1)
: ['class' => '', 'attr' => ''];
echo '<div class="' . esc_attr(trim($col . $pack['class'])) . '"' . $pack['attr'] . '>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '</div>';
$ci++;
}
echo '</div>';
@@ -219,6 +232,16 @@ class WidgetComboInfo extends \WP_Widget {
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('top_band')); ?>" value="1" <?php checked(!empty($instance['top_band']) || !isset($instance['top_band'])); ?>> <?php esc_html_e('顶部色带', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('go_link')); ?>" value="1" <?php checked(!empty($instance['go_link'])); ?>> <?php esc_html_e('外链跳转页', 'aksrt'); ?></label>
</p>
<p>
<label><?php esc_html_e('入场动画', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('obs_animation')); ?>">
<option value="" <?php selected(($instance['obs_animation'] ?? ''), ''); ?>><?php esc_html_e('无', 'aksrt'); ?></option>
<option value="slideup" <?php selected(($instance['obs_animation'] ?? ''), 'slideup'); ?>><?php esc_html_e('上滑', 'aksrt'); ?></option>
<option value="fade" <?php selected(($instance['obs_animation'] ?? ''), 'fade'); ?>><?php esc_html_e('淡入', 'aksrt'); ?></option>
<option value="zoomin" <?php selected(($instance['obs_animation'] ?? ''), 'zoomin'); ?>><?php esc_html_e('放大', 'aksrt'); ?></option>
</select>
</label>
</p>
<?php
}
@@ -233,6 +256,8 @@ class WidgetComboInfo extends \WP_Widget {
'top_band_height' => max(20, min(160, (int) ($new_instance['top_band_height'] ?? 70))),
'cta_text' => sanitize_text_field((string) ($new_instance['cta_text'] ?? '')),
'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
'obs_animation' => sanitize_key((string) ($new_instance['obs_animation'] ?? '')),
'animation_repeat'=> !empty($new_instance['animation_repeat']) ? 1 : 0,
];
}
}
+47 -25
View File
@@ -1,6 +1,6 @@
<?php
/**
* Highlights cards (zibll zib_widget_highlights lite).
* Highlights cards (zibll zib_widget_highlights) with scroll animation + go_link.
*
* @package AKSRT
*/
@@ -22,12 +22,18 @@ class WidgetHighlights extends \WP_Widget {
);
}
/**
* @param array<string,mixed> $args
* @param array<string,mixed> $instance
*/
public function widget($args, $instance): void {
$cards = $this->normalize($instance['cards'] ?? []);
if (!$cards) {
return;
}
$go_link = !empty($instance['go_link']);
$anim = sanitize_key((string) ($instance['obs_animation'] ?? 'slideup'));
$anim_repeat = !empty($instance['animation_repeat']);
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base);
@@ -38,30 +44,23 @@ class WidgetHighlights extends \WP_Widget {
$large = array_values(array_filter($cards, static fn($c) => ($c['size'] ?? 'medium') === 'large'));
$medium = array_values(array_filter($cards, static fn($c) => ($c['size'] ?? 'medium') === 'medium'));
$small = array_values(array_filter($cards, static fn($c) => ($c['size'] ?? 'medium') === 'small'));
// Fallback: if no size tags, treat all as medium.
if (!$large && !$medium && !$small) {
$medium = $cards;
}
echo '<div class="aksrt-highlights">';
if ($large) {
echo '<div class="aksrt-highlights__row is-large">';
foreach ($large as $c) {
echo $this->card($c, 'large', $go_link); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$idx = 0;
foreach (['large' => $large, 'medium' => $medium, 'small' => $small] as $size => $group) {
if (!$group) {
continue;
}
echo '</div>';
}
if ($medium) {
echo '<div class="aksrt-highlights__row is-medium">';
foreach ($medium as $c) {
echo $this->card($c, 'medium', $go_link); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
echo '</div>';
}
if ($small) {
echo '<div class="aksrt-highlights__row is-small">';
foreach ($small as $c) {
echo $this->card($c, 'small', $go_link); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<div class="aksrt-highlights__row is-' . esc_attr($size) . '">';
foreach ($group as $c) {
$pack = function_exists('aksrt_obs_animate_attrs')
? aksrt_obs_animate_attrs($anim, $anim_repeat, $idx * 0.08)
: ['class' => '', 'attr' => ''];
echo $this->card($c, $size, $go_link, $pack); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$idx++;
}
echo '</div>';
}
@@ -72,8 +71,9 @@ class WidgetHighlights extends \WP_Widget {
/**
* @param array<string,mixed> $c
* @param array{class:string,attr:string} $pack
*/
private function card(array $c, string $size, bool $go_link): string {
private function card(array $c, string $size, bool $go_link, array $pack): string {
$color = sanitize_key((string) ($c['color'] ?? 'blue'));
if (!in_array($color, ['blue', 'pink', 'purple', 'green', 'orange', 'cyan'], true)) {
$color = 'blue';
@@ -85,9 +85,11 @@ class WidgetHighlights extends \WP_Widget {
$tag = $url !== '' ? 'a' : 'div';
$href = $url !== '' ? ' href="' . esc_url($url) . '"' : '';
$img = (string) ($c['image'] ?? '');
$html = '<' . $tag . ' class="aksrt-hl-card size-' . esc_attr($size) . ' color-' . esc_attr($color) . '"' . $href . '>';
$cls = 'aksrt-hl-card size-' . esc_attr($size) . ' color-' . esc_attr($color) . $pack['class'];
$html = '<' . $tag . ' class="' . esc_attr(trim($cls)) . '"' . $href . $pack['attr'] . '>';
if ($img !== '') {
$html .= '<div class="aksrt-hl-card__media"><img src="' . esc_url($img) . '" alt="" loading="lazy" /></div>';
$placeholder = get_template_directory_uri() . '/assets/img/placeholder.svg';
$html .= '<div class="aksrt-hl-card__media"><img class="fit-cover aksrt-lazy lazyload" data-src="' . esc_url($img) . '" src="' . esc_url($placeholder) . '" alt="" loading="lazy" /></div>';
}
$html .= '<div class="aksrt-hl-card__body">';
if (!empty($c['icon'])) {
@@ -145,6 +147,9 @@ class WidgetHighlights extends \WP_Widget {
return $out;
}
/**
* @param array<string,mixed> $instance
*/
public function form($instance): void {
$json = is_string($instance['cards'] ?? null)
? (string) $instance['cards']
@@ -161,17 +166,34 @@ class WidgetHighlights extends \WP_Widget {
</label>
<small><?php esc_html_e('size: large|medium|small;color: blue/pink/…', 'aksrt'); ?></small>
</p>
<p>
<label><?php esc_html_e('入场动画', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('obs_animation')); ?>">
<option value="" <?php selected(($instance['obs_animation'] ?? 'slideup'), ''); ?>><?php esc_html_e('无', 'aksrt'); ?></option>
<option value="fade" <?php selected(($instance['obs_animation'] ?? 'slideup'), 'fade'); ?>><?php esc_html_e('淡入', 'aksrt'); ?></option>
<option value="slideup" <?php selected(($instance['obs_animation'] ?? 'slideup'), 'slideup'); ?>><?php esc_html_e('上滑', 'aksrt'); ?></option>
<option value="zoomin" <?php selected(($instance['obs_animation'] ?? 'slideup'), 'zoomin'); ?>><?php esc_html_e('放大', 'aksrt'); ?></option>
</select>
</label>
</p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('go_link')); ?>" value="1" <?php checked(!empty($instance['go_link'])); ?>> <?php esc_html_e('外链跳转页', 'aksrt'); ?></label>
</p>
<?php
}
/**
* @param array<string,mixed> $new_instance
* @param array<string,mixed> $old_instance
* @return array<string,mixed>
*/
public function update($new_instance, $old_instance): array {
return [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'cards' => $this->normalize($new_instance['cards'] ?? []),
'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'cards' => $this->normalize($new_instance['cards'] ?? []),
'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
'obs_animation' => sanitize_key((string) ($new_instance['obs_animation'] ?? 'slideup')),
'animation_repeat' => !empty($new_instance['animation_repeat']) ? 1 : 0,
];
}
}