feat: 新增疯言疯语功能并优化UI样式
- 添加疯言疯语服务及页面,支持随机字符颜色变化效果 - 引入汉仪唐韵字体并优化全局字体设置 - 重构日期工具函数,优化时间显示格式 - 改进左侧模块布局,添加文章/分类/标签统计 - 优化浮动按钮组件,增加动态过渡效果 - 调整多个页面的背景透明度,提升视觉一致性 - 完善文章保存页面样式和交互逻辑 - 更新关于页面内容,增加个人介绍和技术栈展示 - 修复路由状态管理问题,优化页面跳转逻辑
This commit is contained in:
@@ -7,37 +7,210 @@
|
||||
:key="item.id"
|
||||
>
|
||||
<div class="nonsense-meta-info">
|
||||
<span class="nonsense-time">{{ item.time }}</span>
|
||||
<span class="nonsense-time">{{ formatRelativeTime(item.time) }}</span>
|
||||
</div>
|
||||
<div class="nonsense-content">
|
||||
<span
|
||||
v-for="(char, index) in item.content.split('')"
|
||||
:key="index"
|
||||
:ref="el => setCharRef(el, item.id, index)"
|
||||
:style="getCharStyle(item.id, index)"
|
||||
>{{ char }}</span>
|
||||
</div>
|
||||
<div class="nonsense-content">{{ item.content }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
import {nonsenseService } from '@/services'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { formatRelativeTime} from '@/utils/dateUtils'
|
||||
/**
|
||||
* 吐槽数据列表
|
||||
* 仅站长可见/可发
|
||||
*/
|
||||
const nonsenseList = ref([
|
||||
{
|
||||
id: 1,
|
||||
content: '嘿嘿 嘿嘿嘿(流口水ing)',
|
||||
time: '2025-09-26 09:30'
|
||||
const nonsenseList = ref([])
|
||||
|
||||
// 存储字符引用和样式的映射
|
||||
const charRefs = ref(new Map())
|
||||
const charStyles = ref(new Map())
|
||||
|
||||
// 定时器引用
|
||||
let colorChangeTimer = null
|
||||
|
||||
/**
|
||||
* 加载所有吐槽内容
|
||||
*/
|
||||
const loadNonsenseList = async () => {
|
||||
try {
|
||||
const response = await nonsenseService.getAllNonsense()
|
||||
if (response.code === 200) {
|
||||
nonsenseList.value = response.data
|
||||
}else{
|
||||
ElMessage.error('加载吐槽内容失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载吐槽内容失败:', error)
|
||||
} finally {
|
||||
console.log('加载吐槽内容完成')
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
// 设置字符引用
|
||||
const setCharRef = (el, itemId, index) => {
|
||||
if (el) {
|
||||
const key = `${itemId}_${index}`
|
||||
charRefs.value.set(key, el)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取字符样式
|
||||
const getCharStyle = (itemId, index) => {
|
||||
const key = `${itemId}_${index}`
|
||||
return charStyles.value.get(key) || {}
|
||||
}
|
||||
|
||||
// 生成随机颜色
|
||||
const getRandomColor = () => {
|
||||
const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#f9ca24', '#6c5ce7', '#e84393', '#00b894', '#fdcb6e']
|
||||
return colors[Math.floor(Math.random() * colors.length)]
|
||||
}
|
||||
|
||||
// 随机改变部分字体颜色并添加信号故障效果
|
||||
const randomChangeColors = () => {
|
||||
const keys = Array.from(charRefs.value.keys())
|
||||
if (keys.length === 0) return
|
||||
|
||||
// 随机选择20-30%的字符改变颜色
|
||||
const countToChange = Math.floor(keys.length * (Math.random() * 0.1 + 0.2))
|
||||
const shuffledKeys = [...keys].sort(() => 0.5 - Math.random())
|
||||
const selectedKeys = shuffledKeys.slice(0, countToChange)
|
||||
|
||||
// 创建信号故障效果
|
||||
createSignalGlitchEffect(selectedKeys)
|
||||
}
|
||||
|
||||
// 创建数字噪点效果
|
||||
const createDigitalNoiseEffect = (selectedKeys) => {
|
||||
// 噪点字符集
|
||||
const noiseChars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+-=[]{}|;:,.<>?';
|
||||
|
||||
selectedKeys.forEach(key => {
|
||||
const charElement = charRefs.value.get(key);
|
||||
if (charElement) {
|
||||
const rect = charElement.getBoundingClientRect();
|
||||
const container = charElement.closest('.nonsense-item');
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
|
||||
// 生成3-5个噪点
|
||||
const noiseCount = Math.floor(Math.random() * 3) + 3;
|
||||
|
||||
for (let i = 0; i < noiseCount; i++) {
|
||||
const noiseEl = document.createElement('span');
|
||||
noiseEl.textContent = noiseChars.charAt(Math.floor(Math.random() * noiseChars.length));
|
||||
|
||||
// 随机位置相对于原字符
|
||||
const x = Math.random() * 30 - 20; // -20到20px
|
||||
const y = Math.random() * 20 - 15; // -15到15px
|
||||
|
||||
// 随机大小和透明度
|
||||
const size = Math.random() * 8 + 8; // 8-16px
|
||||
const opacity = Math.random() * 0.6 + 0.2; // 0.2-0.8
|
||||
|
||||
noiseEl.style.cssText = `
|
||||
position: absolute;
|
||||
left: ${rect.left - containerRect.left + x}px;
|
||||
top: ${rect.top - containerRect.top + y}px;
|
||||
font-size: ${size}px;
|
||||
opacity: ${opacity};
|
||||
color: ${getRandomColor()};
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
font-family: monospace;
|
||||
transition: all 0.2s ease;
|
||||
`;
|
||||
container.appendChild(noiseEl);
|
||||
|
||||
// 噪点逐渐消失
|
||||
setTimeout(() => {
|
||||
noiseEl.style.opacity = '0';
|
||||
setTimeout(() => {
|
||||
if (container.contains(noiseEl)) {
|
||||
container.removeChild(noiseEl);
|
||||
}
|
||||
}, 200);
|
||||
}, Math.random() * 300 + 200); // 200-500ms后开始消失
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 创建信号故障效果
|
||||
const createSignalGlitchEffect = (selectedKeys) => {
|
||||
// 第一步:随机偏移字符位置,模拟信号干扰
|
||||
selectedKeys.forEach(key => {
|
||||
const glitchOffset = {
|
||||
transform: `translate(${Math.random() * 6 - 3}px, ${Math.random() * 6 - 3}px)`,
|
||||
opacity: Math.random() * 0.4 + 0.6, // 随机透明度
|
||||
transition: 'all 0.1s ease'
|
||||
}
|
||||
charStyles.value.set(key, glitchOffset)
|
||||
})
|
||||
|
||||
// 同时创建数字噪点效果
|
||||
createDigitalNoiseEffect(selectedKeys);
|
||||
|
||||
// 第二步:快速恢复并闪烁
|
||||
setTimeout(() => {
|
||||
selectedKeys.forEach(key => {
|
||||
const flashStyle = {
|
||||
transform: 'translate(0, 0)',
|
||||
opacity: Math.random() > 0.5 ? 0 : 1, // 闪烁效果
|
||||
transition: 'all 0.05s ease'
|
||||
}
|
||||
charStyles.value.set(key, flashStyle)
|
||||
})
|
||||
|
||||
// 第三步:最终设置新颜色
|
||||
setTimeout(() => {
|
||||
selectedKeys.forEach(key => {
|
||||
const finalStyle = {
|
||||
color: getRandomColor(),
|
||||
opacity: 1,
|
||||
transform: 'translate(0, 0)',
|
||||
transition: 'all 0.3s ease'
|
||||
}
|
||||
charStyles.value.set(key, finalStyle)
|
||||
})
|
||||
}, 80)
|
||||
}, 100)}
|
||||
|
||||
// 组件挂载时获取数据并启动定时器
|
||||
onMounted(() => {
|
||||
loadNonsenseList()
|
||||
|
||||
// 启动定时器
|
||||
colorChangeTimer = setInterval(randomChangeColors, 1000)
|
||||
})
|
||||
|
||||
// 组件卸载时清除定时器
|
||||
onBeforeUnmount(() => {
|
||||
if (colorChangeTimer) {
|
||||
clearInterval(colorChangeTimer)
|
||||
colorChangeTimer = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 吐槽页面主容器样式 */
|
||||
.nonsense-container {
|
||||
background: rgba(255,255,255,0.95);
|
||||
/* background-color: rgba(255, 255, 255, 0.85); */
|
||||
border-radius: 12px;
|
||||
padding: 32px 20px 24px 20px;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.06);
|
||||
/* box-shadow: 0 2px 12px rgba(0,0,0,0.06); */
|
||||
transition: box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
@@ -74,7 +247,7 @@ const nonsenseList = ref([
|
||||
|
||||
/* 吐槽项样式 */
|
||||
.nonsense-item {
|
||||
background: #f8fafd;
|
||||
background-color: rgba(255, 255, 255, 0.85);
|
||||
border-radius: 10px;
|
||||
padding: 18px 20px 14px 20px;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.03);
|
||||
|
||||
Reference in New Issue
Block a user