valaxy-theme-hairy 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,8 +2,8 @@
2
2
  import { capitalize, computed } from 'vue'
3
3
  import { useConfig, useThemeConfig } from 'valaxy'
4
4
  import { useI18n } from 'vue-i18n'
5
-
6
5
  import pkg from 'valaxy/package.json'
6
+ import HairyFooterFish from './HairyFooterFish.vue'
7
7
 
8
8
  const { t } = useI18n()
9
9
 
@@ -21,38 +21,39 @@ const footerIcon = computed(() => themeConfig.value.footer.icon)
21
21
  </script>
22
22
 
23
23
  <template>
24
- <footer v-if="themeConfig.footer" class="va-footer p-4 pt-5 pb-14" text="center sm" style="color:var(--va-c-text-light)">
25
- <div v-if="themeConfig.footer.beian?.enable && themeConfig.footer.beian.icp" class="beian" m="y-2">
26
- <a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">
27
- {{ themeConfig.footer.beian.icp }}
28
- </a>
29
- </div>
30
-
31
- <div class="copyright flex justify-center items-center" p="1">
32
- <span>
33
- &copy;
34
- <template v-if="!isThisYear">
35
- {{ themeConfig.footer.since }} -
36
- </template>
37
- {{ year }}
38
- </span>
39
-
40
- <a v-if="footerIcon" class="inline-flex animate-pulse ml-2" :href="footerIcon.url" target="_blank" :title="footerIcon.title">
41
- <div :class="footerIcon.name" />
42
- </a>
43
-
44
- <span>{{ config.author.name }}</span>
45
- <span class="mx-2">|</span>
46
- <span v-if="config.comment.waline" class="flex items-center">
47
- <div class="i-ri-eye-fill mr-1" />
48
- <span class="waline-pageview-count" data-path="/">1</span>
49
- </span>
50
- </div>
51
-
52
- <div v-if="themeConfig.footer.powered" class="powered" m="2">
53
- <span v-html="poweredHtml" /> | <span>{{ t('footer.theme') }} - <a :href="themeConfig.pkg.homepage" :title="`valaxy-theme-${config.theme}`" target="_blank">{{ capitalize(config.theme) }}</a> v{{ themeConfig.pkg.version }}</span>
24
+ <footer v-if="themeConfig.footer" class="va-footer pt-5" text="center sm" style="color:var(--va-c-text-light)">
25
+ <div class="z-5 relative">
26
+ <div v-if="themeConfig.footer.beian?.enable && themeConfig.footer.beian.icp" class="beian" m="y-2">
27
+ <a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">
28
+ {{ themeConfig.footer.beian.icp }}
29
+ </a>
30
+ </div>
31
+ <div class="copyright flex justify-center items-center" p="1">
32
+ <span>
33
+ &copy;
34
+ <template v-if="!isThisYear">
35
+ {{ themeConfig.footer.since }} -
36
+ </template>
37
+ {{ year }}
38
+ </span>
39
+
40
+ <a v-if="footerIcon" class="inline-flex animate-pulse ml-2" :href="footerIcon.url" target="_blank" :title="footerIcon.title">
41
+ <div :class="footerIcon.name" />
42
+ </a>
43
+
44
+ <span>{{ config.author.name }}</span>
45
+ <span class="mx-2">|</span>
46
+ <span v-if="config.comment.waline" class="flex items-center">
47
+ <div class="i-ri-eye-fill mr-1" />
48
+ <span class="waline-pageview-count" data-path="/">1</span>
49
+ </span>
50
+ </div>
51
+ <div v-if="themeConfig.footer.powered" class="powered" m="2">
52
+ <span v-html="poweredHtml" /> | <span>{{ t('footer.theme') }} - <a :href="themeConfig.pkg.homepage" :title="`valaxy-theme-${config.theme}`" target="_blank">{{ capitalize(config.theme) }}</a> v{{ themeConfig.pkg.version }}</span>
53
+ </div>
54
54
  </div>
55
55
 
56
56
  <slot />
57
+ <HairyFooterFish />
57
58
  </footer>
58
59
  </template>
