1, 'flake' => 4, 'yuanbao' => 1, 'coin' => 1, 'santa_hat' => 1, 'candy_cane' => 1, 'christmas_sock' => 1, 'christmas_tree' => 1, 'reindeer' => 1, 'christmas_berry' => 1, ); $saved_weights = get_option('yoone_snow_shape_weights', $default_weights); if (!is_array($saved_weights)) { $saved_weights = array(); } $shape_weights = array(); foreach ($default_weights as $k => $v) { $val = isset($saved_weights[$k]) ? intval($saved_weights[$k]) : intval($v); if ($val < 0) { $val = 0; } $shape_weights[$k] = $val; } // 读取尺寸组合设置 包含最小与最大半径 如未设置则回退到旧选项与默认值 $size_group = get_option('yoone_snow_size', array('min' => 1.0, 'max' => 3.0)); $radius_min_val = isset($size_group['min']) ? floatval($size_group['min']) : floatval(get_option('yoone_snow_radius_min', 1.0)); $radius_max_val = isset($size_group['max']) ? floatval($size_group['max']) : floatval(get_option('yoone_snow_radius_max', 3.0)); if ($radius_min_val < 0) { $radius_min_val = 0.0; } if ($radius_max_val < $radius_min_val) { $radius_max_val = $radius_min_val; } wp_localize_script($script_handle, 'YooneSnowSettings', array( 'selectedShapes' => $mixed_items_sanitized, 'mediaItems' => $media_urls, 'displayDurationSeconds' => intval(get_option('yoone_snow_home_duration', 0)), 'maxCount' => intval(get_option('yoone_snow_max_count', 0)), 'radiusMin' => $radius_min_val, 'radiusMax' => $radius_max_val, 'driftMin' => floatval(get_option('yoone_snow_drift_min', 0.4)), 'driftMax' => floatval(get_option('yoone_snow_drift_max', 1.0)), 'swingMin' => floatval(get_option('yoone_snow_swing_min', 0.2)), 'swingMax' => floatval(get_option('yoone_snow_swing_max', 1.0)), 'assetsMap' => array( 'santa_hat' => plugins_url('assets/圣诞雪帽.svg', __FILE__), 'candy_cane' => plugins_url('assets/圣诞拐杖.svg', __FILE__), 'christmas_sock' => plugins_url('assets/圣诞袜子.svg', __FILE__), 'christmas_tree' => plugins_url('assets/圣诞树.svg', __FILE__), 'reindeer' => plugins_url('assets/圣诞麋鹿.svg', __FILE__), 'christmas_berry' => plugins_url('assets/圣诞果.svg', __FILE__), ), 'shapeWeights' => $shape_weights, 'mediaWeights' => $media_weights_by_url, )); } // 在后台设置页面加载媒体库脚本和交互脚本 用于选择 SVG 或图片 function yoone_snow_admin_enqueue($hook) { // 条件判断 仅在插件设置页面加载脚本 保持性能 if ($hook !== 'settings_page_yoone_snow') { return; } // 加载媒体库脚本 以便使用 wp.media 选择器 wp_enqueue_media(); // 注册并加载后台交互脚本 $admin_script_handle = 'yoone-snow-admin-media'; wp_register_script($admin_script_handle, plugins_url('js/admin-media.js', __FILE__), array(), '1.1.0', true); wp_enqueue_script($admin_script_handle); } function yoone_snow_render_overlay() { if (!yoone_snow_is_enabled()) { return; } static $yoone_snow_rendered = false; if ($yoone_snow_rendered) { return; } $yoone_snow_rendered = true; echo ''; } add_action('wp_enqueue_scripts', 'yoone_snow_enqueue_assets'); add_action('wp_body_open', 'yoone_snow_render_overlay'); add_action('wp_footer', 'yoone_snow_render_overlay', 100); // 注册设置页面和设置项 用于选择雪花形状 function yoone_snow_register_settings() { // 移除形状下拉选项 仅保留复选集合设置 // 注册 mixed 形状集合设置项 默认包含 dot 和 flake register_setting('yoone_snow_options', 'yoone_snow_mixed_items', array( 'type' => 'array', 'sanitize_callback' => function($value) { // 将输入统一为数组 并过滤到允许的集合 $allowed = array('dot','flake','yuanbao','coin','santa_hat','candy_cane','christmas_sock','christmas_tree','reindeer','christmas_berry'); if (is_string($value)) { $value = array_filter(array_map('trim', explode(',', $value))); } if (!is_array($value)) { $value = array('dot','flake'); } $filtered = array_values(array_unique(array_intersect($value, $allowed))); return !empty($filtered) ? $filtered : array('dot','flake'); }, 'default' => array('dot','flake'), )); // 添加设置分区 标题为 Snow Settings add_settings_section( 'yoone_snow_section', 'Snow Settings', function() { // 输出分区描述 使用英文标点保证兼容 echo '

