valaxy-theme-yun 0.14.16 → 0.14.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/App.vue CHANGED
@@ -17,6 +17,7 @@ const themeConfig = useThemeConfig()
17
17
  </script>
18
18
 
19
19
  <template>
20
+ <YunFireworks v-if="themeConfig.fireworks.enable" />
20
21
  <slot name="bg">
21
22
  <YunBg v-if="themeConfig.bg_image.enable" />
22
23
  </slot>
@@ -0,0 +1,28 @@
1
+ <script lang="ts" setup>
2
+ import { useThemeConfig } from 'valaxy/client'
3
+ import { onMounted } from 'vue'
4
+ import { createFireworks } from '../features/fireworks'
5
+
6
+ const themeConfig = useThemeConfig()
7
+
8
+ onMounted(() => {
9
+ createFireworks({
10
+ selector: 'canvas.fireworks',
11
+ colors: themeConfig.value.fireworks.colors,
12
+ })
13
+ })
14
+ </script>
15
+
16
+ <template>
17
+ <canvas class="fireworks" />
18
+ </template>
19
+
20
+ <style>
21
+ canvas.fireworks {
22
+ position: fixed;
23
+ left: 0;
24
+ top: 0;
25
+ z-index: 1;
26
+ pointer-events: none;
27
+ }
28
+ </style>
@@ -1,6 +1,6 @@
1
1
  <script lang="ts" setup>
2
2
  import type { Post } from 'valaxy'
3
- import { formatDate } from 'valaxy'
3
+ import { formatDate, useSiteConfig } from 'valaxy'
4
4
  import { useI18n } from 'vue-i18n'
5
5
 