@@ -0,0 +1,17 @@
1
+ <script lang="ts" setup>
2
+ import { ref } from 'vue'
3
+ import { useScriptTag } from '@vueuse/core'
4
+ import { RENDERER } from './fish'
5
+
6
+ const fishContainer = ref()
7
+
8
+ const tag = useScriptTag('https://cdn.bootcdn.net/ajax/libs/zepto/1.2.0/zepto.min.js')
9
+ tag.load()
10
+ .then(() => {
11
+ RENDERER.init()
12
+ })
13
+ </script>
14
+
15
+ <template>
16
+ <div id="jsi-flying-fish-container" ref="fishContainer" style="margin-top: -60px;"></div>
17
+ </template>
@@ -0,0 +1,315 @@
1
+ /* eslint-disable eqeqeq */
2
+ /* eslint-disable no-var */
3
+ /* eslint-disable vars-on-top */
4
+ /* eslint-disable @typescript-eslint/no-use-before-define */
5
+ /* eslint-disable no-undef */
6
+ const RENDERER = {
7
+ POINT_INTERVAL: 5,
8
+ FISH_COUNT: 3,
9
+ MAX_INTERVAL_COUNT: 50,
10
+ INIT_HEIGHT_RATE: 0.5,
11
+ THRESHOLD: 50,
12
+ init() {
13
+ this.setParameters()
14
+ this.reconstructMethods()
15
+ this.setup()
16
+ this.bindEvent()
17
+ this.render()
18
+ },
19
+ setParameters() {
20
+ this.$window = $(window)
21
+ this.$container = $('#jsi-flying-fish-container')
22
+ this.$canvas = $('<canvas />')
23
+ this.context = this.$canvas.appendTo(this.$container).get(0).getContext('2d')
24
+ this.points = []
25
+ this.fishes = []
26
+ this.watchIds = []
27
+ },
28
+ createSurfacePoints() {
29
+ const count = Math.round(this.width / this.POINT_INTERVAL)
30
+ this.pointInterval = this.width / (count - 1)
31
+ this.points.push(new SURFACE_POINT(this, 0))
32
+ for (let i = 1; i < count; i++) {
33
+ const point = new SURFACE_POINT(this, i * this.pointInterval)
34
+ const previous = this.points[i - 1]
35
+ point.setPreviousPoint(previous)
36
+ previous.setNextPoint(point)
37
+ this.points.push(point)
38
+ }
39
+ },
40
+ reconstructMethods() {
41
+ this.watchWindowSize = this.watchWindowSize.bind(this)
42
+ this.jdugeToStopResize = this.jdugeToStopResize.bind(this)
43
+ this.startEpicenter = this.startEpicenter.bind(this)
44
+ this.moveEpicenter = this.moveEpicenter.bind(this)
45
+ this.reverseVertical = this.reverseVertical.bind(this)
46
+ this.render = this.render.bind(this)
47
+ },
48
+ setup() {
49
+ this.points.length = 0
50
+ this.fishes.length = 0
51
+ this.watchIds.length = 0
52
+ this.intervalCount = this.MAX_INTERVAL_COUNT
53
+ this.width = this.$container.width()
54
+ this.height = this.$container.height()
55
+ this.fishCount = (((this.FISH_COUNT * this.width) / 500) * this.height) / 500
56
+ this.$canvas.attr({ width: this.width, height: this.height })
57
+ this.reverse = false
58
+ this.fishes.push(new FISH(this))
59
+ this.createSurfacePoints()
60
+ },
61
+ watchWindowSize() {
62
+ this.clearTimer()
63
+ this.tmpWidth = this.$window.width()
64
+ this.tmpHeight = this.$window.height()
65
+ this.watchIds.push(setTimeout(this.jdugeToStopResize, this.WATCH_INTERVAL))
66
+ },
67
+ clearTimer() {
68
+ while (this.watchIds.length > 0)
69
+ clearTimeout(this.watchIds.pop())
70
+ },
71
+ jdugeToStopResize() {
72
+ const width = this.$window.width()
73
+ const height = this.$window.height()
74
+ const stopped = width == this.tmpWidth && height == this.tmpHeight
75
+ this.tmpWidth = width
76
+ this.tmpHeight = height
77
+ if (stopped)
78
+ this.setup()
79
+ },
80
+ bindEvent() {
81
+ this.$window.on('resize', this.watchWindowSize)
82
+ this.$container.on('mouseenter', this.startEpicenter)
83
+ this.$container.on('mousemove', this.moveEpicenter)
84
+ },
85
+ getAxis(event) {
86
+ const offset = this.$container.offset()
87
+ return { x: event.clientX - offset.left + this.$window.scrollLeft(), y: event.clientY - offset.top + this.$window.scrollTop() }
88
+ },
89
+ startEpicenter(event) {
90
+ this.axis = this.getAxis(event)
91
+ },
92
+ moveEpicenter(event) {
93
+ const axis = this.getAxis(event)
94
+ if (!this.axis)
95
+ this.axis = axis
96
+
97
+ this.generateEpicenter(axis.x, axis.y, axis.y - this.axis.y)
98
+ this.axis = axis
99
+ },
100
+ generateEpicenter(x, y, velocity) {
101
+ if (y < this.height / 2 - this.THRESHOLD || y > this.height / 2 + this.THRESHOLD)
102
+ return
103
+
104
+ const index = Math.round(x / this.pointInterval)
105
+ if (index < 0 || index >= this.points.length)
106
+ return
107
+
108
+ this.points[index].interfere(y, velocity)
109
+ },
110
+ reverseVertical() {
111
+ this.reverse = !this.reverse
112
+ for (let i = 0, count = this.fishes.length; i < count; i++)
113
+ this.fishes[i].reverseVertical()
114
+ },
115
+ controlStatus() {
116
+ for (let i = 0, count = this.points.length; i < count; i++)
117
+ this.points[i].updateSelf()
118
+
119
+ for (let i = 0, count = this.points.length; i < count; i++)
120
+ this.points[i].updateNeighbors()
121
+
122
+ if (this.fishes.length < this.fishCount && --this.intervalCount == 0) {
123
+ this.intervalCount = this.MAX_INTERVAL_COUNT
124
+ this.fishes.push(new FISH(this))
125
+ }
126
+ },
127
+ render() {
128
+ requestAnimationFrame(this.render)
129
+ this.controlStatus()
130
+ this.context.clearRect(0, 0, this.width, this.height)
131
+ this.context.fillStyle = 'hsl(0, 0%, 95%)'
132
+ for (let i = 0, count = this.fishes.length; i < count; i++)
133
+ this.fishes[i].render(this.context)
134
+
135
+ this.context.save()
136
+ this.context.globalCompositeOperation = 'xor'
137
+ this.context.beginPath()
138
+ this.context.moveTo(0, this.reverse ? 0 : this.height)
139
+ for (let i = 0, count = this.points.length; i < count; i++)
140
+ this.points[i].render(this.context)
141
+
142
+ this.context.lineTo(this.width, this.reverse ? 0 : this.height)
143
+ this.context.closePath()
144
+ this.context.fill()
145
+ this.context.restore()
146
+ },
147
+ }
148
+
149
+ var SURFACE_POINT = function (renderer, x) {
150
+ this.renderer = renderer
151
+ this.x = x
152
+ this.init()
153
+ }
154
+
155
+ SURFACE_POINT.prototype = {
156
+ SPRING_CONSTANT: 0.03,
157
+ SPRING_FRICTION: 0.9,
158
+ WAVE_SPREAD: 0.3,
159
+ ACCELARATION_RATE: 0.01,
160
+ init() {
161
+ this.initHeight = this.renderer.height * this.renderer.INIT_HEIGHT_RATE
162
+ this.height = this.initHeight
163
+ this.fy = 0
164
+ this.force = { previous: 0, next: 0 }
165
+ },
166
+ setPreviousPoint(previous) {
167
+ this.previous = previous
168
+ },
169
+ setNextPoint(next) {
170
+ this.next = next
171
+ },
172
+ interfere(y, velocity) {
173
+ this.fy = this.renderer.height * this.ACCELARATION_RATE * (this.renderer.height - this.height - y >= 0 ? -1 : 1) * Math.abs(velocity)
174
+ },
175
+ updateSelf() {
176
+ this.fy += this.SPRING_CONSTANT * (this.initHeight - this.height)
177
+ this.fy *= this.SPRING_FRICTION
178
+ this.height += this.fy
179
+ },
180
+ updateNeighbors() {
181
+ if (this.previous)
182
+ this.force.previous = this.WAVE_SPREAD * (this.height - this.previous.height)
183
+
184
+ if (this.next)
185
+ this.force.next = this.WAVE_SPREAD * (this.height - this.next.height)
186
+ },
187
+ render(context) {
188
+ if (this.previous) {
189
+ this.previous.height += this.force.previous
190
+ this.previous.fy += this.force.previous
191
+ }
192
+ if (this.next) {
193
+ this.next.height += this.force.next
194
+ this.next.fy += this.force.next
195
+ }
196
+ context.lineTo(this.x, this.renderer.height - this.height)
197
+ },
198
+ }
199
+
200
+ var FISH = function (renderer) {
201
+ this.renderer = renderer
202
+ this.init()
203
+ }
204
+
205
+ FISH.prototype = {
206
+ GRAVITY: 0.4,
207
+ init() {
208
+ this.direction = Math.random() < 0.5
209
+ this.x = this.direction ? this.renderer.width + this.renderer.THRESHOLD : -this.renderer.THRESHOLD
210
+ this.previousY = this.y
211
+ this.vx = this.getRandomValue(4, 10) * (this.direction ? -1 : 1)
212
+ if (this.renderer.reverse) {
213
+ this.y = this.getRandomValue((this.renderer.height * 1) / 10, (this.renderer.height * 4) / 10)
214
+ this.vy = this.getRandomValue(2, 5)
215
+ this.ay = this.getRandomValue(0.05, 0.2)
216
+ }
217
+ else {
218
+ this.y = this.getRandomValue((this.renderer.height * 6) / 10, (this.renderer.height * 9) / 10)
219
+ this.vy = this.getRandomValue(-5, -2)
220
+ this.ay = this.getRandomValue(-0.2, -0.05)
221
+ }
222
+ this.isOut = false
223
+ this.theta = 0
224
+ this.phi = 0
225
+ },
226
+ getRandomValue(min, max) {
227
+ return min + (max - min) * Math.random()
228
+ },
229
+ reverseVertical() {
230
+ this.isOut = !this.isOut
231
+ this.ay *= -1
232
+ },
233
+ controlStatus() {
234
+ this.previousY = this.y
235
+ this.x += this.vx
236
+ this.y += this.vy
237
+ this.vy += this.ay
238
+ if (this.renderer.reverse) {
239
+ if (this.y > this.renderer.height * this.renderer.INIT_HEIGHT_RATE) {
240
+ this.vy -= this.GRAVITY
241
+ this.isOut = true
242
+ }
243
+ else {
244
+ if (this.isOut)
245
+ this.ay = this.getRandomValue(0.05, 0.2)
246
+
247
+ this.isOut = false
248
+ }
249
+ }
250
+ else {
251
+ if (this.y < this.renderer.height * this.renderer.INIT_HEIGHT_RATE) {
252
+ this.vy += this.GRAVITY
253
+ this.isOut = true
254
+ }
255
+ else {
256
+ if (this.isOut)
257
+ this.ay = this.getRandomValue(-0.2, -0.05)
258
+
259
+ this.isOut = false
260
+ }
261
+ }
262
+ if (!this.isOut) {
263
+ this.theta += Math.PI / 20
264
+ this.theta %= Math.PI * 2
265
+ this.phi += Math.PI / 30
266
+ this.phi %= Math.PI * 2
267
+ }
268
+ this.renderer.generateEpicenter(this.x + (this.direction ? -1 : 1) * this.renderer.THRESHOLD, this.y, this.y - this.previousY)
269
+ if ((this.vx > 0 && this.x > this.renderer.width + this.renderer.THRESHOLD) || (this.vx < 0 && this.x < -this.renderer.THRESHOLD))
270
+ this.init()
271
+ },
272
+ render(context) {
273
+ context.save()
274
+ context.translate(this.x, this.y)
275
+ context.rotate(Math.PI + Math.atan2(this.vy, this.vx))
276
+ context.scale(1, this.direction ? 1 : -1)
277
+ context.beginPath()
278
+ context.moveTo(-30, 0)
279
+ context.bezierCurveTo(-20, 15, 15, 10, 40, 0)
280
+ context.bezierCurveTo(15, -10, -20, -15, -30, 0)
281
+ context.fill()
282
+ context.save()
283
+ context.translate(40, 0)
284
+ context.scale(0.9 + 0.2 * Math.sin(this.theta), 1)
285
+ context.beginPath()
286
+ context.moveTo(0, 0)
287
+ context.quadraticCurveTo(5, 10, 20, 8)
288
+ context.quadraticCurveTo(12, 5, 10, 0)
289
+ context.quadraticCurveTo(12, -5, 20, -8)
290
+ context.quadraticCurveTo(5, -10, 0, 0)
291
+ context.fill()
292
+ context.restore()
293
+ context.save()
294
+ context.translate(-3, 0)
295
+ context.rotate((Math.PI / 3 + (Math.PI / 10) * Math.sin(this.phi)) * (this.renderer.reverse ? -1 : 1))
296
+ context.beginPath()
297
+ if (this.renderer.reverse) {
298
+ context.moveTo(5, 0)
299
+ context.bezierCurveTo(10, 10, 10, 30, 0, 40)
300
+ context.bezierCurveTo(-12, 25, -8, 10, 0, 0)
301
+ }
302
+ else {
303
+ context.moveTo(-5, 0)
304
+ context.bezierCurveTo(-10, -10, -10, -30, 0, -40)
305
+ context.bezierCurveTo(12, -25, 8, -10, 0, 0)
306
+ }
307
+ context.closePath()
308
+ context.fill()
309
+ context.restore()
310
+ context.restore()
311
+ this.controlStatus(context)
312
+ },
313
+ }
314
+
315
+ export { RENDERER }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "valaxy-theme-hairy",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "packageManager": "pnpm@7.5.0",
5
5
  "author": {
6
6
  "email": "wwu710632@gmail.com",