Configure snow appearance

'; }, 'yoone_snow' ); // 移除下拉字段 保留复选框作为唯一选择入口 // 添加形状复选集合 用于选择参与渲染的形状 add_settings_field( 'yoone_snow_mixed_items', 'Shapes', function() { $current_list = get_option('yoone_snow_mixed_items', array('dot','flake')); if (is_string($current_list)) { $current_list = array_filter(array_map('trim', explode(',', $current_list))); } $options = array( 'dot' => 'Dot', 'flake' => 'Snowflake', 'yuanbao' => 'Yuanbao', 'coin' => 'Coin', 'santa_hat' => 'Santa Hat', 'candy_cane' => 'Candy Cane', 'christmas_sock' => 'Christmas Sock', 'christmas_tree' => 'Christmas Tree', 'reindeer' => 'Reindeer', 'christmas_berry' => 'Christmas Berry', ); foreach ($options as $key => $label) { $checked = in_array($key, $current_list, true) ? 'checked' : ''; echo ''; } echo '

Choose shapes to render

'; }, 'yoone_snow', 'yoone_snow_section' ); register_setting('yoone_snow_options', 'yoone_snow_shape_weights', array( 'type' => 'array', 'sanitize_callback' => function($value) { $allowed = array('dot','flake','yuanbao','coin','santa_hat','candy_cane','christmas_sock','christmas_tree','reindeer','christmas_berry'); $defaults = array( 'dot' => 1, 'flake' => 4, 'yuanbao' => 1, 'coin' => 1, 'santa_hat' => 1, 'candy_cane' => 1, 'christmas_sock' => 1, 'christmas_tree' => 1, 'reindeer' => 1, 'christmas_berry' => 1, ); if (!is_array($value)) { $value = array(); } $clean = array(); foreach ($allowed as $key) { $num = isset($value[$key]) ? intval($value[$key]) : (isset($defaults[$key]) ? intval($defaults[$key]) : 1); if ($num < 0) { $num = 0; } $clean[$key] = $num; } return $clean; }, 'default' => array( 'dot' => 1, 'flake' => 4, 'yuanbao' => 1, 'coin' => 1, 'santa_hat' => 1, 'candy_cane' => 1, 'christmas_sock' => 1, 'christmas_tree' => 1, 'reindeer' => 1, 'christmas_berry' => 1, ), )); // 形状权重字段将移动到媒体形状字段之后 以满足界面顺序需求 // 注册媒体形状集合 设置项保存为附件 ID 数组 register_setting('yoone_snow_options', 'yoone_snow_media_items', array( 'type' => 'array', 'sanitize_callback' => function($value) { // 将输入统一为整数 ID 数组 并过滤无效值 if (is_string($value)) { $value = array_filter(array_map('trim', explode(',', $value))); } if (!is_array($value)) { $value = array(); } $ids = array(); foreach ($value as $item) { $id = intval($item); if ($id > 0) { $ids[] = $id; } } return array_values(array_unique($ids)); }, 'default' => array(), )); // 添加媒体形状选择字段 支持从媒体库选择图片或 SVG add_settings_field( 'yoone_snow_media_items', 'Media Shapes', function() { // 读取当前已选择的附件 ID 列表 并渲染缩略图列表与添加按钮 $current_media = get_option('yoone_snow_media_items', array()); if (!is_array($current_media)) { $current_media = array(); } echo '
'; foreach ($current_media as $attachment_id) { $url = wp_get_attachment_thumb_url($attachment_id); if (!$url) { $url = wp_get_attachment_url($attachment_id); } $safe_id = intval($attachment_id); $safe_url = esc_url($url); echo '
'; echo 'media'; echo ''; echo ''; echo '
'; } echo '
'; echo '

'; echo '

Choose images or SVG from media library to render

