From faa4663e20020c0e564bfa50207f0174bac83e1f Mon Sep 17 00:00:00 2001 From: koziavin Date: Sat, 2 Aug 2025 09:35:30 +0400 Subject: [PATCH] add heart animation --- index.html | 5 +- package-lock.json | 77 +++++++++++++++ package.json | 2 + .../Heart.glb => public/models/heart.glb | Bin src/App.vue | 31 ++---- src/components/typography/UiHeadering.vue | 48 ++++++++++ src/components/typography/UiParagraph.vue | 45 +++++++++ src/constants/heart-section.ts | 5 + src/constants/index.ts | 1 + src/declarations.d.ts | 66 +++++++++++++ src/layout/UiDefault.vue | 17 ++++ src/layout/UiFooter.vue | 7 ++ src/layout/UiHeader.vue | 7 ++ src/layout/UiMain.vue | 8 ++ src/main.ts | 5 +- src/pages/index/_ui/heartSection.vue | 88 ++++++++++++++++++ src/pages/index/rootPage.vue | 9 ++ src/router/config/_type/TRoutes.ts | 7 ++ src/router/config/routes.ts | 10 ++ src/router/index.ts | 8 ++ tsconfig.app.json | 8 +- 21 files changed, 423 insertions(+), 31 deletions(-) rename src/models/Heart.glb => public/models/heart.glb (100%) create mode 100644 src/components/typography/UiHeadering.vue create mode 100644 src/components/typography/UiParagraph.vue create mode 100644 src/constants/heart-section.ts create mode 100644 src/constants/index.ts create mode 100644 src/declarations.d.ts create mode 100644 src/layout/UiDefault.vue create mode 100644 src/layout/UiFooter.vue create mode 100644 src/layout/UiHeader.vue create mode 100644 src/layout/UiMain.vue create mode 100644 src/pages/index/_ui/heartSection.vue create mode 100644 src/pages/index/rootPage.vue create mode 100644 src/router/config/_type/TRoutes.ts create mode 100644 src/router/config/routes.ts create mode 100644 src/router/index.ts diff --git a/index.html b/index.html index dde16aa..d9ec90d 100644 --- a/index.html +++ b/index.html @@ -3,8 +3,9 @@ - - Vite + Vue + TS + + + I LOVE YOU
diff --git a/package-lock.json b/package-lock.json index 109a970..75f6987 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,8 @@ "vue-router": "^4.5.0" }, "devDependencies": { + "@types/node": "^24.1.0", + "@types/three": "^0.178.1", "@vitejs/plugin-vue": "^6.0.0", "@vue/eslint-config-prettier": "^10.2.0", "@vue/eslint-config-typescript": "^14.5.0", @@ -540,6 +542,13 @@ "node": ">=6.9.0" } }, + "node_modules/@dimforge/rapier3d-compat": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", + "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.8", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", @@ -1875,6 +1884,13 @@ "vite": "^5.2.0 || ^6 || ^7" } }, + "node_modules/@tweenjs/tween.js": { + "version": "23.1.3", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", + "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/draco3d": { "version": "1.4.10", "resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.10.tgz", @@ -1894,12 +1910,52 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node": { + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", + "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, "node_modules/@types/offscreencanvas": { "version": "2019.7.3", "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==", "license": "MIT" }, + "node_modules/@types/stats.js": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", + "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/three": { + "version": "0.178.1", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.178.1.tgz", + "integrity": "sha512-WSabew1mgWgRx2RfLfKY+9h4wyg6U94JfLbZEGU245j/WY2kXqU0MUfghS+3AYMV5ET1VlILAgpy77cB6a3Itw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@dimforge/rapier3d-compat": "~0.12.0", + "@tweenjs/tween.js": "~23.1.3", + "@types/stats.js": "*", + "@types/webxr": "*", + "@webgpu/types": "*", + "fflate": "~0.8.2", + "meshoptimizer": "~0.18.1" + } + }, + "node_modules/@types/three/node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/webxr": { "version": "0.5.22", "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.22.tgz", @@ -2515,6 +2571,13 @@ } } }, + "node_modules/@webgpu/types": { + "version": "0.1.64", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.64.tgz", + "integrity": "sha512-84kRIAGV46LJTlJZWxShiOrNL30A+9KokD7RB3dRCIqODFjodS5tCD5yyiZ8kIReGVZSDfA3XkkwyyOIF6K62A==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -4503,6 +4566,13 @@ "node": ">= 8" } }, + "node_modules/meshoptimizer": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", + "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==", + "dev": true, + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -5544,6 +5614,13 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "devOptional": true, + "license": "MIT" + }, "node_modules/unicorn-magic": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", diff --git a/package.json b/package.json index 09f0974..1006c8d 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,8 @@ "vue-router": "^4.5.0" }, "devDependencies": { + "@types/node": "^24.1.0", + "@types/three": "^0.178.1", "@vitejs/plugin-vue": "^6.0.0", "@vue/eslint-config-prettier": "^10.2.0", "@vue/eslint-config-typescript": "^14.5.0", diff --git a/src/models/Heart.glb b/public/models/heart.glb similarity index 100% rename from src/models/Heart.glb rename to public/models/heart.glb diff --git a/src/App.vue b/src/App.vue index 9691f5b..c89ff2a 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,27 +1,10 @@ - - - + diff --git a/src/components/typography/UiHeadering.vue b/src/components/typography/UiHeadering.vue new file mode 100644 index 0000000..3c91ff9 --- /dev/null +++ b/src/components/typography/UiHeadering.vue @@ -0,0 +1,48 @@ + + + diff --git a/src/components/typography/UiParagraph.vue b/src/components/typography/UiParagraph.vue new file mode 100644 index 0000000..105854e --- /dev/null +++ b/src/components/typography/UiParagraph.vue @@ -0,0 +1,45 @@ + + + diff --git a/src/constants/heart-section.ts b/src/constants/heart-section.ts new file mode 100644 index 0000000..88a2b3d --- /dev/null +++ b/src/constants/heart-section.ts @@ -0,0 +1,5 @@ +export default { + SCALE: 1, + DIRECTION: 1, + ANIMATION_ID: 0, +} diff --git a/src/constants/index.ts b/src/constants/index.ts new file mode 100644 index 0000000..f65eebc --- /dev/null +++ b/src/constants/index.ts @@ -0,0 +1 @@ +export { default as heartSectionConst } from './heart-section.ts' diff --git a/src/declarations.d.ts b/src/declarations.d.ts new file mode 100644 index 0000000..bcce09b --- /dev/null +++ b/src/declarations.d.ts @@ -0,0 +1,66 @@ +declare module 'three/examples/jsm/loaders/GLTFLoader' { + import { Loader, LoadingManager, Group, AnimationClip, Object3D } from 'three' + + export interface GLTF { + scene: Group + scenes: Group[] + animations: AnimationClip[] + cameras: Object3D[] + asset: Record + } + + export class GLTFLoader extends Loader { + constructor(manager?: LoadingManager) + load( + url: string, + onLoad: (gltf: GLTF) => void, + onProgress?: (event: ProgressEvent) => void, + onError?: (event: ErrorEvent | unknown) => void + ): void + } +} + +declare module 'three/examples/jsm/controls/OrbitControls' { + import { Camera, EventDispatcher, MOUSE, TOUCH, Vector3 } from 'three' + + export class OrbitControls extends EventDispatcher { + constructor(object: Camera, domElement?: HTMLElement) + object: Camera + target: Vector3 + update(): boolean + dispose(): void + + enabled: boolean + autoRotate: boolean + autoRotateSpeed: number + + minDistance: number + maxDistance: number + + minZoom: number + maxZoom: number + + minPolarAngle: number + maxPolarAngle: number + + minAzimuthAngle: number + maxAzimuthAngle: number + + enableDamping: boolean + dampingFactor: number + + enableZoom: boolean + zoomSpeed: number + + enableRotate: boolean + rotateSpeed: number + + enablePan: boolean + panSpeed: number + screenSpacePanning: boolean + keyPanSpeed: number + + mouseButtons: { LEFT: MOUSE; MIDDLE: MOUSE; RIGHT: MOUSE } + touches: { ONE: TOUCH; TWO: TOUCH } + } +} diff --git a/src/layout/UiDefault.vue b/src/layout/UiDefault.vue new file mode 100644 index 0000000..8f71aa8 --- /dev/null +++ b/src/layout/UiDefault.vue @@ -0,0 +1,17 @@ + + + diff --git a/src/layout/UiFooter.vue b/src/layout/UiFooter.vue new file mode 100644 index 0000000..967ca28 --- /dev/null +++ b/src/layout/UiFooter.vue @@ -0,0 +1,7 @@ + + + diff --git a/src/layout/UiHeader.vue b/src/layout/UiHeader.vue new file mode 100644 index 0000000..94cbc6e --- /dev/null +++ b/src/layout/UiHeader.vue @@ -0,0 +1,7 @@ + + + diff --git a/src/layout/UiMain.vue b/src/layout/UiMain.vue new file mode 100644 index 0000000..55b5129 --- /dev/null +++ b/src/layout/UiMain.vue @@ -0,0 +1,8 @@ + + diff --git a/src/main.ts b/src/main.ts index 6a27733..2b047f0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,8 @@ import { createApp } from 'vue' import '../src/assets/main.css' import App from './App.vue' +import router from './router' -createApp(App).mount('#app') +const app = createApp(App) +app.use(router) +app.mount('#app') diff --git a/src/pages/index/_ui/heartSection.vue b/src/pages/index/_ui/heartSection.vue new file mode 100644 index 0000000..41beff5 --- /dev/null +++ b/src/pages/index/_ui/heartSection.vue @@ -0,0 +1,88 @@ + + + diff --git a/src/pages/index/rootPage.vue b/src/pages/index/rootPage.vue new file mode 100644 index 0000000..bc9bbb1 --- /dev/null +++ b/src/pages/index/rootPage.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/router/config/_type/TRoutes.ts b/src/router/config/_type/TRoutes.ts new file mode 100644 index 0000000..fcadd8d --- /dev/null +++ b/src/router/config/_type/TRoutes.ts @@ -0,0 +1,7 @@ +import type { Component } from 'vue' + +export type TRoute = { + path: string + name: string + component: () => Promise +} diff --git a/src/router/config/routes.ts b/src/router/config/routes.ts new file mode 100644 index 0000000..7101618 --- /dev/null +++ b/src/router/config/routes.ts @@ -0,0 +1,10 @@ +import type { TRoute } from './_type/TRoutes.ts' + +const routes: TRoute[] = [ + { + path: '/', + name: '/', + component: () => import('../../pages/index/rootPage.vue'), + }, +] +export default routes diff --git a/src/router/index.ts b/src/router/index.ts new file mode 100644 index 0000000..cef1576 --- /dev/null +++ b/src/router/index.ts @@ -0,0 +1,8 @@ +import { createRouter, createWebHistory } from 'vue-router' +import routes from './config/routes.ts' + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes, +}) +export default router diff --git a/tsconfig.app.json b/tsconfig.app.json index 70fe34f..31dad50 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -1,7 +1,11 @@ { "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["./src/**/*", "./src/**/*.vue"], + "exclude": ["./src/**/__tests__/*"], "compilerOptions": { "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "@/*": ["./src/*"], + "~": ["../src/*"], /* Linting */ "strict": true, @@ -11,8 +15,4 @@ "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true }, - "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"], - "paths": { - "@/*": ["./src/*"] - } }