Skip to content

Commit 76365c6

Browse files
author
李磊
committed
fix: 年龄计算
1 parent 7527267 commit 76365c6

File tree

3 files changed

+243
-0
lines changed

3 files changed

+243
-0
lines changed

apps/docs/.vitepress/config/zh.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const zh = defineConfig({
1111
{ text: '首页', link: '/' },
1212
{ text: '个人简历', link: '/resume' },
1313
{ text: '博客', link: '/blog/iframe' },
14+
{ text: '年龄计算', link: '/age' },
1415
{
1516
text: '更多',
1617
items: [

apps/docs/examples/age.vue

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
<template>
2+
<div class="app">
3+
<!-- 粒子背景 -->
4+
<canvas ref="canvas" class="background" />
5+
6+
<!-- 主内容 -->
7+
<div class="container">
8+
<h1 class="title">年龄计算器</h1>
9+
<div class="form">
10+
<label>出生日期:</label>
11+
<input v-model="birth" type="date" />
12+
13+
<label>目标日期(可选):</label>
14+
<input v-model="target" type="date" />
15+
16+
<button @click="calcAge">计算</button>
17+
</div>
18+
19+
<div v-if="result" class="result">
20+
<p>年龄: {{ result.years }} 年 {{ result.months }} 月 {{ result.days }} 天</p>
21+
<p>一共: {{ result.totalDays }} 天</p>
22+
<p v-if="result.leftDays !== null">距离目标日: {{ result.leftDays }} 天</p>
23+
</div>
24+
</div>
25+
</div>
26+
</template>
27+
28+
<script setup>
29+
import { onMounted, reactive, ref } from 'vue';
30+
31+
const birth = ref('2025-07-06');
32+
const target = ref('');
33+
const result = reactive({ years: 0, months: 0, days: 0, totalDays: 0, leftDays: null });
34+
35+
function calcAge() {
36+
const birthDate = new Date(birth.value);
37+
const today = target.value ? new Date(target.value) : new Date();
38+
39+
let years = today.getFullYear() - birthDate.getFullYear();
40+
let months = today.getMonth() - birthDate.getMonth();
41+
let days = today.getDate() - birthDate.getDate();
42+
43+
if (days < 0) {
44+
months--;
45+
const prevMonth = new Date(today.getFullYear(), today.getMonth(), 0);
46+
days += prevMonth.getDate();
47+
}
48+
if (months < 0) {
49+
years--;
50+
months += 12;
51+
}
52+
53+
const diffTime = today - birthDate;
54+
const totalDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
55+
56+
result.years = years;
57+
result.months = months;
58+
result.days = days;
59+
result.totalDays = totalDays;
60+
61+
if (target.value) {
62+
const now = new Date();
63+
result.leftDays = Math.floor((new Date(target.value) - now) / (1000 * 60 * 60 * 24));
64+
} else {
65+
result.leftDays = null;
66+
}
67+
68+
target.value = '';
69+
}
70+
71+
// 粒子背景
72+
const canvas = ref(null);
73+
let ctx;
74+
const particles = [];
75+
const mouse = { x: null, y: null, radius: 120 }; // 鼠标交互半径
76+
77+
class Particle {
78+
constructor(x, y) {
79+
this.x = x;
80+
this.y = y;
81+
this.vx = (Math.random() - 0.5) * 1;
82+
this.vy = (Math.random() - 0.5) * 1;
83+
this.size = 2;
84+
}
85+
86+
draw() {
87+
ctx.beginPath();
88+
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
89+
ctx.fillStyle = 'rgba(255,255,255,0.8)';
90+
ctx.fill();
91+
}
92+
93+
update() {
94+
this.x += this.vx;
95+
this.y += this.vy;
96+
97+
// 边界反弹
98+
if (this.x < 0 || this.x > canvas.value.width) this.vx *= -1;
99+
if (this.y < 0 || this.y > canvas.value.height) this.vy *= -1;
100+
101+
// 鼠标交互
102+
const dx = this.x - mouse.x;
103+
const dy = this.y - mouse.y;
104+
const dist = Math.sqrt(dx * dx + dy * dy);
105+
if (dist < mouse.radius) {
106+
// 吸引/排斥(可调节)
107+
const force = (mouse.radius - dist) / mouse.radius;
108+
const angle = Math.atan2(dy, dx);
109+
this.x += Math.cos(angle) * force * 3; // 排斥
110+
this.y += Math.sin(angle) * force * 3;
111+
}
112+
113+
this.draw();
114+
}
115+
}
116+
117+
function connectParticles() {
118+
for (let i = 0; i < particles.length; i++) {
119+
for (let j = i; j < particles.length; j++) {
120+
const dx = particles[i].x - particles[j].x;
121+
const dy = particles[i].y - particles[j].y;
122+
const dist = dx * dx + dy * dy;
123+
if (dist < 120 * 120) {
124+
ctx.beginPath();
125+
ctx.strokeStyle = 'rgba(255,255,255,0.1)';
126+
ctx.lineWidth = 1;
127+
ctx.moveTo(particles[i].x, particles[i].y);
128+
ctx.lineTo(particles[j].x, particles[j].y);
129+
ctx.stroke();
130+
}
131+
}
132+
}
133+
}
134+
135+
function animate() {
136+
ctx.clearRect(0, 0, canvas.value.width, canvas.value.height);
137+
particles.forEach((p) => p.update());
138+
connectParticles();
139+
requestAnimationFrame(animate);
140+
}
141+
142+
onMounted(() => {
143+
canvas.value.width = window.innerWidth;
144+
canvas.value.height = window.innerHeight;
145+
ctx = canvas.value.getContext('2d');
146+
147+
// 初始化粒子
148+
const count = Math.floor((canvas.value.width * canvas.value.height) / 15000);
149+
for (let i = 0; i < count; i++) {
150+
const x = Math.random() * canvas.value.width;
151+
const y = Math.random() * canvas.value.height;
152+
particles.push(new Particle(x, y));
153+
}
154+
155+
// 鼠标监听
156+
window.addEventListener('mousemove', (e) => {
157+
mouse.x = e.clientX;
158+
mouse.y = e.clientY;
159+
});
160+
window.addEventListener('mouseout', () => {
161+
mouse.x = null;
162+
mouse.y = null;
163+
});
164+
165+
animate();
166+
window.addEventListener('resize', () => {
167+
canvas.value.width = window.innerWidth;
168+
canvas.value.height = window.innerHeight;
169+
});
170+
171+
calcAge(); // 初始计算
172+
});
173+
</script>
174+
175+
<style scoped>
176+
.app {
177+
position: relative;
178+
width: 100%;
179+
height: 100vh;
180+
overflow: hidden;
181+
}
182+
183+
.background {
184+
position: absolute;
185+
top: 0;
186+
left: 0;
187+
z-index: 0;
188+
display: block;
189+
width: 100%;
190+
height: 100%;
191+
background: black;
192+
}
193+
194+
.container {
195+
position: relative;
196+
z-index: 1;
197+
padding: 20px;
198+
color: white;
199+
text-align: center;
200+
}
201+
202+
.title {
203+
margin-bottom: 20px;
204+
font-size: 2em;
205+
text-shadow: 0 0 15px #0ff;
206+
}
207+
208+
.form {
209+
margin-bottom: 20px;
210+
}
211+
212+
input,
213+
button {
214+
padding: 8px 12px;
215+
margin: 5px;
216+
border: none;
217+
border-radius: 6px;
218+
}
219+
220+
button {
221+
color: white;
222+
cursor: pointer;
223+
background: linear-gradient(45deg, #00f, #0ff);
224+
transition: 0.3s;
225+
}
226+
227+
button:hover {
228+
box-shadow: 0 0 10px #0ff;
229+
transform: scale(1.1);
230+
}
231+
232+
.result {
233+
margin-top: 15px;
234+
font-size: 1.2em;
235+
text-shadow: 0 0 10px #fff;
236+
}
237+
</style>

apps/docs/zh/age.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# 年龄计算
2+
3+
::: raw
4+
<demo class="vp-raw" vue="age.vue" />
5+
:::

0 commit comments

Comments
 (0)