feat(雪花效果): 添加雪花形状配置选项并实现不同形状的渲染

添加后台设置页面允许用户选择雪花形状(圆点、雪花或混合)
将配置传递到前端脚本并实现不同形状的雪花渲染
在插件列表添加设置链接方便快速访问
This commit is contained in:
tikkhun 2025-12-10 17:51:37 +08:00
parent a1b53eff06
commit a95b3e2362
2 changed files with 158 additions and 6 deletions

View File

@ -1,12 +1,17 @@
(function(){ (function(){
// 初始化函数 用于启动雪花效果
function init(){ function init(){
var canvas = document.getElementById('effectiveAppsSnow'); var canvas = document.getElementById('effectiveAppsSnow');
// 条件判断 如果未找到画布元素则不执行
if (!canvas) return; if (!canvas) return;
var context = canvas.getContext('2d'); var context = canvas.getContext('2d');
var viewportWidth = window.innerWidth; var viewportWidth = window.innerWidth;
var viewportHeight = window.innerHeight; var viewportHeight = window.innerHeight;
var devicePixelRatio = window.devicePixelRatio || 1; var devicePixelRatio = window.devicePixelRatio || 1;
// 从后端配置读取雪花形状 默认值为 dot
var configuredShape = (window.YooneSnowSettings && window.YooneSnowSettings.shape) ? window.YooneSnowSettings.shape : 'dot';
function resizeCanvas(){ function resizeCanvas(){
viewportWidth = window.innerWidth; viewportWidth = window.innerWidth;
viewportHeight = window.innerHeight; viewportHeight = window.innerHeight;
@ -24,12 +29,21 @@
var snowflakeCount = Math.floor(Math.min(400, Math.max(120, (viewportWidth * viewportHeight) / 12000))); var snowflakeCount = Math.floor(Math.min(400, Math.max(120, (viewportWidth * viewportHeight) / 12000)));
var snowflakes = []; var snowflakes = [];
for (var i = 0; i < snowflakeCount; i++){ for (var i = 0; i < snowflakeCount; i++){
// 为每个雪花确定形状类型 如果是 mixed 则随机赋值 dot 或 flake
var snowflakeShapeType;
if (configuredShape === 'mixed') {
// 条件判断 随机选择雪花形状
snowflakeShapeType = Math.random() < 0.5 ? 'dot' : 'flake';
} else {
snowflakeShapeType = configuredShape;
}
snowflakes.push({ snowflakes.push({
positionX: Math.random() * viewportWidth, positionX: Math.random() * viewportWidth,
positionY: Math.random() * viewportHeight, positionY: Math.random() * viewportHeight,
radius: Math.random() * 2 + 1, radius: Math.random() * 2 + 1,
driftSpeed: Math.random() * 0.6 + 0.4, driftSpeed: Math.random() * 0.6 + 0.4,
swingAmplitude: Math.random() * 0.8 + 0.2 swingAmplitude: Math.random() * 0.8 + 0.2,
shapeType: snowflakeShapeType
}); });
} }
@ -48,16 +62,62 @@
} }
} }
// 绘制雪花形状 使用线段绘制六角雪花
function drawSnowflake(x, y, size) {
context.save();
context.translate(x, y);
context.fillStyle = 'rgba(255,255,255,0.9)';
context.strokeStyle = 'rgba(255,255,255,0.9)';
context.lineWidth = size * 0.15;
// 循环绘制六个分支
for (var i = 0; i < 6; i++) {
context.rotate(Math.PI / 3);
context.beginPath();
context.moveTo(0, 0);
context.lineTo(0, size);
context.stroke();
// 绘制分支上的小分叉
context.beginPath();
context.moveTo(0, size * 0.3);
context.lineTo(size * 0.3, size * 0.5);
context.stroke();
context.beginPath();
context.moveTo(0, size * 0.5);
context.lineTo(-size * 0.3, size * 0.7);
context.stroke();
context.beginPath();
context.moveTo(0, size * 0.7);
context.lineTo(size * 0.3, size * 0.9);
context.stroke();
}
context.restore();
}
// 绘制圆点形状 使用填充圆形作为雪花
function drawDot(x, y, size) {
context.beginPath();
context.arc(x, y, size, 0, Math.PI * 2);
context.fillStyle = 'rgba(255,255,255,0.9)';
context.fill();
}
function drawSnowflakes(){ function drawSnowflakes(){
context.clearRect(0, 0, viewportWidth, viewportHeight); context.clearRect(0, 0, viewportWidth, viewportHeight);
context.fillStyle = 'rgba(255,255,255,0.9)';
context.beginPath();
for (var k = 0; k < snowflakes.length; k++){ for (var k = 0; k < snowflakes.length; k++){
var flake = snowflakes[k]; var flake = snowflakes[k];
context.moveTo(flake.positionX, flake.positionY); // 条件判断 根据形状类型绘制雪花
context.arc(flake.positionX, flake.positionY, flake.radius, 0, Math.PI * 2); if (flake.shapeType === 'dot') {
drawDot(flake.positionX, flake.positionY, flake.radius);
} else {
drawSnowflake(flake.positionX, flake.positionY, flake.radius * 3);
}
} }
context.fill();
} }
function animate(){ function animate(){
@ -75,6 +135,7 @@
animate(); animate();
} }
// 条件判断 如果文档尚未加载则等待 DOMContentLoaded 事件
if (document.readyState === 'loading'){ if (document.readyState === 'loading'){
document.addEventListener('DOMContentLoaded', init); document.addEventListener('DOMContentLoaded', init);
} else { } else {

View File

@ -23,6 +23,16 @@ function yoone_snow_enqueue_assets() {
$script_src = plugins_url('js/snow-canvas.js', __FILE__); $script_src = plugins_url('js/snow-canvas.js', __FILE__);
wp_register_script($script_handle, $script_src, array(), '1.1.0', true); wp_register_script($script_handle, $script_src, array(), '1.1.0', true);
wp_enqueue_script($script_handle); wp_enqueue_script($script_handle);
// 将后端设置传递到前端脚本 变量名称为 YooneSnowSettings
$shape = get_option('yoone_snow_shape', 'dot');
if (!in_array($shape, array('dot', 'flake', 'mixed'), true)) {
// 如果选项值不合法则回退到默认值 dot
$shape = 'dot';
}
wp_localize_script($script_handle, 'YooneSnowSettings', array(
'shape' => $shape,
));
} }
function yoone_snow_render_overlay() { function yoone_snow_render_overlay() {
@ -37,3 +47,84 @@ add_action('wp_enqueue_scripts', 'yoone_snow_enqueue_assets');
add_action('wp_body_open', 'yoone_snow_render_overlay'); add_action('wp_body_open', 'yoone_snow_render_overlay');
add_action('wp_footer', 'yoone_snow_render_overlay', 100); add_action('wp_footer', 'yoone_snow_render_overlay', 100);
// 注册设置页面和设置项 用于选择雪花形状
function yoone_snow_register_settings() {
// 注册设置项 选项名称为 yoone_snow_shape 默认值为 dot
register_setting('yoone_snow_options', 'yoone_snow_shape', array(
'type' => 'string',
'sanitize_callback' => function($value) {
// 对提交的值进行校验 只允许 dot flake mixed 三种
$allowed = array('dot', 'flake', 'mixed');
if (!is_string($value)) { return 'dot'; }
return in_array($value, $allowed, true) ? $value : 'dot';
},
'default' => 'dot',
));
// 添加设置分区 标题为 Snow Settings
add_settings_section(
'yoone_snow_section',
'Snow Settings',
function() {
// 输出分区描述 使用英文标点保证兼容
echo '<p>Configure snow appearance</p>';
},
'yoone_snow'
);
// 添加设置字段 下拉选择雪花形状
add_settings_field(
'yoone_snow_shape',
'Snow Shape',
function() {
// 渲染选择框 选项值 dot flake mixed
$current = get_option('yoone_snow_shape', 'dot');
echo '<select name="yoone_snow_shape" id="yoone_snow_shape">';
echo '<option value="dot"' . selected($current, 'dot', false) . '>Dot</option>';
echo '<option value="flake"' . selected($current, 'flake', false) . '>Snowflake</option>';
echo '<option value="mixed"' . selected($current, 'mixed', false) . '>Mixed</option>';
echo '</select>';
echo '<p class="description">Choose dot snowflake or mixed</p>';
},
'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 '<div class="wrap">';
echo '<h1>Yoone Snow</h1>';
echo '<form method="post" action="options.php">';
settings_fields('yoone_snow_options');
do_settings_sections('yoone_snow');
submit_button();
echo '</form>';
echo '</div>';
}
);
}
// 在 admin 初始化时注册设置 在 admin 菜单挂载页面
add_action('admin_init', 'yoone_snow_register_settings');
add_action('admin_menu', 'yoone_snow_add_settings_page');
// 在插件列表行添加 Settings 链接 指向设置页面
function yoone_snow_plugin_action_links($links) {
// 构造设置页面链接 使用 admin_url 保证后台路径正确
$settingsUrl = admin_url('options-general.php?page=yoone_snow');
$settingsLink = '<a href="' . esc_url($settingsUrl) . '">Settings</a>';
// 将设置链接插入到最前面 便于用户点击
array_unshift($links, $settingsLink);
return $links;
}
// 绑定到当前插件的 action links 钩子 使用 plugin_basename 计算插件标识
add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'yoone_snow_plugin_action_links');