'; }, 'yoone_snow', 'yoone_snow_section' ); // 注册媒体权重设置项 将附件 ID 映射到权重数值 默认 1 register_setting('yoone_snow_options', 'yoone_snow_media_weights', array( 'type' => 'array', 'sanitize_callback' => function($value) { // 将输入统一为附件 ID 到非负整数权重的映射 if (!is_array($value)) { $value = array(); } $clean = array(); foreach ($value as $id => $num) { $aid = intval($id); $weight = intval($num); if ($aid > 0) { if ($weight < 0) { $weight = 0; } $clean[$aid] = $weight; } } return $clean; }, 'default' => array(), )); // 在媒体形状字段之后渲染权重字段 包含形状权重与媒体权重 add_settings_field( 'yoone_snow_weights_combined', 'Weights', function() { // 渲染形状权重 输入仅显示已勾选的形状 默认 1 $selected_shapes = get_option('yoone_snow_mixed_items', array('dot','flake')); if (is_string($selected_shapes)) { $selected_shapes = array_filter(array_map('trim', explode(',', $selected_shapes))); } if (!is_array($selected_shapes)) { $selected_shapes = array('dot','flake'); } $shape_labels = array( 'dot' => 'Dot', 'flake' => 'Snowflake', 'yuanbao' => 'Yuanbao', 'coin' => 'Coin', 'santa_hat' => 'Santa Hat', 'candy_cane' => 'Candy Cane', 'christmas_sock' => 'Christmas Sock', 'christmas_tree' => 'Christmas Tree', 'reindeer' => 'Reindeer', 'christmas_berry' => 'Christmas Berry', ); $shape_weights_current = get_option('yoone_snow_shape_weights', array()); if (!is_array($shape_weights_current)) { $shape_weights_current = array(); } echo '
'; echo '
'; foreach ($selected_shapes as $sk) { if (!isset($shape_labels[$sk])) { continue; } $val = isset($shape_weights_current[$sk]) ? intval($shape_weights_current[$sk]) : 1; echo ''; } echo '
'; // 渲染媒体权重 按当前媒体列表生成 输入名称为附件 ID 映射 默认 1 $current_media = get_option('yoone_snow_media_items', array()); if (!is_array($current_media)) { $current_media = array(); } $media_weights_current = get_option('yoone_snow_media_weights', array()); if (!is_array($media_weights_current)) { $media_weights_current = array(); } echo '
'; foreach ($current_media as $attachment_id) { $aid = intval($attachment_id); $label = 'Media ' . $aid; $val = isset($media_weights_current[$aid]) ? intval($media_weights_current[$aid]) : 1; echo ''; } echo '
'; echo '

Higher value increases selection probability 0 disables default is 1

'; echo '
'; // 绑定交互脚本 通过已有 admin 脚本实现动态刷新 }, 'yoone_snow', 'yoone_snow_section' ); // 注册首页显示时长设置 项为整数秒 0 表示无限 register_setting('yoone_snow_options', 'yoone_snow_home_duration', array( 'type' => 'integer', 'sanitize_callback' => function($value) { // 将输入转换为非负整数 单位为秒 0 表示无限 $num = intval($value); if ($num < 0) { $num = 0; } return $num; }, 'default' => 0, )); // 添加首页显示时长字段 输入为数字最小值为 0 add_settings_field( 'yoone_snow_home_duration', 'Home Display Duration Seconds', function() { // 读取当前设置值 并渲染数字输入框 $current = intval(get_option('yoone_snow_home_duration', 0)); echo ''; echo '

Duration in seconds for snow on home 0 means infinite

'; }, 'yoone_snow', 'yoone_snow_section' ); // 注册目标在屏最大数量设置项 0 表示自动根据视口面积 register_setting('yoone_snow_options', 'yoone_snow_max_count', array( 'type' => 'integer', 'sanitize_callback' => function($value) { $num = intval($value); if ($num < 0) { $num = 0; } // 上限保护 防止过大影响性能 if ($num > 1000) { $num = 1000; } return $num; }, 'default' => 0, )); // 添加输入字段 用于设置在屏最大数量 0 表示自动 add_settings_field( 'yoone_snow_max_count', 'Max Snowflakes On Screen', function() { $current = intval(get_option('yoone_snow_max_count', 0)); echo ''; echo '

0 means auto based on viewport area upper bound 1000

