From 262fe0c513d9d4c68a387affdf6ad202da4ff126 Mon Sep 17 00:00:00 2001 From: tikkhun Date: Thu, 11 Dec 2025 10:15:49 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=8A=A8=E7=94=BB):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=A6=96=E9=A1=B5=E9=9B=AA=E8=8A=B1=E5=8A=A8=E7=94=BB=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E6=97=B6=E9=95=BF=E9=85=8D=E7=BD=AE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加首页雪花动画显示时长配置选项,支持设置有限时长或无限显示 在达到设定时长后停止生成新粒子并等待现有粒子自然落出后清理资源 --- js/snow-canvas.js | 63 +++++++++++++++++++++++++++++++++++++++++------ yoone-snow.php | 28 +++++++++++++++++++++ 2 files changed, 83 insertions(+), 8 deletions(-) diff --git a/js/snow-canvas.js b/js/snow-canvas.js index 124fb2a..de73dbb 100644 --- a/js/snow-canvas.js +++ b/js/snow-canvas.js @@ -9,6 +9,15 @@ let viewportHeight = window.innerHeight; const devicePixelRatio = window.devicePixelRatio || 1; + // 读取首页显示时长设置 单位为秒 0 表示无限 + const displayDurationSeconds = (window.YooneSnowSettings && typeof window.YooneSnowSettings.displayDurationSeconds !== 'undefined') + ? Math.max(0, parseInt(window.YooneSnowSettings.displayDurationSeconds, 10) || 0) + : 0; + // 记录启动时间 用于计算已运行时间 + const startTimestamp = performance.now(); + // 标记是否已达到显示时长 到达后不再生成新粒子并不再重生 + let hasReachedDuration = false; + // 读取选中的形状集合 简化为仅使用复选框集合 const selectedShapes = (window.YooneSnowSettings && Array.isArray(window.YooneSnowSettings.selectedShapes) && window.YooneSnowSettings.selectedShapes.length > 0) ? window.YooneSnowSettings.selectedShapes @@ -56,7 +65,9 @@ driftSpeed: Math.random() * 0.6 + 0.4, swingAmplitude: Math.random() * 0.8 + 0.2, shapeType: chosenType, - imageUrl: chosenImageUrl + imageUrl: chosenImageUrl, + // 标记该粒子是否已经移出视口 用于停止后清理 + outOfView: false }); } @@ -66,11 +77,16 @@ flake.positionY += flake.driftSpeed * 2 + flake.radius * 0.25; flake.positionX += Math.sin(flake.positionY * 0.01) * flake.swingAmplitude; if (flake.positionY > viewportHeight + 5){ - flake.positionY = -5; - flake.positionX = Math.random() * viewportWidth; - flake.radius = Math.random() * 2 + 1; - flake.driftSpeed = Math.random() * 0.6 + 0.4; - flake.swingAmplitude = Math.random() * 0.8 + 0.2; + // 条件判断 如果尚未达到时长则重生 如果已达到则标记为移出视口 + if (!hasReachedDuration){ + flake.positionY = -5; + flake.positionX = Math.random() * viewportWidth; + flake.radius = Math.random() * 2 + 1; + flake.driftSpeed = Math.random() * 0.6 + 0.4; + flake.swingAmplitude = Math.random() * 0.8 + 0.2; + } else { + flake.outOfView = true; + } } } } @@ -82,6 +98,8 @@ for (let k = 0; k < snowflakes.length; k++){ const flake = snowflakes[k]; + // 条件判断 如果该粒子已移出视口则不再绘制 + if (flake.outOfView){ continue; } // 条件判断 如果是媒体图片类型则使用通用图像渲染 if (flake.shapeType === 'media_image' && flake.imageUrl){ const record = window.YooneSnowGetOrLoadImage ? window.YooneSnowGetOrLoadImage(flake.imageUrl) : { img: null, ready: false }; @@ -101,17 +119,46 @@ } } + // 清理停止函数 用于在达到设定时长后停止动画并释放资源 + function cleanupStop(){ + // 条件判断 如果存在窗口尺寸事件监听则移除 + if (typeof onResize === 'function') { + window.removeEventListener('resize', onResize); + } + // 清空画布并隐藏元素 + context.clearRect(0, 0, viewportWidth, viewportHeight); + canvas.style.display = 'none'; + } + function animate(){ updateSnowflakes(); drawSnowflakes(); + // 条件判断 如果设置了有限时长则在达到时长后不再生成新粒子并等待自然落出 + if (displayDurationSeconds > 0 && !hasReachedDuration){ + const elapsedSeconds = (performance.now() - startTimestamp) / 1000; + if (elapsedSeconds >= displayDurationSeconds){ + hasReachedDuration = true; + } + } + // 条件判断 如果已达到时长且所有粒子都移出视口则执行清理停止 + if (hasReachedDuration){ + const allOut = snowflakes.every(function(f){ return f.outOfView; }); + if (allOut){ + cleanupStop(); + return; + } + } requestAnimationFrame(animate); } - window.addEventListener('resize', function(){ + // 定义窗口尺寸事件处理器 以便在停止时移除 + function onResize(){ + // 条件判断 保证画布尺寸与视口一致 resizeCanvas(); viewportWidth = window.innerWidth; viewportHeight = window.innerHeight; - }); + } + window.addEventListener('resize', onResize); animate(); } diff --git a/yoone-snow.php b/yoone-snow.php index 3853f85..3607922 100644 --- a/yoone-snow.php +++ b/yoone-snow.php @@ -89,6 +89,8 @@ function yoone_snow_enqueue_assets() { wp_localize_script($script_handle, 'YooneSnowSettings', array( 'selectedShapes' => $mixed_items_sanitized, 'mediaItems' => $media_urls, + // 读取并传递首页显示时长设置 单位为秒 0 表示无限 + 'displayDurationSeconds' => intval(get_option('yoone_snow_home_duration', 0)), // 传递资源基础映射 用于前端按需加载 SVG 图像 'assetsMap' => array( 'santa_hat' => plugins_url('assets/圣诞雪帽.svg', __FILE__), @@ -235,6 +237,32 @@ function yoone_snow_register_settings() { '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' + ); } // 添加设置页面到后台菜单 条目在设置菜单下