diff --git a/pages/index/index.vue b/pages/index/index.vue
new file mode 100644
index 0000000..ece85f2
--- /dev/null
+++ b/pages/index/index.vue
@@ -0,0 +1,2074 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/index/utils.js b/pages/index/utils.js
new file mode 100644
index 0000000..2715f00
--- /dev/null
+++ b/pages/index/utils.js
@@ -0,0 +1,79 @@
+import { ref, onMounted, onUnmounted } from 'vue'
+
+export function useElementSize(targetSelector) {
+ const width = ref(0)
+ const height = ref(0)
+ let observer = null
+
+ const updateSize = () => {
+ const query = uni.createSelectorQuery()
+ query.select(targetSelector).boundingClientRect(rect => {
+ if (rect) {
+ width.value = rect.width
+ height.value = rect.height
+ }
+ }).exec()
+ }
+
+ onMounted(() => {
+ updateSize()
+ // 尝试使用 ResizeObserver(部分小程序基础库支持)
+ if (uni.createIntersectionObserver) {
+ observer = uni.createIntersectionObserver(this, {
+ observeAll: true
+ })
+ observer.relativeToViewport().observe(targetSelector, updateSize)
+ }
+ })
+
+ onUnmounted(() => {
+ observer?.disconnect()
+ })
+
+ return { width, height, updateSize }
+}
+
+export function useEventListener(target, event, handler) {
+ // 组件内事件
+ if (target.$on) {
+ target.$on(event, handler)
+ const stop = () => target.$off(event, handler)
+ onUnmounted(stop)
+ return stop
+ }
+ // 全局事件(需配合 uni.$emit 使用)
+ else {
+ uni.$on(event, handler)
+ const stop = () => uni.$off(event, handler)
+ onUnmounted(stop)
+ return stop
+ }
+}
+
+
+export function useRafFn(fn, { immediate = true } = {}) {
+ let isActive = false
+ let timerId = null
+
+ const loop = () => {
+ if (!isActive) return
+ fn()
+ timerId = setTimeout(loop, 16) // 模拟 60fps
+ }
+
+ const start = () => {
+ if (isActive) return
+ isActive = true
+ loop()
+ }
+
+ const stop = () => {
+ isActive = false
+ clearTimeout(timerId)
+ }
+
+ onUnmounted(stop)
+ immediate && start()
+
+ return { start, stop }
+}
\ No newline at end of file