feat(雪花效果): 实现权重控制和更多自定义参数

添加形状和媒体权重功能,支持按权重随机选择雪花类型
新增大小比例、偏移比例及各项随机范围参数配置
优化雪花生成逻辑,增加渐进式生成效果
在后台设置页面添加权重和参数配置界面
This commit is contained in:
tikkhun 2025-12-11 11:00:33 +08:00
parent 0db74b9dd6
commit 0c063d304e
3 changed files with 518 additions and 31 deletions

View File

@ -6,13 +6,119 @@
var listContainer = document.getElementById('yooneSnowMediaList'); var listContainer = document.getElementById('yooneSnowMediaList');
if (!addButton || !listContainer) { return; } if (!addButton || !listContainer) { return; }
// 形状与媒体权重容器元素 获取以便动态更新
var weightsContainer = document.getElementById('yooneSnowWeightsContainer');
var shapeWeightsBox = document.getElementById('yooneSnowShapeWeights');
var mediaWeightsBox = document.getElementById('yooneSnowMediaWeights');
// 形状标签映射用于显示友好名称
var shapeLabelsMap = {
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'
};
// 同步形状权重输入 根据勾选状态增删对应输入
function ensureShapeWeightInputs(){
// 条件判断 权重容器不存在则跳过
if (!shapeWeightsBox) { return; }
var checkboxes = document.querySelectorAll('input[name="yoone_snow_mixed_items[]"]');
var presentKeys = {};
// 收集已存在的标签记录用于对比
var existingLabels = shapeWeightsBox.querySelectorAll('label[data-shape-key]');
existingLabels.forEach(function(el){
presentKeys[el.getAttribute('data-shape-key')] = el;
});
checkboxes.forEach(function(cb){
var key = cb.value;
if (cb.checked){
// 条件判断 如果不存在对应输入则创建 默认权重值为 1
if (!presentKeys[key]){
var lab = document.createElement('label');
lab.setAttribute('data-shape-key', key);
lab.style.display = 'flex';
lab.style.flexDirection = 'column';
lab.style.minWidth = '160px';
var title = document.createElement('span');
title.textContent = shapeLabelsMap[key] || key;
var input = document.createElement('input');
input.type = 'number';
input.min = '0';
input.name = 'yoone_snow_shape_weights[' + key + ']';
input.value = '1';
input.style.width = '120px';
lab.appendChild(title);
lab.appendChild(input);
shapeWeightsBox.appendChild(lab);
}
} else {
// 条件判断 如果取消勾选则移除对应输入
var exist = shapeWeightsBox.querySelector('label[data-shape-key="' + key + '"]');
if (exist) { exist.remove(); }
}
});
}
// 添加媒体权重输入 根据附件 ID 创建输入 默认值为 1
function addMediaWeightInput(attachmentId){
if (!mediaWeightsBox) { return; }
var exist = mediaWeightsBox.querySelector('label[data-attachment-id="' + String(attachmentId) + '"]');
if (exist) { return; }
var lab = document.createElement('label');
lab.setAttribute('data-attachment-id', String(attachmentId));
lab.style.display = 'flex';
lab.style.flexDirection = 'column';
lab.style.minWidth = '160px';
var title = document.createElement('span');
title.textContent = 'Media ' + String(attachmentId);
var input = document.createElement('input');
input.type = 'number';
input.min = '0';
input.name = 'yoone_snow_media_weights[' + String(attachmentId) + ']';
input.value = '1';
input.style.width = '120px';
lab.appendChild(title);
lab.appendChild(input);
mediaWeightsBox.appendChild(lab);
}
// 移除媒体权重输入 根据附件 ID 删除对应输入
function removeMediaWeightInput(attachmentId){
if (!mediaWeightsBox) { return; }
var exist = mediaWeightsBox.querySelector('label[data-attachment-id="' + String(attachmentId) + '"]');
if (exist) { exist.remove(); }
}
// 初始化时同步一次形状权重输入
ensureShapeWeightInputs();
// 监听形状复选框变化事件 动态刷新权重输入
document.addEventListener('change', function(event){
var t = event.target;
if (t && t.name === 'yoone_snow_mixed_items[]'){
ensureShapeWeightInputs();
}
});
// 绑定移除按钮事件 使用事件委托处理动态元素 // 绑定移除按钮事件 使用事件委托处理动态元素
listContainer.addEventListener('click', function(event){ listContainer.addEventListener('click', function(event){
// 条件判断 如果点击的是移除按钮则执行删除 // 条件判断 如果点击的是移除按钮则执行删除
var target = event.target; var target = event.target;
if (target && target.classList.contains('yoone-snow-remove-media')){ if (target && target.classList.contains('yoone-snow-remove-media')){
var item = target.closest('.yoone-snow-media-item'); var item = target.closest('.yoone-snow-media-item');
if (item) { item.remove(); } if (item) {
var aidStr = item.getAttribute('data-attachment-id');
item.remove();
// 同步移除对应媒体权重输入
if (aidStr) { removeMediaWeightInput(aidStr); }
}
} }
}); });
@ -59,6 +165,8 @@
wrapper.appendChild(input); wrapper.appendChild(input);
wrapper.appendChild(removeBtn); wrapper.appendChild(removeBtn);
listContainer.appendChild(wrapper); listContainer.appendChild(wrapper);
// 同步添加媒体权重输入 默认权重为 1
addMediaWeightInput(id);
}); });
}); });
frame.open(); frame.open();