'; }, 'yoone_snow', 'yoone_snow_section' ); // 尺寸组合设置 使用单一选项 snow size 存储最小与最大半径 register_setting('yoone_snow_options', 'yoone_snow_size', array( 'type' => 'array', 'sanitize_callback' => function($value) { // 将输入统一为包含 min 与 max 的数值数组 并保证非负与 max 不小于 min if (!is_array($value)) { $value = array(); } $min = isset($value['min']) ? floatval($value['min']) : 1.0; $max = isset($value['max']) ? floatval($value['max']) : 3.0; if ($min < 0) { $min = 0.0; } if ($max < $min) { $max = $min; } return array('min' => $min, 'max' => $max); }, 'default' => array('min' => 1.0, 'max' => 3.0), )); add_settings_field( 'yoone_snow_size', 'Snow Size', function() { // 渲染组合输入 使用同一选项保存最小与最大半径 $grp = get_option('yoone_snow_size', array('min' => 1.0, 'max' => 3.0)); $min = isset($grp['min']) ? floatval($grp['min']) : 1.0; $max = isset($grp['max']) ? floatval($grp['max']) : 3.0; echo ''; echo ''; echo '

Random radius in [min max] single option

'; }, 'yoone_snow', 'yoone_snow_section' ); // 漂移速度与摆动幅度的随机范围设置 保持独立选项 register_setting('yoone_snow_options', 'yoone_snow_drift_min', array( 'type' => 'number', 'sanitize_callback' => function($value) { $num = floatval($value); if ($num < 0) { $num = 0; } return $num; }, 'default' => 0.4, )); register_setting('yoone_snow_options', 'yoone_snow_drift_max', array( 'type' => 'number', 'sanitize_callback' => function($value) { $min = floatval(get_option('yoone_snow_drift_min', 0.4)); $num = floatval($value); if ($num < $min) { $num = $min; } return $num; }, 'default' => 1.0, )); add_settings_field( 'yoone_snow_drift_range', 'Drift Speed Random Range', function() { $min = floatval(get_option('yoone_snow_drift_min', 0.4)); $max = floatval(get_option('yoone_snow_drift_max', 1.0)); echo ''; echo ''; echo '

Random vertical drift speed base in [min max]

'; }, 'yoone_snow', 'yoone_snow_section' ); register_setting('yoone_snow_options', 'yoone_snow_swing_min', array( 'type' => 'number', 'sanitize_callback' => function($value) { $num = floatval($value); if ($num < 0) { $num = 0; } return $num; }, 'default' => 0.2, )); register_setting('yoone_snow_options', 'yoone_snow_swing_max', array( 'type' => 'number', 'sanitize_callback' => function($value) { $min = floatval(get_option('yoone_snow_swing_min', 0.2)); $num = floatval($value); if ($num < $min) { $num = $min; } return $num; }, 'default' => 1.0, )); add_settings_field( 'yoone_snow_swing_range', 'Swing Amplitude Random Range', function() { $min = floatval(get_option('yoone_snow_swing_min', 0.2)); $max = floatval(get_option('yoone_snow_swing_max', 1.0)); echo ''; echo ''; echo '

Random horizontal swing amplitude base in [min max] before offset scale

'; }, 'yoone_snow', 'yoone_snow_section' ); } // 添加设置页面到后台菜单 条目在设置菜单下 function yoone_snow_add_settings_page() { add_options_page( 'Yoone Snow', 'Yoone Snow', 'manage_options', 'yoone_snow', function() { // 渲染设置页面 表单提交到 options.php echo '
'; echo '

Yoone Snow

'; echo '
'; settings_fields('yoone_snow_options'); do_settings_sections('yoone_snow'); submit_button(); echo '
'; echo '
'; } ); } // 在 admin 初始化时注册设置 在 admin 菜单挂载页面 add_action('admin_init', 'yoone_snow_register_settings'); add_action('admin_menu', 'yoone_snow_add_settings_page'); add_action('admin_enqueue_scripts', 'yoone_snow_admin_enqueue'); // 在插件列表行添加 Settings 链接 指向设置页面 function yoone_snow_plugin_action_links($links) { // 构造设置页面链接 使用 admin_url 保证后台路径正确 $settingsUrl = admin_url('options-general.php?page=yoone_snow'); $settingsLink = 'Settings'; // 将设置链接插入到最前面 便于用户点击 array_unshift($links, $settingsLink); return $links; } // 绑定到当前插件的 action links 钩子 使用 plugin_basename 计算插件标识 add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'yoone_snow_plugin_action_links');