feat: 组件缩放功能

Re-1.0
咬轮猫 3 years ago
parent 2bb5b69afe
commit afd91e80e3

@ -1,4 +1,4 @@
lockfileVersion: 5.3
lockfileVersion: 5.4
specifiers:
'@commitlint/cli': ^17.0.3
@ -29,20 +29,20 @@ specifiers:
dependencies:
element-plus: 2.2.9_vue@3.2.37
pinia: 2.0.17_typescript@4.7.4+vue@3.2.37
pinia: 2.0.17_j6bzmzd4ujpabbp5objtwxyjp4
vue: 3.2.37
vue-router: 4.1.1_vue@3.2.37
devDependencies:
'@commitlint/cli': 17.0.3
'@commitlint/config-conventional': 17.0.3
'@typescript-eslint/eslint-plugin': 5.30.5_f646e16e2de31e818e163bded4698d6b
'@typescript-eslint/parser': 5.30.5_eslint@8.19.0+typescript@4.7.4
'@typescript-eslint/eslint-plugin': 5.30.5_6zdoc3rn4mpiddqwhppni2mnnm
'@typescript-eslint/parser': 5.30.5_4x5o4skxv6sl53vpwefgt23khm
'@vitejs/plugin-vue': 2.3.3_vite@2.9.13+vue@3.2.37
eslint: 8.19.0
eslint-config-prettier: 8.5.0_eslint@8.19.0
eslint-define-config: 1.5.1
eslint-plugin-prettier: 4.2.1_fd2e32b7574349919aac0818c3f895ea
eslint-plugin-prettier: 4.2.1_7uxdfn2xinezdgvmbammh6ev5i
eslint-plugin-vue: 9.2.0_eslint@8.19.0
husky: 8.0.1
less: 4.1.3
@ -69,11 +69,19 @@ packages:
'@babel/highlight': 7.18.6
dev: true
/@babel/helper-string-parser/7.19.4:
resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==}
engines: {node: '>=6.9.0'}
/@babel/helper-validator-identifier/7.18.6:
resolution: {integrity: sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-validator-identifier/7.19.1:
resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==}
engines: {node: '>=6.9.0'}
/@babel/highlight/7.18.6:
resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==}
engines: {node: '>=6.9.0'}
@ -87,6 +95,16 @@ packages:
resolution: {integrity: sha512-uQVSa9jJUe/G/304lXspfWVpKpK4euFLgGiMQFOCpM/bgcAdeoHwi/OQz23O9GK2osz26ZiXRRV9aV+Yl1O8tw==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
'@babel/types': 7.19.4
/@babel/types/7.19.4:
resolution: {integrity: sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-string-parser': 7.19.4
'@babel/helper-validator-identifier': 7.19.1
to-fast-properties: 2.0.0
/@commitlint/cli/17.0.3:
resolution: {integrity: sha512-oAo2vi5d8QZnAbtU5+0cR2j+A7PO8zuccux65R/EycwvsZrDVyW518FFrnJK2UQxbRtHFFIG+NjQ6vOiJV0Q8A==}
@ -173,7 +191,7 @@ packages:
'@types/node': 18.0.3
chalk: 4.1.2
cosmiconfig: 7.0.1
cosmiconfig-typescript-loader: 2.0.2_2dd5d46eecda2aef953638919121af58
cosmiconfig-typescript-loader: 2.0.2_fxk5i3xm3ivo7fjwhcizcinpla
lodash: 4.17.21
resolve-from: 5.0.0
typescript: 4.7.4
@ -431,7 +449,7 @@ packages:
resolution: {integrity: sha512-5d2RhCard1nQUC3aHcq/gHzWYO6K0WJmAbjO7mQJgCQKtZpgXxv1rOM6O/dBDhDYYVutk1sciOgNSe+5YyfM8A==}
dev: false
/@typescript-eslint/eslint-plugin/5.30.5_f646e16e2de31e818e163bded4698d6b:
/@typescript-eslint/eslint-plugin/5.30.5_6zdoc3rn4mpiddqwhppni2mnnm:
resolution: {integrity: sha512-lftkqRoBvc28VFXEoRgyZuztyVUQ04JvUnATSPtIRFAccbXTWL6DEtXGYMcbg998kXw1NLUJm7rTQ9eUt+q6Ig==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@ -442,10 +460,10 @@ packages:
typescript:
optional: true
dependencies:
'@typescript-eslint/parser': 5.30.5_eslint@8.19.0+typescript@4.7.4
'@typescript-eslint/parser': 5.30.5_4x5o4skxv6sl53vpwefgt23khm
'@typescript-eslint/scope-manager': 5.30.5
'@typescript-eslint/type-utils': 5.30.5_eslint@8.19.0+typescript@4.7.4
'@typescript-eslint/utils': 5.30.5_eslint@8.19.0+typescript@4.7.4
'@typescript-eslint/type-utils': 5.30.5_4x5o4skxv6sl53vpwefgt23khm
'@typescript-eslint/utils': 5.30.5_4x5o4skxv6sl53vpwefgt23khm
debug: 4.3.4
eslint: 8.19.0
functional-red-black-tree: 1.0.1
@ -458,7 +476,7 @@ packages:
- supports-color
dev: true
/@typescript-eslint/parser/5.30.5_eslint@8.19.0+typescript@4.7.4:
/@typescript-eslint/parser/5.30.5_4x5o4skxv6sl53vpwefgt23khm:
resolution: {integrity: sha512-zj251pcPXI8GO9NDKWWmygP6+UjwWmrdf9qMW/L/uQJBM/0XbU2inxe5io/234y/RCvwpKEYjZ6c1YrXERkK4Q==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@ -486,7 +504,7 @@ packages:
'@typescript-eslint/visitor-keys': 5.30.5
dev: true
/@typescript-eslint/type-utils/5.30.5_eslint@8.19.0+typescript@4.7.4:
/@typescript-eslint/type-utils/5.30.5_4x5o4skxv6sl53vpwefgt23khm:
resolution: {integrity: sha512-k9+ejlv1GgwN1nN7XjVtyCgE0BTzhzT1YsQF0rv4Vfj2U9xnslBgMYYvcEYAFVdvhuEscELJsB7lDkN7WusErw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@ -496,7 +514,7 @@ packages:
typescript:
optional: true
dependencies:
'@typescript-eslint/utils': 5.30.5_eslint@8.19.0+typescript@4.7.4
'@typescript-eslint/utils': 5.30.5_4x5o4skxv6sl53vpwefgt23khm
debug: 4.3.4
eslint: 8.19.0
tsutils: 3.21.0_typescript@4.7.4
@ -531,7 +549,7 @@ packages:
- supports-color
dev: true
/@typescript-eslint/utils/5.30.5_eslint@8.19.0+typescript@4.7.4:
/@typescript-eslint/utils/5.30.5_4x5o4skxv6sl53vpwefgt23khm:
resolution: {integrity: sha512-o4SSUH9IkuA7AYIfAvatldovurqTAHrfzPApOZvdUq01hHojZojCFXx06D/aFpKCgWbMPRdJBWAC3sWp3itwTA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@ -659,7 +677,6 @@ packages:
dependencies:
'@vue/reactivity': 3.2.37
'@vue/shared': 3.2.37
dev: false
/@vue/runtime-dom/3.2.37:
resolution: {integrity: sha512-HimKdh9BepShW6YozwRKAYjYQWg9mQn63RGEiSswMbW+ssIht1MILYlVGkAGGQbkhSh31PCdoUcfiu4apXJoPw==}
@ -667,7 +684,6 @@ packages:
'@vue/runtime-core': 3.2.37
'@vue/shared': 3.2.37
csstype: 2.6.20
dev: false
/@vue/server-renderer/3.2.37_vue@3.2.37:
resolution: {integrity: sha512-kLITEJvaYgZQ2h47hIzPh2K3jG8c1zCVbp/o/bzQOyvzaKiCquKS7AaioPI28GNxIsE/zSx+EwWYsNxDCX95MA==}
@ -677,7 +693,6 @@ packages:
'@vue/compiler-ssr': 3.2.37
'@vue/shared': 3.2.37
vue: 3.2.37
dev: false
/@vue/shared/3.2.37:
resolution: {integrity: sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw==}
@ -924,6 +939,8 @@ packages:
snapdragon-node: 2.1.1
split-string: 3.1.0
to-regex: 3.0.2
transitivePeerDependencies:
- supports-color
dev: true
/braces/3.0.2:
@ -1089,8 +1106,8 @@ packages:
engines: {node: '>=10'}
hasBin: true
dependencies:
is-text-path: 1.0.1
JSONStream: 1.3.5
is-text-path: 1.0.1
lodash: 4.17.21
meow: 8.1.2
split2: 3.2.2
@ -1116,7 +1133,7 @@ packages:
vary: 1.1.2
dev: true
/cosmiconfig-typescript-loader/2.0.2_2dd5d46eecda2aef953638919121af58:
/cosmiconfig-typescript-loader/2.0.2_fxk5i3xm3ivo7fjwhcizcinpla:
resolution: {integrity: sha512-KmE+bMjWMXJbkWCeY4FJX/npHuZPNr9XF9q9CIQ/bpFwi1qHfCmSiKarrCcRa0LO4fWjk93pVoeRtJAkTGcYNw==}
engines: {node: '>=12', npm: '>=6'}
peerDependencies:
@ -1125,7 +1142,7 @@ packages:
dependencies:
'@types/node': 18.0.3
cosmiconfig: 7.0.1
ts-node: 10.8.2_2dd5d46eecda2aef953638919121af58
ts-node: 10.8.2_fxk5i3xm3ivo7fjwhcizcinpla
typescript: 4.7.4
transitivePeerDependencies:
- '@swc/core'
@ -1194,7 +1211,6 @@ packages:
/csstype/2.6.20:
resolution: {integrity: sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==}
dev: false
/dargs/7.0.0:
resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==}
@ -1207,12 +1223,22 @@ packages:
/debug/2.6.9:
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.0.0
dev: true
/debug/3.2.7:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.1.2
dev: true
@ -1368,7 +1394,7 @@ packages:
escape-html: 1.0.3
lodash: 4.17.21
lodash-es: 4.17.21
lodash-unified: 1.0.2_da03a4540fbd16bbaafbb96724306afd
lodash-unified: 1.0.2_3ib2ivapxullxkx3xftsimdk7u
memoize-one: 6.0.0
normalize-wheel-es: 1.1.2
vue: 3.2.37
@ -1649,7 +1675,7 @@ packages:
engines: {node: '>= 14.6.0', npm: '>= 6.0.0', pnpm: '>= 7.0.0'}
dev: true
/eslint-plugin-prettier/4.2.1_fd2e32b7574349919aac0818c3f895ea:
/eslint-plugin-prettier/4.2.1_7uxdfn2xinezdgvmbammh6ev5i:
resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==}
engines: {node: '>=12.0.0'}
peerDependencies:
@ -1836,6 +1862,8 @@ packages:
regex-not: 1.0.2
snapdragon: 0.8.2
to-regex: 3.0.2
transitivePeerDependencies:
- supports-color
dev: true
/extend-shallow/2.0.1:
@ -1865,6 +1893,8 @@ packages:
regex-not: 1.0.2
snapdragon: 0.8.2
to-regex: 3.0.2
transitivePeerDependencies:
- supports-color
dev: true
/fast-deep-equal/3.1.3:
@ -2490,6 +2520,8 @@ packages:
mime: 1.6.0
needle: 3.1.0
source-map: 0.6.1
transitivePeerDependencies:
- supports-color
dev: true
/levn/0.4.1:
@ -2531,7 +2563,7 @@ packages:
resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
dev: false
/lodash-unified/1.0.2_da03a4540fbd16bbaafbb96724306afd:
/lodash-unified/1.0.2_3ib2ivapxullxkx3xftsimdk7u:
resolution: {integrity: sha512-OGbEy+1P+UT26CYi4opY4gebD8cWRDxAT6MAObIVQMiqYdxZr1g3QHWCToVsm31x2NkLS4K3+MC2qInaRMa39g==}
peerDependencies:
'@types/lodash-es': '*'
@ -2663,6 +2695,8 @@ packages:
regex-not: 1.0.2
snapdragon: 0.8.2
to-regex: 3.0.2
transitivePeerDependencies:
- supports-color
dev: true
/micromatch/4.0.5:
@ -2746,6 +2780,8 @@ packages:
regex-not: 1.0.2
snapdragon: 0.8.2
to-regex: 3.0.2
transitivePeerDependencies:
- supports-color
dev: true
/natural-compare/1.4.0:
@ -2761,6 +2797,8 @@ packages:
debug: 3.2.7
iconv-lite: 0.6.3
sax: 1.2.4
transitivePeerDependencies:
- supports-color
dev: true
optional: true
@ -2955,7 +2993,7 @@ packages:
dev: true
optional: true
/pinia/2.0.17_typescript@4.7.4+vue@3.2.37:
/pinia/2.0.17_j6bzmzd4ujpabbp5objtwxyjp4:
resolution: {integrity: sha512-AtwLwEWQgIjofjgeFT+nxbnK5lT2QwQjaHNEDqpsi2AiCwf/NY78uWTeHUyEhiiJy8+sBmw0ujgQMoQbWiZDfA==}
peerDependencies:
'@vue/composition-api': ^1.4.0
@ -3320,6 +3358,8 @@ packages:
source-map: 0.5.7
source-map-resolve: 0.5.3
use: 3.1.1
transitivePeerDependencies:
- supports-color
dev: true
/source-map-js/1.0.2:
@ -3500,6 +3540,8 @@ packages:
posthtml-svg-mode: 1.0.3
query-string: 4.3.4
traverse: 0.6.6
transitivePeerDependencies:
- supports-color
dev: true
/svgo/2.8.0:
@ -3535,6 +3577,10 @@ packages:
readable-stream: 3.6.0
dev: true
/to-fast-properties/2.0.0:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'}
/to-object-path/0.3.0:
resolution: {integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==}
engines: {node: '>=0.10.0'}
@ -3576,7 +3622,7 @@ packages:
engines: {node: '>=8'}
dev: true
/ts-node/10.8.2_2dd5d46eecda2aef953638919121af58:
/ts-node/10.8.2_fxk5i3xm3ivo7fjwhcizcinpla:
resolution: {integrity: sha512-LYdGnoGddf1D6v8REPtIH+5iq/gTDuZqv2/UJUU7tKjuEU8xVZorBM+buCGNjj+pGEud+sOoM4CX3/YzINpENA==}
hasBin: true
peerDependencies:
@ -3656,7 +3702,6 @@ packages:
resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==}
engines: {node: '>=4.2.0'}
hasBin: true
dev: true
/union-value/1.0.1:
resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==}
@ -3851,7 +3896,6 @@ packages:
'@vue/runtime-dom': 3.2.37
'@vue/server-renderer': 3.2.37_vue@3.2.37
'@vue/shared': 3.2.37
dev: false
/which/2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}

@ -15,6 +15,6 @@ module.exports = {
requirePragma: false, // 需要编译指示
proseWrap: 'never', // 如果散文超过打印宽度,则换行
htmlWhitespaceSensitivity: 'strict', // 所有标签周围的空格(或缺少空格)被认为是重要的。
endOfLine: 'lf', // 确保在文本文件中仅使用 ( \n)换行,常见于 Linux 和 macOS 以及 git repos 内部。
endOfLine: 'auto', // 确保在文本文件中仅使用 ( \n)换行,常见于 Linux 和 macOS 以及 git repos 内部。
rangeStart: 0 // 格式化文件时,回到包含所选语句的第一行的开头。
};

@ -26,29 +26,59 @@
v-for="(item, index) in globalStore.done_json"
:key="item.id"
:transform="`translate(${item.x},${item.y})rotate(0)scale(1)`"
:class="`${globalStore.intention == EGlobalStoreIntention.None ? 'svg-item-none' : ''}
${
globalStore.intention == EGlobalStoreIntention.Move &&
globalStore.handle_svg_info?.info.id == item.id
? 'svg-item-move'
: ''
} ${
globalStore.intention == EGlobalStoreIntention.Select &&
globalStore.handle_svg_info?.info.id == item.id
? 'svg-item-select'
: ''
}`"
@mousedown="onSvgMouseDown(item, index, $event)"
>
<rect
<use
:xlink:href="`#svg-${item.name}`"
fill="#ff0000"
width="100"
height="100"
:id="item.id"
:transform="`translate(${item.actual_bound.x + item.actual_bound.width / 2},${
item.actual_bound.y + item.actual_bound.height / 2
}) scale(${item.scale_x},${item.scale_y}) translate(${-(
item.actual_bound.x +
item.actual_bound.width / 2
)},${-(item.actual_bound.y + item.actual_bound.height / 2)})`"
></use>
<rect
:id="`rect${item.id}`"
fill="black"
fill-opacity="0"
:x="
item.actual_bound.x -
(item.actual_bound.width / 2) * item.scale_x +
item.actual_bound.width / 2
"
:y="
item.actual_bound.y -
(item.actual_bound.height / 2) * item.scale_y +
item.actual_bound.height / 2
"
:width="item.actual_bound.width * item.scale_x"
:height="item.actual_bound.height * item.scale_y"
style="stroke: none; stroke-width: 2; stroke-miterlimit: 10"
:class="`${globalStore.intention == EGlobalStoreIntention.None ? 'svg-item-none' : ''}
${
globalStore.intention == EGlobalStoreIntention.Move &&
globalStore.handle_svg_info?.info.id == item.id
? 'svg-item-move'
: ''
} ${
globalStore.intention == EGlobalStoreIntention.Select &&
globalStore.handle_svg_info?.info.id == item.id
? 'svg-item-select'
: ''
}`"
@mousedown="onSvgMouseDown(item, index, $event)"
></rect>
<use :xlink:href="`#svg-${item.name}`" fill="#ff0000" width="100" height="100"></use>
<handle-panel
v-if="
globalStore.handle_svg_info?.info.id == item.id &&
(globalStore.intention == EGlobalStoreIntention.Select ||
globalStore.intention == EGlobalStoreIntention.Zoom)
"
:item-info="item"
></handle-panel>
</g>
</g>
</svg>
@ -60,11 +90,13 @@
import {
EGlobalStoreIntention,
EMouseInfoState,
EScaleInfoType,
IDoneJson
} from '../../../../store/global/types';
import { useSvgEditLayoutStore } from '../../../../store/svgedit-layout';
import { randomString } from '../../../../utils';
import HandlePanel from '../handle-panel/index.vue';
// import HandlePanel from '../handle-panel/index.vue';
const globalStore = useGlobalStore();
const configStore = useConfigStore();
const svgEditLayoutStore = useSvgEditLayoutStore();
@ -76,14 +108,23 @@
console.error('要创建的数据获取失败');
return;
}
const done_item_json = {
id: randomString(),
const done_item_json: IDoneJson = {
id: globalStore.create_svg_info.name + randomString(),
x: e.offsetX - svgEditLayoutStore.center_offset.x,
y: e.offsetY - svgEditLayoutStore.center_offset.y,
scale_x: 1,
scale_y: 1,
actual_bound: {
x: 0,
y: 0,
width: 0,
height: 0
},
...globalStore.create_svg_info
};
globalStore.setHandleSvgInfo(done_item_json, globalStore.done_json.length);
globalStore.setDoneJson(done_item_json);
globalStore.intention = EGlobalStoreIntention.None;
}
};
const dragEnterEvent = (e: DragEvent) => {
@ -133,10 +174,63 @@
//
svgEditLayoutStore.center_offset.x = globalStore.mouse_info.new_position_x;
svgEditLayoutStore.center_offset.y = globalStore.mouse_info.new_position_y;
} else if (globalStore.intention == EGlobalStoreIntention.Zoom) {
//
const move_length_x =
globalStore.scale_info.type === EScaleInfoType.TopLeft ||
globalStore.scale_info.type === EScaleInfoType.Left ||
globalStore.scale_info.type === EScaleInfoType.BottomLeft
? globalStore.mouse_info.new_position_x - globalStore.mouse_info.now_position_x
: globalStore.scale_info.type === EScaleInfoType.TopRight ||
globalStore.scale_info.type === EScaleInfoType.Right ||
globalStore.scale_info.type === EScaleInfoType.BottomRight
? globalStore.mouse_info.now_position_x - globalStore.mouse_info.new_position_x
: 0;
const move_length_y =
globalStore.scale_info.type === EScaleInfoType.TopLeft ||
globalStore.scale_info.type === EScaleInfoType.TopCenter ||
globalStore.scale_info.type === EScaleInfoType.TopRight
? globalStore.mouse_info.new_position_y - globalStore.mouse_info.now_position_y
: globalStore.scale_info.type === EScaleInfoType.BottomLeft ||
globalStore.scale_info.type === EScaleInfoType.BottomCenter ||
globalStore.scale_info.type === EScaleInfoType.BottomRight
? globalStore.mouse_info.now_position_y - globalStore.mouse_info.new_position_y
: 0;
//
if (globalStore.handle_svg_info) {
const scale_x =
(globalStore.handle_svg_info.info.actual_bound.width *
globalStore.scale_info.scale_times.x -
move_length_x) /
globalStore.handle_svg_info.info.actual_bound.width;
const scale_y =
(globalStore.handle_svg_info.info.actual_bound.height *
globalStore.scale_info.scale_times.y -
move_length_y) /
globalStore.handle_svg_info.info.actual_bound.height;
if (scale_x > 0) {
globalStore.handle_svg_info.info.scale_x = scale_x;
globalStore.handle_svg_info.info.x =
globalStore.scale_info.type === EScaleInfoType.TopLeft ||
globalStore.scale_info.type === EScaleInfoType.Left ||
globalStore.scale_info.type === EScaleInfoType.BottomLeft
? globalStore.scale_info.scale_item_info.x + move_length_x / 2
: globalStore.scale_info.scale_item_info.x - move_length_x / 2;
}
if (scale_y > 0) {
globalStore.handle_svg_info.info.scale_y = scale_y;
globalStore.handle_svg_info.info.y =
globalStore.scale_info.type === EScaleInfoType.TopLeft ||
globalStore.scale_info.type === EScaleInfoType.TopCenter ||
globalStore.scale_info.type === EScaleInfoType.TopRight
? globalStore.scale_info.scale_item_info.y + move_length_y / 2
: globalStore.scale_info.scale_item_info.y - move_length_y / 2;
}
}
}
};
const onCanvasMouseUp = () => {
//
//
if (globalStore.mouse_info.state != EMouseInfoState.Down) {
return;
}

@ -0,0 +1,148 @@
<!-- eslint-disable prettier/prettier -->
<!-- 旋转缩放组件 -->
<template>
<g style="vector-effect: non-scaling-stroke">
<rect
id="resize_tl"
width="8"
height="8"
:fill="fill"
style="cursor: nw-resize; vector-effect: non-scaling-stroke"
pointer-events="all"
:x="props.itemInfo.actual_bound.x-offset-getCoordinateOffset(props.itemInfo.actual_bound.width,props.itemInfo.scale_x)"
:y="props.itemInfo.actual_bound.y-offset-getCoordinateOffset(props.itemInfo.actual_bound.height,props.itemInfo.scale_y)"
stroke="rgba(0,0,0,0)"
@mousedown="onHandleMouseDown(EScaleInfoType.TopLeft,$event)"
></rect
><rect
id="resize_tc"
width="8"
height="8"
:fill="fill"
style="cursor: n-resize; vector-effect: non-scaling-stroke"
pointer-events="all"
:x="props.itemInfo.actual_bound.x+props.itemInfo.actual_bound.width/2-offset"
:y="props.itemInfo.actual_bound.y-offset-getCoordinateOffset(props.itemInfo.actual_bound.height,props.itemInfo.scale_y)"
stroke="rgba(0,0,0,0)"
@mousedown="onHandleMouseDown(EScaleInfoType.TopCenter,$event)"
></rect
><rect
id="resize_tr"
width="8"
height="8"
:fill="fill"
style="cursor: ne-resize; vector-effect: non-scaling-stroke"
pointer-events="all"
:x="props.itemInfo.actual_bound.x+props.itemInfo.actual_bound.width-offset+getCoordinateOffset(props.itemInfo.actual_bound.width,props.itemInfo.scale_x)"
:y="props.itemInfo.actual_bound.y-offset-getCoordinateOffset(props.itemInfo.actual_bound.height,props.itemInfo.scale_y)"
stroke="rgba(0,0,0,0)"
@mousedown="onHandleMouseDown(EScaleInfoType.TopRight,$event)"
></rect
><rect
id="resize_l"
width="8"
height="8"
:fill="fill"
style="cursor: e-resize; vector-effect: non-scaling-stroke"
pointer-events="all"
:x="props.itemInfo.actual_bound.x-offset-getCoordinateOffset(props.itemInfo.actual_bound.width,props.itemInfo.scale_x)"
:y="props.itemInfo.actual_bound.y-offset+props.itemInfo.actual_bound.height*props.itemInfo.scale_y/2-getCoordinateOffset(props.itemInfo.actual_bound.height,props.itemInfo.scale_y)"
stroke="rgba(0,0,0,0)"
@mousedown="onHandleMouseDown(EScaleInfoType.Left,$event)"
></rect
><rect
id="resize_r"
width="8"
height="8"
:fill="fill"
style="cursor: w-resize; vector-effect: non-scaling-stroke"
pointer-events="all"
:x="props.itemInfo.actual_bound.x-offset+props.itemInfo.actual_bound.width+getCoordinateOffset(props.itemInfo.actual_bound.width,props.itemInfo.scale_x)"
:y="props.itemInfo.actual_bound.y-offset+props.itemInfo.actual_bound.height*props.itemInfo.scale_y/2-getCoordinateOffset(props.itemInfo.actual_bound.height,props.itemInfo.scale_y)"
stroke="rgba(0,0,0,0)"
@mousedown="onHandleMouseDown(EScaleInfoType.Right,$event)"
></rect
><rect
id="resize_bl"
width="8"
height="8"
:fill="fill"
style="cursor: sw-resize; vector-effect: non-scaling-stroke"
pointer-events="all"
:x="props.itemInfo.actual_bound.x-offset-getCoordinateOffset(props.itemInfo.actual_bound.width,props.itemInfo.scale_x)"
:y="props.itemInfo.actual_bound.y-offset+props.itemInfo.actual_bound.height*props.itemInfo.scale_y-getCoordinateOffset(props.itemInfo.actual_bound.height,props.itemInfo.scale_y)"
stroke="rgba(0,0,0,0)"
@mousedown="onHandleMouseDown(EScaleInfoType.BottomLeft,$event)"
></rect
><rect
id="resize_bc"
width="8"
height="8"
:fill="fill"
style="cursor: s-resize; vector-effect: non-scaling-stroke"
pointer-events="all"
:x="props.itemInfo.actual_bound.x-offset+props.itemInfo.actual_bound.width/2"
:y="props.itemInfo.actual_bound.y-offset+props.itemInfo.actual_bound.height+getCoordinateOffset(props.itemInfo.actual_bound.height,props.itemInfo.scale_y)"
stroke="rgba(0,0,0,0)"
@mousedown="onHandleMouseDown(EScaleInfoType.BottomCenter,$event)"
></rect
><rect
id="resize_br"
width="8"
height="8"
:fill="fill"
style="cursor: se-resize; vector-effect: non-scaling-stroke"
pointer-events="all"
:x="props.itemInfo.actual_bound.x-offset+props.itemInfo.actual_bound.width+getCoordinateOffset(props.itemInfo.actual_bound.width,props.itemInfo.scale_x)"
:y="props.itemInfo.actual_bound.y-offset+props.itemInfo.actual_bound.height+getCoordinateOffset(props.itemInfo.actual_bound.height,props.itemInfo.scale_y)"
stroke="rgba(0,0,0,0)"
@mousedown="onHandleMouseDown(EScaleInfoType.BottomRight,$event)"
></rect>
</g>
</template>
<script lang="ts" setup>
import { PropType, ref } from 'vue';
import { useGlobalStore } from '../../../../store/global/index.js';
import {
EGlobalStoreIntention,
EMouseInfoState,
EScaleInfoType,
IDoneJson
} from '../../../../store/global/types';
import { getCoordinateOffset } from '../../../../utils/index';
const props = defineProps({
itemInfo: {
type: Object as PropType<IDoneJson>,
default: () => {}
}
});
const globalStore = useGlobalStore();
const offset = ref(5);
const fill = ref('#4F80FF');
const onHandleMouseDown = (type: EScaleInfoType, e: MouseEvent) => {
const { clientX, clientY } = e;
e.cancelBubble = true;
globalStore.intention = EGlobalStoreIntention.Zoom;
globalStore.setMouseInfo({
state: EMouseInfoState.Down,
position_x: clientX,
position_y: clientY,
now_position_x: clientX,
now_position_y: clientY,
new_position_x: 0,
new_position_y: 0
});
globalStore.setScaleInfo({
type: type,
scale_times: {
x: props.itemInfo.scale_x,
y: props.itemInfo.scale_y
},
scale_item_info: {
x: props.itemInfo.x,
y: props.itemInfo.y
}
});
};
</script>
<style scoped></style>

@ -1,13 +1,16 @@
import { defineStore } from 'pinia';
import { nextTick } from 'vue';
import { configCenter } from '../../config-center';
import { IConfigItem } from '../../config-center/types';
import { isOfType } from '../../utils';
import {
EGlobalStoreIntention,
EMouseInfoState,
EScaleInfoType,
IDoneJson,
IGlobalStore,
IMouseInfo
IMouseInfo,
IScaleInfo
} from './types';
/**
*
@ -28,7 +31,18 @@ export const useGlobalStore = defineStore('global-store', {
new_position_x: 0,
new_position_y: 0
},
handle_svg_info: undefined
handle_svg_info: undefined,
scale_info: {
type: EScaleInfoType.None,
scale_times: {
x: 1,
y: 1
},
scale_item_info: {
x: 0,
y: 0
}
}
};
},
getters: {},
@ -38,11 +52,25 @@ export const useGlobalStore = defineStore('global-store', {
this.create_svg_info = create_svg_info;
},
setDoneJson(done_json: IDoneJson[] | IDoneJson) {
// console.log('这里要记录操作历史记录');
console.log('这里要记录操作历史记录');
if (isOfType(done_json, 'id')) {
this.done_json.push(done_json);
nextTick(() => {
const queryBbox = document.querySelector(`#${done_json.id}`);
const rectBBox = document.querySelector(`#rect${done_json.id}`);
// console.log(queryBbox, 190);
if (queryBbox && rectBBox) {
const BBox = (queryBbox as SVGGraphicsElement).getBBox();
const { x, y, width, height } = BBox;
rectBBox.setAttribute('x', x.toString());
rectBBox.setAttribute('y', y.toString());
rectBBox.setAttribute('width', width.toString());
rectBBox.setAttribute('height', height.toString());
done_json.actual_bound = { x, y, width, height };
}
});
} else {
this.done_json = done_json;
this.done_json = JSON.parse(JSON.stringify(done_json));
}
},
setMouseInfo(mouse_info: IMouseInfo) {
@ -57,6 +85,9 @@ export const useGlobalStore = defineStore('global-store', {
} else {
this.handle_svg_info = info;
}
},
setScaleInfo(info: IScaleInfo) {
this.scale_info = info;
}
}
});

@ -7,26 +7,36 @@ export interface IGlobalStore {
done_json: IDoneJson[];
mouse_info: IMouseInfo;
handle_svg_info: IHandleSvgInfo | undefined;
scale_info: IScaleInfo;
}
export interface IDoneJson extends IConfigItem {
id: string;
x: number;
y: number;
scale_x: number;
scale_y: number;
actual_bound: {
x: number;
y: number;
width: number;
height: number;
};
}
export enum EGlobalStoreIntention {
None = 'None',
Create = 'Create',
Move = 'Move',
MoveCanvas = 'MoveCanvas',
Select = 'Select'
Select = 'Select',
Zoom = 'Zoom'
}
export interface IMouseInfo {
state: EMouseInfoState;
position_x: number;
position_x: number; //鼠标指针坐标
position_y: number;
now_position_x: number;
now_position_x: number; //当前目标的坐标
now_position_y: number;
new_position_x: number;
new_position_x: number; //移动之后目标的坐标
new_position_y: number;
}
export enum EMouseInfoState {
@ -37,3 +47,29 @@ export interface IHandleSvgInfo {
info: IDoneJson;
index: number;
}
/**
*
*/
export interface IScaleInfo {
type: EScaleInfoType;
scale_times: {
//点击缩放时当前组件的缩放倍数
x: number;
y: number;
};
scale_item_info: {
x: number;
y: number;
};
}
export enum EScaleInfoType {
None = '',
TopLeft = 'TopLeft',
TopCenter = 'TopCenter',
TopRight = 'TopRight',
Left = 'Left',
Right = 'Right',
BottomLeft = 'BottomLeft',
BottomCenter = 'BottomCenter',
BottomRight = 'BottomRight'
}

@ -17,3 +17,12 @@ export const randomString = (len?: number) => {
export const isOfType = <T>(target: unknown, prop: keyof T): target is T => {
return (target as T)[prop] !== undefined;
};
/**
*
* @param length /
* @param scale
* @returns
*/
export const getCoordinateOffset = (length: number, scale: number) => {
return (length / 2) * (scale - 1);
};

@ -2,7 +2,8 @@
"compilerOptions": {
"composite": true,
"module": "esnext",
"moduleResolution": "node"
"moduleResolution": "node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

Loading…
Cancel
Save