View File

@ -18,14 +18,56 @@
// 标记是否已达到显示时长 到达后不再生成新粒子并不再重生 // 标记是否已达到显示时长 到达后不再生成新粒子并不再重生
let hasReachedDuration = false; let hasReachedDuration = false;
// 读取选中的形状集合 简化为仅使用复选框集合
const selectedShapes = (window.YooneSnowSettings && Array.isArray(window.YooneSnowSettings.selectedShapes) && window.YooneSnowSettings.selectedShapes.length > 0) const selectedShapes = (window.YooneSnowSettings && Array.isArray(window.YooneSnowSettings.selectedShapes) && window.YooneSnowSettings.selectedShapes.length > 0)
? window.YooneSnowSettings.selectedShapes ? window.YooneSnowSettings.selectedShapes
: ['dot','flake']; : ['dot','flake'];
// 读取媒体形状列表 用于将媒体图片作为形状参与渲染
const mediaItems = (window.YooneSnowSettings && Array.isArray(window.YooneSnowSettings.mediaItems)) const mediaItems = (window.YooneSnowSettings && Array.isArray(window.YooneSnowSettings.mediaItems))
? window.YooneSnowSettings.mediaItems ? window.YooneSnowSettings.mediaItems
: []; : [];
const defaultShapeWeights = { 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 };
const shapeWeightsRaw = (window.YooneSnowSettings && window.YooneSnowSettings.shapeWeights && typeof window.YooneSnowSettings.shapeWeights === 'object')
? window.YooneSnowSettings.shapeWeights
: {};
const mediaWeightsRaw = (window.YooneSnowSettings && window.YooneSnowSettings.mediaWeights && typeof window.YooneSnowSettings.mediaWeights === 'object')
? window.YooneSnowSettings.mediaWeights
: {};
const shapeWeights = {};
for (let key in defaultShapeWeights){
const val = typeof shapeWeightsRaw[key] !== 'undefined' ? parseInt(shapeWeightsRaw[key], 10) : defaultShapeWeights[key];
shapeWeights[key] = isNaN(val) ? defaultShapeWeights[key] : Math.max(0, val);
}
const sizeScaleRaw = (window.YooneSnowSettings && typeof window.YooneSnowSettings.sizeScale !== 'undefined')
? parseFloat(window.YooneSnowSettings.sizeScale)
: 1.0;
const offsetScaleRaw = (window.YooneSnowSettings && typeof window.YooneSnowSettings.offsetScale !== 'undefined')
? parseFloat(window.YooneSnowSettings.offsetScale)
: 1.0;
const sizeScale = isNaN(sizeScaleRaw) ? 1.0 : Math.max(0.1, sizeScaleRaw);
const offsetScale = isNaN(offsetScaleRaw) ? 1.0 : Math.max(0, offsetScaleRaw);
const radiusMinRaw = (window.YooneSnowSettings && typeof window.YooneSnowSettings.radiusMin !== 'undefined')
? parseFloat(window.YooneSnowSettings.radiusMin)
: 1.0;
const radiusMaxRaw = (window.YooneSnowSettings && typeof window.YooneSnowSettings.radiusMax !== 'undefined')
? parseFloat(window.YooneSnowSettings.radiusMax)
: 3.0;
const driftMinRaw = (window.YooneSnowSettings && typeof window.YooneSnowSettings.driftMin !== 'undefined')
? parseFloat(window.YooneSnowSettings.driftMin)
: 0.4;
const driftMaxRaw = (window.YooneSnowSettings && typeof window.YooneSnowSettings.driftMax !== 'undefined')
? parseFloat(window.YooneSnowSettings.driftMax)
: 1.0;
const swingMinRaw = (window.YooneSnowSettings && typeof window.YooneSnowSettings.swingMin !== 'undefined')
? parseFloat(window.YooneSnowSettings.swingMin)
: 0.2;
const swingMaxRaw = (window.YooneSnowSettings && typeof window.YooneSnowSettings.swingMax !== 'undefined')
? parseFloat(window.YooneSnowSettings.swingMax)
: 1.0;
const radiusMin = isNaN(radiusMinRaw) ? 1.0 : Math.max(0, radiusMinRaw);
const radiusMax = isNaN(radiusMaxRaw) ? 3.0 : Math.max(radiusMin, radiusMaxRaw);
const driftMin = isNaN(driftMinRaw) ? 0.4 : Math.max(0, driftMinRaw);
const driftMax = isNaN(driftMaxRaw) ? 1.0 : Math.max(driftMin, driftMaxRaw);
const swingMin = isNaN(swingMinRaw) ? 0.2 : Math.max(0, swingMinRaw);
const swingMax = isNaN(swingMaxRaw) ? 1.0 : Math.max(swingMin, swingMaxRaw);
function resizeCanvas(){ function resizeCanvas(){
viewportWidth = window.innerWidth; viewportWidth = window.innerWidth;
@ -41,35 +83,64 @@
resizeCanvas(); resizeCanvas();
const snowflakeCount = Math.floor(Math.min(400, Math.max(120, (viewportWidth * viewportHeight) / 12000))); const snowflakesTargetCount = Math.floor(Math.min(400, Math.max(120, (viewportWidth * viewportHeight) / 12000)));
const snowflakes = []; const snowflakes = [];
for (let i = 0; i < snowflakeCount; i++){ function selectWeightedItem(){
// 为每个雪花确定形状类型 从选中集合与媒体列表的合并池中随机选择 const items = [];
const poolSize = selectedShapes.length + mediaItems.length; for (let sIndex = 0; sIndex < selectedShapes.length; sIndex++){
const randomIndex = Math.floor(Math.random() * Math.max(1, poolSize)); const shapeKey = selectedShapes[sIndex];
let chosenType = 'dot'; const weightVal = typeof shapeWeights[shapeKey] !== 'undefined' ? shapeWeights[shapeKey] : 1;
let chosenImageUrl = null; if (weightVal > 0){
if (randomIndex < selectedShapes.length) { items.push({ kind: 'shape', key: shapeKey, weight: weightVal });
// 条件判断 使用普通形状类型
chosenType = selectedShapes[randomIndex];
} else if (mediaItems.length > 0) {
// 条件判断 使用媒体图片作为形状 并记录其 URL
const mediaIndex = randomIndex - selectedShapes.length;
chosenType = 'media_image';
chosenImageUrl = mediaItems[mediaIndex % mediaItems.length];
} }
snowflakes.push({ }
for (let mIndex = 0; mIndex < mediaItems.length; mIndex++){
const mediaUrl = mediaItems[mIndex];
const mediaWeight = typeof mediaWeightsRaw[mediaUrl] !== 'undefined' ? parseInt(mediaWeightsRaw[mediaUrl], 10) : 1;
const finalMediaWeight = isNaN(mediaWeight) ? 1 : Math.max(0, mediaWeight);
if (finalMediaWeight > 0){
items.push({ kind: 'media', url: mediaUrl, weight: finalMediaWeight });
}
}
if (items.length === 0){
return { type: 'dot', url: null };
}
let totalWeight = 0;
for (let i = 0; i < items.length; i++){
totalWeight += items[i].weight;
}
const r = Math.random() * totalWeight;
let acc = 0;
for (let i = 0; i < items.length; i++){
acc += items[i].weight;
if (r <= acc){
if (items[i].kind === 'shape'){
return { type: items[i].key, url: null };
} else {
return { type: 'media_image', url: items[i].url };
}
}
}
return { type: 'dot', url: null };
}
function createSnowflake(){
const picked = selectWeightedItem();
let chosenType = picked.type;
let chosenImageUrl = picked.url;
return {
positionX: Math.random() * viewportWidth, positionX: Math.random() * viewportWidth,
positionY: Math.random() * viewportHeight, positionY: -5 - Math.random() * 20,
radius: Math.random() * 2 + 1, radius: (Math.random() * (radiusMax - radiusMin) + radiusMin) * sizeScale,
driftSpeed: Math.random() * 0.6 + 0.4, driftSpeed: Math.random() * (driftMax - driftMin) + driftMin,
swingAmplitude: Math.random() * 0.8 + 0.2, swingAmplitude: (Math.random() * (swingMax - swingMin) + swingMin) * offsetScale,
shapeType: chosenType, shapeType: chosenType,
imageUrl: chosenImageUrl, imageUrl: chosenImageUrl,
// 标记该粒子是否已经移出视口 用于停止后清理 // 标记该粒子是否已经移出视口 用于停止后清理
outOfView: false outOfView: false
}); };
} }
const spawnIntervalMs = Math.max(10, Math.floor(4000 / Math.max(1, snowflakesTargetCount)));
let nextSpawnTimestamp = performance.now();
function updateSnowflakes(){ function updateSnowflakes(){
for (let j = 0; j < snowflakes.length; j++){ for (let j = 0; j < snowflakes.length; j++){
@ -81,9 +152,9 @@
if (!hasReachedDuration){ if (!hasReachedDuration){
flake.positionY = -5; flake.positionY = -5;
flake.positionX = Math.random() * viewportWidth; flake.positionX = Math.random() * viewportWidth;
flake.radius = Math.random() * 2 + 1; flake.radius = (Math.random() * (radiusMax - radiusMin) + radiusMin) * sizeScale;
flake.driftSpeed = Math.random() * 0.6 + 0.4; flake.driftSpeed = Math.random() * (driftMax - driftMin) + driftMin;
flake.swingAmplitude = Math.random() * 0.8 + 0.2; flake.swingAmplitude = (Math.random() * (swingMax - swingMin) + swingMin) * offsetScale;
} else { } else {
flake.outOfView = true; flake.outOfView = true;
} }
@ -131,6 +202,13 @@
} }
function animate(){ function animate(){
if (!hasReachedDuration && snowflakes.length < snowflakesTargetCount){
const nowTs = performance.now();
while (snowflakes.length < snowflakesTargetCount && nowTs >= nextSpawnTimestamp){
snowflakes.push(createSnowflake());
nextSpawnTimestamp += spawnIntervalMs;
}
}
updateSnowflakes(); updateSnowflakes();
drawSnowflakes(); drawSnowflakes();
// 条件判断 如果设置了有限时长则在达到时长后不再生成新粒子并等待自然落出 // 条件判断 如果设置了有限时长则在达到时长后不再生成新粒子并等待自然落出

View File

@ -2,7 +2,7 @@
/* /*
Plugin Name: Yoone Snow Plugin Name: Yoone Snow
Description: 首页 canvas 雪花效果 Description: 首页 canvas 雪花效果
Version: 1.2.0 Version: 1.3.0
Author: Yoone Author: Yoone
*/ */
@ -82,16 +82,50 @@ function yoone_snow_enqueue_assets() {
$media_ids = get_option('yoone_snow_media_items', array()); $media_ids = get_option('yoone_snow_media_items', array());
if (!is_array($media_ids)) { $media_ids = array(); } if (!is_array($media_ids)) { $media_ids = array(); }
$media_urls = array(); $media_urls = array();
$media_weights_option = get_option('yoone_snow_media_weights', array());
if (!is_array($media_weights_option)) { $media_weights_option = array(); }
$media_weights_by_url = array();
foreach ($media_ids as $mid) { foreach ($media_ids as $mid) {
$url = wp_get_attachment_url(intval($mid)); $url = wp_get_attachment_url(intval($mid));
if ($url) { $media_urls[] = $url; } if ($url) {
$media_urls[] = $url;
$w = isset($media_weights_option[intval($mid)]) ? intval($media_weights_option[intval($mid)]) : 1;
if ($w < 0) { $w = 0; }
$media_weights_by_url[$url] = $w;
}
}
$default_weights = 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,
);
$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;
} }
wp_localize_script($script_handle, 'YooneSnowSettings', array( wp_localize_script($script_handle, 'YooneSnowSettings', array(
'selectedShapes' => $mixed_items_sanitized, 'selectedShapes' => $mixed_items_sanitized,
'mediaItems' => $media_urls, 'mediaItems' => $media_urls,
// 读取并传递首页显示时长设置 单位为秒 0 表示无限
'displayDurationSeconds' => intval(get_option('yoone_snow_home_duration', 0)), 'displayDurationSeconds' => intval(get_option('yoone_snow_home_duration', 0)),
// 传递资源基础映射 用于前端按需加载 SVG 图像 'sizeScale' => floatval(get_option('yoone_snow_size_scale', 1.0)),
'offsetScale' => floatval(get_option('yoone_snow_offset_scale', 1.0)),
'radiusMin' => floatval(get_option('yoone_snow_radius_min', 1.0)),
'radiusMax' => floatval(get_option('yoone_snow_radius_max', 3.0)),
'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( 'assetsMap' => array(
'santa_hat' => plugins_url('assets/圣诞雪帽.svg', __FILE__), 'santa_hat' => plugins_url('assets/圣诞雪帽.svg', __FILE__),
'candy_cane' => plugins_url('assets/圣诞拐杖.svg', __FILE__), 'candy_cane' => plugins_url('assets/圣诞拐杖.svg', __FILE__),
@ -100,6 +134,8 @@ function yoone_snow_enqueue_assets() {
'reindeer' => plugins_url('assets/圣诞麋鹿.svg', __FILE__), 'reindeer' => plugins_url('assets/圣诞麋鹿.svg', __FILE__),
'christmas_berry' => plugins_url('assets/圣诞果.svg', __FILE__), 'christmas_berry' => plugins_url('assets/圣诞果.svg', __FILE__),
), ),
'shapeWeights' => $shape_weights,
'mediaWeights' => $media_weights_by_url,
)); ));
} }
@ -191,6 +227,47 @@ function yoone_snow_register_settings() {
'yoone_snow_section' '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 数组 // 注册媒体形状集合 设置项保存为附件 ID 数组
register_setting('yoone_snow_options', 'yoone_snow_media_items', array( register_setting('yoone_snow_options', 'yoone_snow_media_items', array(
'type' => 'array', 'type' => 'array',
@ -238,6 +315,86 @@ function yoone_snow_register_settings() {
'yoone_snow_section' '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 '<div id="yooneSnowWeightsContainer" style="margin-top:8px;">';
echo '<div id="yooneSnowShapeWeights" style="display:flex;flex-wrap:wrap;gap:12px;margin-bottom:12px;">';
foreach ($selected_shapes as $sk) {
if (!isset($shape_labels[$sk])) { continue; }
$val = isset($shape_weights_current[$sk]) ? intval($shape_weights_current[$sk]) : 1;
echo '<label data-shape-key="' . esc_attr($sk) . '" style="display:flex;flex-direction:column;min-width:160px;">';
echo '<span>' . esc_html($shape_labels[$sk]) . '</span>';
echo '<input type="number" min="0" name="yoone_snow_shape_weights[' . esc_attr($sk) . ']" value="' . esc_attr($val) . '" style="width:120px;" />';
echo '</label>';
}
echo '</div>';
// 渲染媒体权重 按当前媒体列表生成 输入名称为附件 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 '<div id="yooneSnowMediaWeights" style="display:flex;flex-wrap:wrap;gap:12px;">';
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 '<label data-attachment-id="' . esc_attr($aid) . '" style="display:flex;flex-direction:column;min-width:160px;">';
echo '<span>' . esc_html($label) . '</span>';
echo '<input type="number" min="0" name="yoone_snow_media_weights[' . esc_attr($aid) . ']" value="' . esc_attr($val) . '" style="width:120px;" />';
echo '</label>';
}
echo '</div>';
echo '<p class="description">Higher value increases selection probability 0 disables default is 1</p>';
echo '</div>';
// 绑定交互脚本 通过已有 admin 脚本实现动态刷新
},
'yoone_snow',
'yoone_snow_section'
);
// 注册首页显示时长设置 项为整数秒 0 表示无限 // 注册首页显示时长设置 项为整数秒 0 表示无限
register_setting('yoone_snow_options', 'yoone_snow_home_duration', array( register_setting('yoone_snow_options', 'yoone_snow_home_duration', array(
'type' => 'integer', 'type' => 'integer',
@ -263,6 +420,150 @@ function yoone_snow_register_settings() {
'yoone_snow', 'yoone_snow',
'yoone_snow_section' 'yoone_snow_section'
); );
register_setting('yoone_snow_options', 'yoone_snow_size_scale', array(
'type' => 'number',
'sanitize_callback' => function($value) {
$num = floatval($value);
if ($num < 0.1) { $num = 0.1; }
return $num;
},
'default' => 1.0,
));
add_settings_field(
'yoone_snow_size_scale',
'Snow Size Scale',
function() {
$current = floatval(get_option('yoone_snow_size_scale', 1.0));
echo '<input type="number" min="0.1" step="0.1" name="yoone_snow_size_scale" value="' . esc_attr($current) . '" style="width:120px;" />';
echo '<p class="description">Multiply snowflake radius default is 1.0</p>';
},
'yoone_snow',
'yoone_snow_section'
);
register_setting('yoone_snow_options', 'yoone_snow_offset_scale', array(
'type' => 'number',
'sanitize_callback' => function($value) {
$num = floatval($value);
if ($num < 0) { $num = 0; }
return $num;
},
'default' => 1.0,
));
add_settings_field(
'yoone_snow_offset_scale',
'Snow Horizontal Offset Scale',
function() {
$current = floatval(get_option('yoone_snow_offset_scale', 1.0));
echo '<input type="number" min="0" step="0.1" name="yoone_snow_offset_scale" value="' . esc_attr($current) . '" style="width:120px;" />';
echo '<p class="description">Multiply horizontal swing amplitude default is 1.0</p>';
},
'yoone_snow',
'yoone_snow_section'
);
// 随机范围设置 注册与字段渲染 包含半径 漂移速度 摆动幅度的最小与最大
register_setting('yoone_snow_options', 'yoone_snow_radius_min', array(
'type' => 'number',
'sanitize_callback' => function($value) {
$num = floatval($value);
if ($num < 0) { $num = 0; }
return $num;
},
'default' => 1.0,
));
register_setting('yoone_snow_options', 'yoone_snow_radius_max', array(
'type' => 'number',
'sanitize_callback' => function($value) {
$min = floatval(get_option('yoone_snow_radius_min', 1.0));
$num = floatval($value);
if ($num < $min) { $num = $min; }
return $num;
},
'default' => 3.0,
));
add_settings_field(
'yoone_snow_radius_range',
'Radius Random Range',
function() {
$min = floatval(get_option('yoone_snow_radius_min', 1.0));
$max = floatval(get_option('yoone_snow_radius_max', 3.0));
echo '<label style="margin-right:12px;">Min <input type="number" min="0" step="0.1" name="yoone_snow_radius_min" value="' . esc_attr($min) . '" style="width:120px;" /></label>';
echo '<label>Max <input type="number" min="0" step="0.1" name="yoone_snow_radius_max" value="' . esc_attr($max) . '" style="width:120px;" /></label>';
echo '<p class="description">Random radius in [min max] before size scale</p>';
},
'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 '<label style="margin-right:12px;">Min <input type="number" min="0" step="0.1" name="yoone_snow_drift_min" value="' . esc_attr($min) . '" style="width:120px;" /></label>';
echo '<label>Max <input type="number" min="0" step="0.1" name="yoone_snow_drift_max" value="' . esc_attr($max) . '" style="width:120px;" /></label>';
echo '<p class="description">Random vertical drift speed base in [min max]</p>';
},
'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 '<label style="margin-right:12px;">Min <input type="number" min="0" step="0.1" name="yoone_snow_swing_min" value="' . esc_attr($min) . '" style="width:120px;" /></label>';
echo '<label>Max <input type="number" min="0" step="0.1" name="yoone_snow_swing_max" value="' . esc_attr($max) . '" style="width:120px;" /></label>';
echo '<p class="description">Random horizontal swing amplitude base in [min max] before offset scale</p>';
},
'yoone_snow',
'yoone_snow_section'
);
} }
// 添加设置页面到后台菜单 条目在设置菜单下 // 添加设置页面到后台菜单 条目在设置菜单下