feat: 统一移动端和桌面端动画系统
- 整合 CSS 动画变量系统,添加 --animation-instant (100ms) 时长 - 添加 --ease-smooth 缓动函数用于平滑过渡 - 优化卡片入场动画,使用 scale(0.95) + translateY(8px) 更自然 - 统一链接、按钮、标签等元素的过渡动画时长和缓动函数 - 优化顶栏搜索框展开/收起动画 - 优化侧边栏搜索框和菜单项的交互动画 - 优化文章卡片悬浮效果,使用 spring 缓动 - 统一 JS 中的滚动动画时长 (回顶 600ms, 评论回复 400ms) - 优化折叠区块展开/收起动画时长 - 更新分页按钮、日历导航等组件的过渡动画
This commit is contained in:
@@ -0,0 +1,93 @@
|
|||||||
|
# Design Document: Privacy Page Mobile UI Optimization
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
本设计文档描述了 Argon 主题隐私政策页面移动端 UI 问题的修复方案。核心问题是隐私政策页面未正确应用全局样式设置,导致侧边抽屉功能异常和文章页面转发动画问题。
|
||||||
|
|
||||||
|
修复策略是确保隐私政策页面正确加载和初始化全局 JavaScript 功能,同时优化相关 CSS 动画效果。
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
隐私政策页面需要正确继承全局组件:
|
||||||
|
- Sidebar Drawer 侧边抽屉(包含 TOC、Duolingo、Theme Switcher)
|
||||||
|
- Share Panel 分享面板
|
||||||
|
- Ripple Effect 涟漪点击效果
|
||||||
|
|
||||||
|
## Components and Interfaces
|
||||||
|
|
||||||
|
### 1. TOC Generator (文章目录生成器)
|
||||||
|
|
||||||
|
解析页面标题元素并生成导航目录,需确保在隐私政策页面正确初始化。
|
||||||
|
|
||||||
|
### 2. Duolingo Streak (多邻国连胜组件)
|
||||||
|
|
||||||
|
显示博主的多邻国学习连胜记录,需在隐私政策页面与其他页面保持一致。
|
||||||
|
|
||||||
|
### 3. Ripple Effect (涟漪效果)
|
||||||
|
|
||||||
|
涟漪动画需修改为:从点击位置扩散,结束时使用 opacity 淡出而非 scale 收缩。
|
||||||
|
|
||||||
|
### 4. Theme Switcher (主题切换器)
|
||||||
|
|
||||||
|
添加 CSS transition 实现平滑的暗黑/明亮模式切换。
|
||||||
|
|
||||||
|
### 5. Share Panel (分享面板)
|
||||||
|
|
||||||
|
修复退出动画方向,确保向上滑出且不出现歪斜。
|
||||||
|
|
||||||
|
## Data Models
|
||||||
|
|
||||||
|
动画配置使用 CSS 变量统一管理,已在 style.css 中定义。
|
||||||
|
|
||||||
|
## Correctness Properties
|
||||||
|
|
||||||
|
*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
|
||||||
|
|
||||||
|
由于本需求主要涉及 UI 动画和样式修复,大部分验收标准属于视觉效果验证,不适合自动化属性测试。以下为可测试的属性:
|
||||||
|
|
||||||
|
### Property 1: TOC 生成完整性
|
||||||
|
*For any* 包含标题元素(h1-h6)的 HTML 内容,TOC_Generator 生成的目录项数量应等于页面中标题元素的数量
|
||||||
|
**Validates: Requirements 1.1**
|
||||||
|
|
||||||
|
### Property 2: 目录层级正确性
|
||||||
|
*For any* 生成的目录结构,每个目录项的层级值应与其对应标题元素的层级(h1=1, h2=2, ..., h6=6)一致
|
||||||
|
**Validates: Requirements 1.2**
|
||||||
|
|
||||||
|
### Property 3: 动画时长有效性
|
||||||
|
*For any* 涟漪效果动画,其时长应在 300-400ms 范围内;*For any* 主题切换过渡,其时长应在 200-300ms 范围内
|
||||||
|
**Validates: Requirements 3.4, 4.2**
|
||||||
|
|
||||||
|
### Property 4: 分享面板进出动画一致性
|
||||||
|
*For any* 分享面板的进入和退出动画,其 transition-duration 值应相等
|
||||||
|
**Validates: Requirements 5.4**
|
||||||
|
|
||||||
|
### Property 5: 分享选项视口可见性
|
||||||
|
*For any* 屏幕尺寸,分享面板展开时所有分享选项应完整显示在视口范围内,且与屏幕边缘保持安全边距
|
||||||
|
**Validates: Requirements 6.1, 6.3, 6.4**
|
||||||
|
|
||||||
|
### Property 6: 错落动画延迟递增
|
||||||
|
*For any* 分享面板中的按钮序列,第 n 个按钮的 transition-delay 应大于第 n-1 个按钮的 transition-delay
|
||||||
|
**Validates: Requirements 7.4**
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
1. TOC 生成失败:显示"无目录"提示(Requirements 1.4)
|
||||||
|
2. 多邻国 API 失败:隐藏组件或显示友好错误提示(Requirements 2.3)
|
||||||
|
3. 动画性能问题:使用 GPU 加速属性(transform, opacity)确保流畅(Requirements 7.1)
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
### 单元测试
|
||||||
|
- TOC 生成器:测试标题解析和层级计算
|
||||||
|
- CSS 属性验证:检查动画时长、缓动函数等配置值
|
||||||
|
|
||||||
|
### 属性测试
|
||||||
|
- 使用 Jest 进行 DOM 测试
|
||||||
|
- 验证 TOC 生成的完整性和正确性(Property 1, 2)
|
||||||
|
- 验证 CSS 动画配置的有效性(Property 3, 4, 6)
|
||||||
|
- 验证分享选项的视口可见性(Property 5)
|
||||||
|
|
||||||
|
### 手动测试
|
||||||
|
- 视觉效果验证:涟漪动画淡出效果、主题切换平滑度、分享面板动画方向
|
||||||
|
- 跨设备测试:不同屏幕尺寸下的表现
|
||||||
|
- 性能测试:动画流畅度,无抖动或卡顿
|
||||||
|
|||||||
@@ -698,7 +698,7 @@ if (argonConfig.waterflow_columns != "1") {
|
|||||||
$backToTopBtn.on("click" , function(){
|
$backToTopBtn.on("click" , function(){
|
||||||
$("body,html").stop().animate({
|
$("body,html").stop().animate({
|
||||||
scrollTop: 0
|
scrollTop: 0
|
||||||
}, 800, 'easeOutExpo');
|
}, 600, 'easeOutExpo');
|
||||||
});
|
});
|
||||||
|
|
||||||
$toggleDarkmode.on("click" , function(){
|
$toggleDarkmode.on("click" , function(){
|
||||||
@@ -923,16 +923,16 @@ if (argonConfig.waterflow_columns != "1") {
|
|||||||
}
|
}
|
||||||
$("body,html").animate({
|
$("body,html").animate({
|
||||||
scrollTop: $('#post_comment').offset().top - 100
|
scrollTop: $('#post_comment').offset().top - 100
|
||||||
}, 500, 'easeOutCirc');
|
}, 400, 'easeOutCirc');
|
||||||
$('#post_comment_reply_info').slideDown(500, 'easeOutCirc');
|
$('#post_comment_reply_info').slideDown(350, 'easeOutCirc');
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
$("#post_comment_content").focus();
|
$("#post_comment_content").focus();
|
||||||
}, 500);
|
}, 350);
|
||||||
}
|
}
|
||||||
function cancelReply(){
|
function cancelReply(){
|
||||||
replying = false;
|
replying = false;
|
||||||
replyID = 0;
|
replyID = 0;
|
||||||
$('#post_comment_reply_info').slideUp(300, 'easeOutCirc');
|
$('#post_comment_reply_info').slideUp(250, 'easeOutCirc');
|
||||||
$("#post_comment").removeClass("post-comment-force-privatemode-on post-comment-force-privatemode-off");
|
$("#post_comment").removeClass("post-comment-force-privatemode-on post-comment-force-privatemode-off");
|
||||||
}
|
}
|
||||||
$(document).on("click" , ".comment-reply" , function(){
|
$(document).on("click" , ".comment-reply" , function(){
|
||||||
@@ -968,7 +968,7 @@ if (argonConfig.waterflow_columns != "1") {
|
|||||||
}
|
}
|
||||||
$("body,html").animate({
|
$("body,html").animate({
|
||||||
scrollTop: $('#post_comment').offset().top - 100
|
scrollTop: $('#post_comment').offset().top - 100
|
||||||
}, 500, 'easeOutCirc');
|
}, 400, 'easeOutCirc');
|
||||||
$("#post_comment_content").focus();
|
$("#post_comment_content").focus();
|
||||||
}
|
}
|
||||||
function cancelEdit(clear){
|
function cancelEdit(clear){
|
||||||
@@ -1083,9 +1083,9 @@ if (argonConfig.waterflow_columns != "1") {
|
|||||||
$(document).on("click" , "#post_comment_toggle_extra_input" , function(){
|
$(document).on("click" , "#post_comment_toggle_extra_input" , function(){
|
||||||
$("#post_comment").toggleClass("show-extra-input");
|
$("#post_comment").toggleClass("show-extra-input");
|
||||||
if ($("#post_comment").hasClass("show-extra-input")){
|
if ($("#post_comment").hasClass("show-extra-input")){
|
||||||
$("#post_comment_extra_input").slideDown(300, 'easeOutCirc');
|
$("#post_comment_extra_input").slideDown(250, 'easeOutCirc');
|
||||||
}else{
|
}else{
|
||||||
$("#post_comment_extra_input").slideUp(300, 'easeOutCirc');
|
$("#post_comment_extra_input").slideUp(200, 'easeOutCirc');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2776,7 +2776,7 @@ $(document).on("click" , ".collapse-block .collapse-block-title" , function(){
|
|||||||
$(block).toggleClass("collapsed");
|
$(block).toggleClass("collapsed");
|
||||||
let inner = $(".collapse-block-body", block);
|
let inner = $(".collapse-block-body", block);
|
||||||
if (block.hasClass("collapsed")){
|
if (block.hasClass("collapsed")){
|
||||||
inner.stop(true, false).slideUp(300, 'easeOutCirc');
|
inner.stop(true, false).slideUp(250, 'easeOutCirc');
|
||||||
}else{
|
}else{
|
||||||
inner.stop(true, false).slideDown(300, 'easeOutCirc');
|
inner.stop(true, false).slideDown(300, 'easeOutCirc');
|
||||||
}
|
}
|
||||||
|
|||||||
63
style.css
63
style.css
@@ -204,13 +204,15 @@ Tags: 简约, 两栏, 侧栏在左边, 浮动侧栏, 文章目录, 自适应,
|
|||||||
|
|
||||||
--color-selection: #cce2ff;
|
--color-selection: #cce2ff;
|
||||||
|
|
||||||
/* Animation System - Duration Variables */
|
/* ========== 统一动画系统 - 桌面端和移动端一致 ========== */
|
||||||
|
/* 动画时长 - 基于 Material Design 3 规范 */
|
||||||
|
--animation-instant: 100ms;
|
||||||
--animation-fast: 150ms;
|
--animation-fast: 150ms;
|
||||||
--animation-normal: 250ms;
|
--animation-normal: 250ms;
|
||||||
--animation-slow: 400ms;
|
--animation-slow: 400ms;
|
||||||
--animation-slower: 600ms;
|
--animation-slower: 600ms;
|
||||||
|
|
||||||
/* Animation System - Material 3 Easing Functions */
|
/* 缓动函数 - Material 3 标准曲线 */
|
||||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||||
--ease-standard-decelerate: cubic-bezier(0, 0, 0, 1);
|
--ease-standard-decelerate: cubic-bezier(0, 0, 0, 1);
|
||||||
--ease-standard-accelerate: cubic-bezier(0.3, 0, 1, 1);
|
--ease-standard-accelerate: cubic-bezier(0.3, 0, 1, 1);
|
||||||
@@ -218,11 +220,12 @@ Tags: 简约, 两栏, 侧栏在左边, 浮动侧栏, 文章目录, 自适应,
|
|||||||
--ease-emphasized-decelerate: cubic-bezier(0.05, 0.7, 0.1, 1);
|
--ease-emphasized-decelerate: cubic-bezier(0.05, 0.7, 0.1, 1);
|
||||||
--ease-emphasized-accelerate: cubic-bezier(0.3, 0, 0.8, 0.15);
|
--ease-emphasized-accelerate: cubic-bezier(0.3, 0, 0.8, 0.15);
|
||||||
|
|
||||||
/* Animation System - Spring & Bounce Easing */
|
/* 弹性缓动 - 用于交互反馈 */
|
||||||
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
|
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
--ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
--ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||||
|
--ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
|
||||||
/* State Layer Opacity - Material 3 */
|
/* 状态层透明度 - Material 3 */
|
||||||
--state-hover-opacity: 0.08;
|
--state-hover-opacity: 0.08;
|
||||||
--state-focus-opacity: 0.12;
|
--state-focus-opacity: 0.12;
|
||||||
--state-pressed-opacity: 0.12;
|
--state-pressed-opacity: 0.12;
|
||||||
@@ -454,7 +457,7 @@ body {
|
|||||||
|
|
||||||
a {
|
a {
|
||||||
|
|
||||||
transition: color 0.25s ease;
|
transition: color var(--animation-normal) var(--ease-standard);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1578,7 +1581,7 @@ html.triple-column #content {
|
|||||||
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|
||||||
transform: scale(0.8);
|
transform: scale(0.95) translateY(8px);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1594,13 +1597,13 @@ html.triple-column #content {
|
|||||||
|
|
||||||
#primary {
|
#primary {
|
||||||
|
|
||||||
transition: all 0.3s ease;
|
transition: all var(--animation-normal) var(--ease-standard);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
|
|
||||||
animation: card-show 0.25s ease-out;
|
animation: card-show var(--animation-normal) var(--ease-emphasized-decelerate);
|
||||||
|
|
||||||
transform-origin: center top;
|
transform-origin: center top;
|
||||||
|
|
||||||
@@ -1714,7 +1717,7 @@ html.darkmode #navbar-main {
|
|||||||
|
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
|
|
||||||
transition: all 0.3s ease;
|
transition: all var(--animation-normal) var(--ease-standard);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1748,7 +1751,7 @@ body.leftbar-can-headroom.headroom---unpinned #navbar-main {
|
|||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
transition: all 0.3s ease;
|
transition: all var(--animation-normal) var(--ease-standard);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1798,7 +1801,7 @@ body.leftbar-can-headroom.headroom---unpinned #navbar-main {
|
|||||||
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
transition: color 0.3s ease;
|
transition: color var(--animation-normal) var(--ease-standard);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1832,7 +1835,7 @@ body.leftbar-can-headroom.headroom---unpinned #navbar-main {
|
|||||||
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
transition: color 0.3s ease;
|
transition: color var(--animation-normal) var(--ease-standard);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1840,7 +1843,7 @@ body.leftbar-can-headroom.headroom---unpinned #navbar-main {
|
|||||||
|
|
||||||
color: rgba(255, 255, 255, 0.7);
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
|
||||||
transition: color 0.3s ease;
|
transition: color var(--animation-normal) var(--ease-standard);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1868,7 +1871,7 @@ body.leftbar-can-headroom.headroom---unpinned #navbar-main {
|
|||||||
|
|
||||||
#navbar_search_input_container .input-group-text {
|
#navbar_search_input_container .input-group-text {
|
||||||
|
|
||||||
transition: all 0.5s cubic-bezier(0.4, 0, 0, 1);
|
transition: all var(--animation-slow) var(--ease-emphasized);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2309,7 +2312,7 @@ html:not(.is-home) .cover-scroll-down {
|
|||||||
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
||||||
transition: background 0.2s ease-in-out;
|
transition: background var(--animation-fast) var(--ease-standard);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2383,9 +2386,9 @@ html:not(.is-home) .cover-scroll-down {
|
|||||||
|
|
||||||
#leftbar_search_container {
|
#leftbar_search_container {
|
||||||
|
|
||||||
transition: width 0.3s cubic-bezier(0.4, 0, 0, 1), height 0.3s cubic-bezier(0.4, 0, 0, 1), box-shadow 0.15s ease,
|
transition: width var(--animation-normal) var(--ease-emphasized), height var(--animation-normal) var(--ease-emphasized), box-shadow var(--animation-fast) var(--ease-standard),
|
||||||
|
|
||||||
transform 0.15s ease;
|
transform var(--animation-fast) var(--ease-standard);
|
||||||
|
|
||||||
height: 30px;
|
height: 30px;
|
||||||
|
|
||||||
@@ -2457,7 +2460,7 @@ html.darkmode.amoled-dark #leftbar_search_container {
|
|||||||
|
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
|
||||||
transition: all 0.3s ease;
|
transition: all var(--animation-normal) var(--ease-standard);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2543,7 +2546,7 @@ html.darkmode #leftbar_part2_inner::-webkit-scrollbar-thumb {
|
|||||||
|
|
||||||
border-bottom: 1px solid transparent;
|
border-bottom: 1px solid transparent;
|
||||||
|
|
||||||
transition: border-bottom 0.2s ease;
|
transition: border-bottom var(--animation-fast) var(--ease-standard);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2661,7 +2664,7 @@ html.darkmode .sidebar-tab-switcher > a.active {
|
|||||||
|
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
|
|
||||||
transition: background 0.2s ease;
|
transition: background var(--animation-fast) var(--ease-standard);
|
||||||
|
|
||||||
background: var(--color-border-on-foreground);
|
background: var(--color-border-on-foreground);
|
||||||
|
|
||||||
@@ -2711,7 +2714,7 @@ html.darkmode .sidebar-tab-switcher > a.active {
|
|||||||
|
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
|
|
||||||
transition: background 0.2s ease;
|
transition: background var(--animation-fast) var(--ease-standard);
|
||||||
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
||||||
@@ -2983,7 +2986,7 @@ html.darkmode #leftbar_announcement {
|
|||||||
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
||||||
transition: all 0.25s ease;
|
transition: all var(--animation-normal) var(--ease-standard);
|
||||||
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
@@ -3083,7 +3086,7 @@ html.darkmode .wp-block-calendar tbody td {
|
|||||||
|
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
|
|
||||||
transition: all 0.3s ease;
|
transition: all var(--animation-normal) var(--ease-standard);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3139,7 +3142,7 @@ html.is-home.banner-as-cover #float_action_buttons.hidden {
|
|||||||
|
|
||||||
border: none !important;
|
border: none !important;
|
||||||
|
|
||||||
transition: all 0.3s ease;
|
transition: all var(--animation-normal) var(--ease-standard);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3925,7 +3928,7 @@ html.filter-grayscale {
|
|||||||
|
|
||||||
.page-link {
|
.page-link {
|
||||||
|
|
||||||
transition: background-color 0.2s ease;
|
transition: background-color var(--animation-fast) var(--ease-standard);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3945,7 +3948,7 @@ html.filter-grayscale {
|
|||||||
|
|
||||||
.post-preview {
|
.post-preview {
|
||||||
|
|
||||||
transition: all 0.5s ease;
|
transition: all var(--animation-slow) var(--ease-emphasized);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3979,7 +3982,7 @@ html.filter-grayscale {
|
|||||||
|
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
|
|
||||||
transition: all 0.35s ease;
|
transition: all var(--animation-normal) var(--ease-standard);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3991,7 +3994,7 @@ html.filter-grayscale {
|
|||||||
|
|
||||||
#main.waterflow .post-title {
|
#main.waterflow .post-title {
|
||||||
|
|
||||||
transition: all 0.3s ease;
|
transition: all var(--animation-normal) var(--ease-standard);
|
||||||
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
@@ -14938,8 +14941,8 @@ html.darkmode .card {
|
|||||||
|
|
||||||
/* 卡片悬浮提升效果 */
|
/* 卡片悬浮提升效果 */
|
||||||
article.post.card {
|
article.post.card {
|
||||||
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1),
|
transition: transform var(--animation-normal) var(--ease-spring),
|
||||||
box-shadow 0.3s ease;
|
box-shadow var(--animation-normal) var(--ease-standard);
|
||||||
}
|
}
|
||||||
|
|
||||||
article.post.card:hover {
|
article.post.card:hover {
|
||||||
|
|||||||
Reference in New Issue
Block a user