6
6
  defineProps<{
@@ -9,6 +9,8 @@ defineProps<{
9
9
  }>()
10
10
 
11
11
  const { t } = useI18n()
12
+
13
+ const siteConfig = useSiteConfig()
12
14
  </script>
13
15
 
14
16
  <template>
@@ -23,7 +25,10 @@ const { t } = useI18n()
23
25
  <div i-ri-pushpin-line />
24
26
  </div>
25
27
 
26
- <div v-if="frontmatter" class="post-meta justify-center" flex="~" text="sm" py="1">
28
+ <div
29
+ v-if="frontmatter" class="post-meta"
30
+ flex="~ col" justify="center" items="center" text="sm" py="1"
31
+ >
27
32
  <div v-if="frontmatter.date" class="post-time flex items-center">
28
33
  <span class="inline-flex-center" :title="t('post.posted')">
29
34
  <div class="inline-block" i-ri-calendar-line />
@@ -39,6 +44,28 @@ const { t } = useI18n()
39
44
  <time m="l-1">{{ formatDate(frontmatter.updated) }}</time>
40
45
  </span>
41
46
  </div>
47
+
48
+ <div
49
+ v-if="siteConfig.statistics.enable"
50
+ class="post-counter flex items-center" mt="2"
51
+ >
52
+ <span
53
+ v-if="frontmatter.wordCount"
54
+ class="inline-flex-center" :title="t('statistics.word')"
55
+ >
56
+ <div class="inline-block" i-ri-file-word-line />
57
+ <time m="l-1">{{ frontmatter.wordCount }}</time>
58
+ </span>
59
+
60
+ <span
61
+ v-if="frontmatter.readingTime"
62
+ class="inline-flex-center" :title="t('statistics.time')"
63
+ >
64
+ <span m="x-2">-</span>
65
+ <div i-ri-timer-line />
66
+ <time m="l-1">{{ frontmatter.readingTime }}m</time>
67
+ </span>
68
+ </div>
42
69
  </div>
43
70
 
44
71
  <slot />
@@ -0,0 +1,246 @@
1
+ /**
2
+ * @see https://codepen.io/juliangarnier/pen/gmOwJX
3
+ * inherited from hexo-theme-yun
4
+ * customized by valaxy-theme-yun
5
+ * @author YunYouJun
6
+ */
7
+
8
+ import anime from 'animejs/lib/anime.es.js'
9
+ import { TinyColor } from '@ctrl/tinycolor'
10
+
11
+ interface MinMax {
12
+ min: number
13
+ max: number
14
+ }
15
+
16
+ interface FireworksConfig {
17
+ selector: string
18
+ colors: string[]
19
+ numberOfParticles: number
20
+ orbitRadius: MinMax
21
+ circleRadius: MinMax
22
+ diffuseRadius: MinMax
23
+ animeDuration: MinMax
24
+ }
25
+
26
+ export interface Point {
27
+ x: number
28
+ y: number
29
+ }
30
+
31
+ export interface Particle extends Point {
32
+ color: string
33
+ radius: number
34
+ endPos: Point
35
+ draw(): void
36
+ }
37
+
38
+ /**
39
+ * 创建烟花
40
+ * @param config
41
+ */
42
+ export function createFireworks(config: Partial<FireworksConfig>) {
43
+ const defaultColors = ['#66A7DD', '#3E83E1', '#214EC2']
44
+
45
+ const {
46
+ colors = defaultColors,
47
+ selector = 'canvas.fireworks',
48
+ // sky blue
49
+ numberOfParticles = 20,
50
+ circleRadius = {
51
+ min: 10,
52
+ max: 20,
53
+ },
54
+ diffuseRadius = {
55
+ min: 50,
56
+ max: 100,
57
+ },
58
+ orbitRadius = {
59
+ min: 50,
60
+ max: 100,
61
+ },
62
+ animeDuration = {
63
+ min: 900,
64
+ max: 1500,
65
+ },
66
+ } = config
67
+
68
+ let pointerX = 0
69
+ let pointerY = 0
70
+
71
+ const canvasEl = document.querySelector(selector) as HTMLCanvasElement
72
+ const ctx = canvasEl.getContext('2d')
73
+
74
+ if (!ctx)
75
+ return
76
+
77
+ /**
78
+ * 设置画布尺寸
79
+ */
80
+ function setCanvasSize(canvasEl: HTMLCanvasElement) {
81
+ canvasEl.width = window.innerWidth
82
+ canvasEl.height = window.innerHeight
83
+ canvasEl.style.width = `${window.innerWidth}px`
84
+ canvasEl.style.height = `${window.innerHeight}px`
85
+ }
86
+
87
+ /**
88
+ * update pointer
89
+ * @param e
90
+ */
91
+ function updateCoords(e: MouseEvent | TouchEvent) {
92
+ pointerX
93
+ = 'clientX' in e
94
+ ? e.clientX
95
+ : (e.touches[0] ? e.touches[0].clientX : e.changedTouches[0].clientX)
96
+ pointerY
97
+ = 'clientY' in e
98
+ ? e.clientY
99
+ : (e.touches[0] ? e.touches[0].clientY : e.changedTouches[0].clientY)
100
+ }
101
+
102
+ function setParticleDirection(p: Point) {
103
+ const angle = (anime.random(0, 360) * Math.PI) / 180
104
+ const value = anime.random(
105
+ diffuseRadius.min,
106
+ diffuseRadius.max,
107
+ )
108
+ const radius = [-1, 1][anime.random(0, 1)] * value
109
+ return {
110
+ x: p.x + radius * Math.cos(angle),
111
+ y: p.y + radius * Math.sin(angle),
112
+ }
113
+ }
114
+
115
+ /**
116
+ * 在指定位置创建粒子
117
+ * @param {number} x
118
+ * @param {number} y
119
+ * @returns
120
+ */
121
+ function createParticle(x: number, y: number) {
122
+ const tinyColor = new TinyColor(colors[anime.random(0, colors.length - 1)])
123
+ tinyColor.setAlpha(anime.random(0.2, 0.8))
124
+
125
+ const p: Particle = {
126
+ x,
127
+ y,
128
+ color: tinyColor.toRgbString(),
129
+ radius: anime.random(circleRadius.min, circleRadius.max),
130
+ endPos: setParticleDirection({ x, y }),
131
+ draw: () => {},
132
+ }
133
+
134
+ p.draw = function () {
135
+ if (!ctx)
136
+ return
137
+
138
+ ctx.beginPath()
139
+ ctx.arc(p.x, p.y, p.radius, 0, 2 * Math.PI, true)
140
+ ctx.fillStyle = p.color
141
+ ctx.fill()
142
+ }
143
+ return p
144
+ }
145
+
146
+ function createCircle(x: number, y: number) {
147
+ const p = {
148
+ x,
149
+ y,
150
+ color: '#000',
151
+ radius: 0.1,
152
+ alpha: 0.5,
153
+ lineWidth: 6,
154
+ draw() {},
155
+ }
156
+
157
+ p.draw = () => {
158
+ if (!ctx)
159
+ return
160
+
161
+ ctx.globalAlpha = p.alpha
162
+ ctx.beginPath()
163
+ ctx.arc(p.x, p.y, p.radius, 0, 2 * Math.PI, true)
164
+ ctx.lineWidth = p.lineWidth
165
+ ctx.strokeStyle = p.color
166
+ ctx.stroke()
167
+ ctx.globalAlpha = 1
168
+ }
169
+ return p
170
+ }
171
+
172
+ function renderParticle(anim: anime.AnimeInstance) {
173
+ for (let i = 0; i < anim.animatables.length; i++) {
174
+ const target = anim.animatables[i].target as any as Particle
175
+ target.draw()
176
+ }
177
+ }
178
+
179
+ function animateParticles(x: number, y: number) {
180
+ const circle = createCircle(x, y)
181
+ const particles = []
182
+ for (let i = 0; i < numberOfParticles; i++)
183
+ particles.push(createParticle(x, y))
184
+
185
+ anime
186
+ .timeline()
187
+ .add({
188
+ targets: particles,
189
+ x(p: Particle) {
190
+ return p.endPos.x
191
+ },
192
+ y(p: Particle) {
193
+ return p.endPos.y
194
+ },
195
+ radius: 0.1,
196
+ duration: anime.random(
197
+ animeDuration.min,
198
+ animeDuration.max,
199
+ ),
200
+ easing: 'easeOutExpo',
201
+ update: renderParticle,
202
+ })
203
+ .add(
204
+ {
205
+ targets: circle,
206
+ radius: anime.random(orbitRadius.min, orbitRadius.max),
207
+ lineWidth: 0,
208
+ alpha: {
209
+ value: 0,
210
+ easing: 'linear',
211
+ duration: anime.random(600, 800),
212
+ },
213
+ duration: anime.random(1200, 1800),
214
+ easing: 'easeOutExpo',
215
+ update: renderParticle,
216
+ },
217
+ 0,
218
+ )
219
+ }
220
+
221
+ const render = anime({
222
+ duration: Infinity,
223
+ update: () => {
224
+ ctx.clearRect(0, 0, canvasEl.width, canvasEl.height)
225
+ },
226
+ })
227
+
228
+ document.addEventListener(
229
+ 'mousedown',
230
+ (e) => {
231
+ render.play()
232
+ updateCoords(e)
233
+ animateParticles(pointerX, pointerY)
234
+ },
235
+ false,
236
+ )
237
+
238
+ setCanvasSize(canvasEl)
239
+ window.addEventListener(
240
+ 'resize',
241
+ () => {
242
+ setCanvasSize(canvasEl)
243
+ },
244
+ false,
245
+ )
246
+ }
@@ -1,4 +1,5 @@
1
1
  <script lang="ts" setup>
2
+ import { defineWebPage, useSchemaOrg } from '@vueuse/schema-org'
2
3
  import { useFrontmatter, usePostTitle, useSiteStore } from 'valaxy'
3
4
  import { useI18n } from 'vue-i18n'
4
5
 
@@ -8,6 +9,12 @@ const frontmatter = useFrontmatter()
8
9
 
9
10
  const title = usePostTitle(frontmatter)
10
11
  const site = useSiteStore()
12
+
13
+ useSchemaOrg([
14
+ defineWebPage({
15
+ '@type': 'CollectionPage',
16
+ }),
17
+ ])
11
18
  </script>
12
19
 
13
20
  <template>
@@ -3,6 +3,7 @@ import { computed } from 'vue'
3
3
  import { useCategory, useFrontmatter, usePostTitle, useSiteStore } from 'valaxy'
4
4
  import { useI18n } from 'vue-i18n'
5
5
  import { useRoute } from 'vue-router'
6
+ import { defineWebPage, useSchemaOrg } from '@vueuse/schema-org'
6
7
 
7
8
  const { t } = useI18n()
8
9
 
@@ -29,6 +30,12 @@ const posts = computed(() => {
29
30
  })
30
31
 
31
32
  const title = usePostTitle(frontmatter)
33
+
34
+ useSchemaOrg([
35
+ defineWebPage({
36
+ '@type': 'CollectionPage',
37
+ }),
38
+ ])
32
39
  </script>
33
40
 
34
41
  <template>
package/layouts/post.vue CHANGED
@@ -2,6 +2,8 @@
2
2
  import { computed } from 'vue'
3
3
  import { useFrontmatter, useFullUrl, useSiteConfig } from 'valaxy'
4
4
 
5
+ import { defineArticle, useSchemaOrg } from '@vueuse/schema-org'
6
+
5
7
  const siteConfig = useSiteConfig()
6
8
  const frontmatter = useFrontmatter()
7
9
  const url = useFullUrl()
@@ -12,6 +14,20 @@ const showSponsor = computed(() => {
12
14
 
13
15
  return siteConfig.value.sponsor.enable
14
16
  })
17
+
18
+ useSchemaOrg(
19
+ defineArticle({
20
+ '@type': 'BlogPosting',
21
+ 'headline': frontmatter.value.title,
22
+ 'description': frontmatter.value.description,
23
+ 'author': [
24
+ {
25
+ name: siteConfig.value.author.name,
26
+ url: siteConfig.value.author.link,
27
+ },
28
+ ],
29
+ }),
30
+ )
15
31
  </script>
16
32
 
17
33
  <template>
package/layouts/tags.vue CHANGED
@@ -3,8 +3,15 @@ import { useFrontmatter, useInvisibleElement, usePostTitle, useSiteStore, useTag
3
3
  import { useI18n } from 'vue-i18n'
4
4
  import { computed, ref } from 'vue'
5
5
  import { useRoute, useRouter } from 'vue-router'
6
+ import { defineWebPage, useSchemaOrg } from '@vueuse/schema-org'
6
7
  import { useThemeConfig } from '../composables'
7
8
 
9
+ useSchemaOrg([
10
+ defineWebPage({
11
+ '@type': 'CollectionPage',
12
+ }),
13
+ ])
14
+
8
15
  const route = useRoute()
9
16
  const router = useRouter()
10
17
 
package/node/config.ts CHANGED
@@ -31,6 +31,11 @@ export const defaultThemeConfig: ThemeConfig = {
31
31
  },
32
32
  },
33
33
 
34
+ fireworks: {
35
+ enable: true,
36
+ colors: ['#66A7DD', '#3E83E1', '#214EC2'],
37
+ },
38
+
34
39
  notice: {
35
40
  enable: false,
36
41
  content: '',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "valaxy-theme-yun",
3
- "version": "0.14.16",
3
+ "version": "0.14.18",
4
4
  "author": {
5
5
  "email": "me@yunyoujun.cn",
6
6
  "name": "YunYouJun",
@@ -18,10 +18,12 @@
18
18
  "types": "types/index.d.ts",
19
19
  "dependencies": {
20
20
  "@iconify-json/ant-design": "^1.1.5",
21
- "@iconify-json/simple-icons": "^1.1.45",
21
+ "@iconify-json/simple-icons": "^1.1.47",
22
+ "animejs": "^3.2.1",
22
23
  "valaxy-addon-waline": "0.1.0"
23
24
  },
24
25
  "devDependencies": {
25
- "valaxy": "0.14.16"
26
+ "@types/animejs": "^3.1.7",
27
+ "valaxy": "0.14.18"
26
28
  }
27
29
  }
package/types/index.d.ts CHANGED
@@ -109,6 +109,20 @@ export interface ThemeConfig {
109
109
  content: string
110
110
  }
111
111
 
112
+ /**
113
+ * @en - Fireworks when click
114
+ * @zh - 点击时的烟花效果
115
+ */
116
+ fireworks: {
117
+ enable: boolean
118
+ /**
119
+ * @en - Fireworks colors
120
+ * @zh - 烟花颜色
121
+ * @default ['#66A7DD', '#3E83E1', '#214EC2']
122
+ */
123
+ colors?: string[]
124
+ }
125
+
112
126
  /**
113
127
  * @en - Pages
114
128
  * @zh - 页面,显示在社交导航栏下方