This commit is contained in:
aoshisen 2026-01-29 20:44:57 +08:00
parent 656de5b2b3
commit 4146933039
11 changed files with 118 additions and 2922 deletions

View File

@ -8,8 +8,7 @@
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
<script type="module" src="/src/main.ts"></script>
<script type="module" src="node_modules/hover-tilt/dist/hover-tilt.js"></script>
</body>
</html>

2874
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,20 @@
<script setup lang="ts">
import { useEditorStore } from '@/stores/editor';
const store = useEditorStore();
</script>
<style scoped>
img {
width: 400px;
display: block;
transition: all 0.3s ease;
}
</style>
<template>
<hover-tilt :width="200">
<img src="https://images.pokemontcg.io/swsh4/25_hires.png" />
<hover-tilt>
<img
src="https://images.pokemontcg.io/swsh4/25_hires.png"
:style="store.imageStyle"
/>
</hover-tilt>
</template>

View File

@ -1,35 +1,19 @@
<script lang="ts" setup>
import { folders } from "@/const/editor"
import { onMounted, ref, reactive } from 'vue'
import * as Tweakpane from 'tweakpane'
const Pane = Tweakpane.Pane
import { useEditorStore } from "@/stores/editor"
import { onMounted, ref } from 'vue'
import { Pane } from 'tweakpane'
const editorStore = useEditorStore()
const paneContainer = ref<HTMLDivElement | null>(null)
let pane: any = null
//
const formData = reactive<Record<string, any>>({})
//
function initializeFormData() {
folders.flatMap(f => f.type === 'folder' ? f.children : []).forEach((item: any) => {
formData[item.label] = item.value
})
}
// tweakpane
function createPane() {
function createPane(container: HTMLDivElement) {
if (!paneContainer.value) return
// pane
if (pane) {
pane.dispose()
}
pane = new Pane({
container: paneContainer.value
})
const pane = new Pane({ container })
// folders
folders.forEach(folder => {
@ -40,15 +24,17 @@ function createPane() {
})
folder.children.forEach((item: any) => {
f.addBinding(formData, item.label, item.params)
f.addBinding(editorStore.formData, item.label, item.params)
})
} else {
pane.addBinding(editorStore.formData, folder.label, folder.params)
}
})
}
onMounted(() => {
initializeFormData()
createPane()
editorStore.initializeFormData()
createPane(paneContainer.value as HTMLDivElement)
})
</script>

View File

@ -1,33 +1,26 @@
const editorConfig = [
{
value: 'Hello World',
label: "Text",
params: {}
},
{
label: "Range",
label: "border-radius",
value: 50,
params: {
min: 0,
step: 1,
max: 100
}
},
{
label: "Step",
value: 50,
label: "width",
value: 200,
params: {
min: 0,
max: 100,
min: 100,
max: 500,
step: 10
}
},
{
label: "quality",
value: 0,
params: {
options: { "medium": 0, "high": 1, "ultra": 2 }
}
label: "background-color",
value: "#fff",
params: {}
}
].map((i, index) => ({ ...i, id: index }));

View File

@ -2,6 +2,7 @@ import './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import App from './App.vue'
import router from './router'
@ -9,6 +10,7 @@ import router from './router'
const app = createApp(App)
app.use(router)
const pinia = createPinia();
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
app.use(pinia)
app.mount('#app')

View File

@ -1,6 +1,6 @@
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
const routes: any[] = [
]

42
src/stores/editor.ts Normal file
View File

@ -0,0 +1,42 @@
import { defineStore } from 'pinia'
import { reactive, computed } from 'vue'
import { folders } from '@/const/editor'
// 定义哪些属性需要添加 px 单位
const PX_PROPERTIES = ['width', 'border-radius']
export const useEditorStore = defineStore('editor', () => {
// 存储表单数据
const formData = reactive<Record<string, any>>({})
// 初始化表单数据
function initializeFormData() {
folders.flatMap(f => f.type === 'folder' ? f.children : []).forEach((item: any) => {
if (!(item.label in formData)) {
formData[item.label] = item.value
}
})
}
// 获取动态样式对象
const imageStyle = computed(() => {
const style: Record<string, any> = {}
Object.entries(formData).forEach(([key, value]) => {
if (PX_PROPERTIES.includes(key) && typeof value === 'number') {
style[key] = `${value}px`
} else {
style[key] = value
}
})
return style
})
return {
formData,
imageStyle,
initializeFormData,
}
}, {
persist: false
})

31
tsconfig.json Normal file
View File

@ -0,0 +1,31 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
/* Path mapping */
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}

10
tsconfig.node.json Normal file
View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}