Skip to content

Commit d3f8225

Browse files
committed
feat: 添加更多图形,支持取消操作,添加query选择函数
1 parent fa189cc commit d3f8225

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2945
-604
lines changed

.eslintrc.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
"ignoredNodes": ["ConditionalExpression"]
2020
}
2121
],
22-
"object-curly-spacing": ["warn", "always", { "arraysInObjects": false }],
2322
"linebreak-style": ["warn", "unix"],
2423
"quotes": ["warn", "single"],
2524
"semi": ["warn", "never"],
2625
"@typescript-eslint/no-non-null-assertion": ["warn"],
2726
"@typescript-eslint/no-unused-vars": ["warn"],
28-
"@typescript-eslint/no-empty-function": ["warn"]
27+
"@typescript-eslint/no-empty-function": ["warn"],
28+
"no-restricted-imports": ["error", "."],
29+
"eqeqeq": ["error"],
30+
"@typescript-eslint/no-namespace": [2, { "allowDeclarations": true }]
2931
}
3032
}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ dist
44
dist-ssr
55
*.local
66
*.log
7+
*.cache

.rush/temp/shrinkwrap-deps.json

Lines changed: 0 additions & 19 deletions
This file was deleted.

index.html

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,43 +7,53 @@
77
<title>Revit Simulator</title>
88
<style>
99
body {
10+
display: grid;
1011
padding: 0;
1112
margin: 0;
1213
overflow: hidden;
1314
}
14-
#app {
15-
width: 100vw;
16-
height: 100vh;
15+
.kad-debug {
16+
width: 200px;
17+
left: 0;
1718
}
1819
.action-bar {
19-
position: absolute;
20+
position: relative;
2021
z-index: 2;
21-
bottom: 0;
22+
width: 100%;
23+
height: 40px;
2224
display: flex;
2325
width: 100%;
2426
justify-content: center;
2527
align-items: center;
2628
padding: 6px;
2729
box-sizing: border-box;
30+
background: #cccccc33;
2831
}
29-
.action-bar::before {
30-
content: "";
31-
position: absolute;
32-
width: 100%;
33-
height: 100%;
34-
pointer-events: none;
32+
#app {
33+
display: grid;
34+
position: relative;
35+
margin-left: 200px;
36+
width: calc(100vw - 200);
37+
height: 100vh;
38+
overflow: auto;
3539
}
36-
.action-bar:hover::before {
37-
background: #cccccc33;
40+
.stage-wrapper {
41+
position: relative;
42+
overflow: auto;
43+
}
44+
#stage {
45+
position: relative;
46+
width: 200%;
47+
height: 200%;
3848
}
3949
</style>
4050
</head>
4151
<body>
42-
<div id="app"></div>
43-
<div class="action-bar">
44-
<button class="action" data-action="select">select</button>
45-
<button class="action" data-action="move">move</button>
46-
<button class="action" data-action="rotate">rotate</button>
52+
<div id="app">
53+
<div class="action-bar"></div>
54+
<div class="stage-wrapper">
55+
<div id="stage"></div>
56+
</div>
4757
</div>
4858
<script type="module" src="/src/preview.ts"></script>
4959
</body>

package.json

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"build:esm": "rollup --config rollup.config.js",
88
"serve": "vite preview",
99
"watch": "yarn build:esm --watch",
10-
"test": "tsc --noEmit -p tsconfig.json",
10+
"test": "npm run build:esm",
1111
"lint": "eslint --fix --config .eslintrc.json",
1212
"precommit": "echo 'precommit' && lint-staged"
1313
},
@@ -35,24 +35,29 @@
3535
"@babel/plugin-proposal-class-properties": "^7.16.0",
3636
"@babel/plugin-proposal-decorators": "^7.16.0",
3737
"@rollup/plugin-babel": "^5.3.0",
38+
"@rollup/plugin-commonjs": "^21.0.1",
3839
"@rollup/plugin-image": "^2.1.1",
40+
"@rollup/plugin-node-resolve": "^13.1.1",
3941
"@rollup/plugin-typescript": "^8.3.0",
40-
"@types/fabric": "^4.5.3",
42+
"@types/bluebird": "^3.5.36",
4143
"@types/node": "^16.11.6",
4244
"@typescript-eslint/eslint-plugin": "^5.2.0",
4345
"@typescript-eslint/parser": "^5.2.0",
46+
"@zerollup/ts-transform-paths": "^1.7.18",
4447
"cross-env": "^7.0.3",
4548
"eslint": "^8.1.0",
4649
"eslint-config-prettier": "^8.3.0",
4750
"prettier": "^2.4.1",
4851
"rollup": "^2.60.0",
4952
"rollup-plugin-multi-input": "^1.3.1",
53+
"tslib": "^2.3.1",
54+
"ttypescript": "^1.5.13",
5055
"typescript": "^4.4.4",
5156
"vite": "^2.4.3"
5257
},
5358
"dependencies": {
54-
"@dxfom/dxf": "^0.0.2",
55-
"konva": "^8.2.3",
59+
"bluebird": "^3.7.2",
60+
"konva": "^8.3.1",
5661
"rxjs": "~7.3.0"
5762
}
5863
}

rollup.config.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
1+
import ttypescript from 'ttypescript'
2+
import commomjs from '@rollup/plugin-commonjs'
3+
import image from '@rollup/plugin-image'
4+
import resolve from '@rollup/plugin-node-resolve'
15
import typescript from '@rollup/plugin-typescript'
26
import multiInput from 'rollup-plugin-multi-input'
3-
import image from '@rollup/plugin-image'
47

58
/**
69
* @type {import('rollup').RollupOptions}
710
*/
811
export default {
912
input: ['./src/**/*.ts', '!./src/**/*.test.ts', '!./src/renderer', '!./src/data'],
1013
output: { dir: './dist/esm', format: 'esm', sourcemap: true },
11-
plugins: [typescript({ tsconfig: './tsconfig.json' }), multiInput(), image()],
12-
external: ['konva', 'rxjs'],
14+
plugins: [
15+
resolve(),
16+
commomjs(),
17+
typescript({ typescript: ttypescript, tsconfig: './tsconfig.json' }),
18+
multiInput(),
19+
image(),
20+
],
21+
external: ['konva', 'bluebird'],
1322
}

src/actions/align.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { applyMove } from '@api/index'
2+
import Konva from 'konva'
3+
import { Group } from 'konva/lib/Group'
4+
import { Layer } from 'konva/lib/Layer'
5+
import { Shape, ShapeConfig } from 'konva/lib/Shape'
6+
import { Stage } from 'konva/lib/Stage'
7+
import { getDraftLayer } from '../helpers/draft'
8+
import { ShapeOrGroup } from './helper'
9+
import { select } from './select'
10+
type IAlignTarget = Stage | Shape<ShapeConfig> | Group
11+
const helpLines: Shape[] = []
12+
13+
export interface AlignOptions {
14+
firstNode?: ShapeOrGroup
15+
secondNode?: ShapeOrGroup
16+
lockX?: boolean
17+
lockY?: boolean
18+
alignTo?: 'wall' | 'wallCenter' | 'core' | 'coreCenter'
19+
}
20+
21+
export const align = (layer: Layer, options = {} as AlignOptions) => {
22+
const { lockX, lockY = true } = options
23+
const stage = layer.getStage() as Stage
24+
const draftLayer = getDraftLayer(stage)
25+
draftLayer.visible(true)
26+
// clearLines()
27+
28+
return select(layer, { useDrag: false })
29+
.then(nodes => {
30+
const node = nodes[0]
31+
node.opacity(1)
32+
const name = node.name()
33+
node.addName('unselectable')
34+
drawHelpLine(node)
35+
return { firstNode: node, firstNodeName: name }
36+
})
37+
.then(result => {
38+
return select(layer, { useDrag: false }).then(nodes => {
39+
const secondNode = nodes[0]
40+
const name = secondNode.name()
41+
secondNode.opacity(1)
42+
secondNode.addName('unselectable')
43+
return { ...result, secondNode, secondNodeName: name }
44+
})
45+
})
46+
.then(({ firstNode, firstNodeName, secondNode, secondNodeName }) => {
47+
const dest = {
48+
x: lockX ? 0 : firstNode.getClientRect().x - secondNode.getClientRect().x,
49+
y: lockY ? 0 : firstNode.getClientRect().y - secondNode.getClientRect().y,
50+
}
51+
applyMove([secondNode], dest)
52+
53+
clearLines()
54+
draftLayer.visible(false)
55+
firstNode.name(firstNodeName)
56+
secondNode.name(secondNodeName)
57+
58+
return dest
59+
})
60+
}
61+
62+
function clearLines() {
63+
while (helpLines.length) {
64+
helpLines.pop()?.destroy()
65+
}
66+
}
67+
function drawHelpLine(target: IAlignTarget) {
68+
//画辅助线
69+
const stage = target.getStage()
70+
if (!target?.attrs || !stage) return
71+
const draftLayer = getDraftLayer(stage)
72+
const { id, x, y } = target.attrs
73+
const config = {
74+
id: 'help' + id,
75+
x: x,
76+
y: y,
77+
strokeWidth: 1,
78+
points: [] as number[],
79+
dash: [10, 10],
80+
stroke: 'rgba(0,81,255,0.5)',
81+
}
82+
if (target.width() > target.height()) {
83+
//横向
84+
config.x = 0
85+
config.points = [0, 3000, 0, 0]
86+
} else {
87+
//纵向
88+
config.y = 0
89+
config.points = [0, 0, 0, 3000]
90+
}
91+
const line: Konva.Shape = new Konva.Line(config)
92+
draftLayer.add(line)
93+
draftLayer.visible(true)
94+
helpLines.push(line)
95+
}

src/actions/copy.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import Bluebird from 'bluebird'
2+
import Konva from 'konva'
3+
import { Layer } from 'konva/lib/Layer'
4+
import { move } from './move'
5+
import { SelectOptions } from './select'
6+
7+
export interface CopyOptions extends SelectOptions {
8+
copyResult?: boolean //是否拷贝结果
9+
lockX?: boolean //纵向移动
10+
lockY?: boolean //横向移动
11+
showCompass?: boolean //是否显示角度
12+
pixelRatio?: number
13+
}
14+
export const copy = (layer: Layer, config = {} as CopyOptions) => {
15+
const { showCompass = true, lockY = false, lockX = false, copyResult = true, pixelRatio, useSelectedNode } = config
16+
17+
return move(layer, { showCompass, lockY, lockX, pixelRatio, useSelectedNode })
18+
.then(result => {
19+
if (!copyResult) {
20+
return Bluebird.reject('pass')
21+
}
22+
return result
23+
})
24+
.then(result => {
25+
const { nodes, x, y } = result
26+
const node = nodes[0] as Konva.Shape
27+
const clone = node.clone()
28+
const layer = node.getLayer()
29+
if (!layer) {
30+
return Bluebird.reject('no layer')
31+
}
32+
clone.x(clone.x() - x)
33+
clone.y(clone.y() - y)
34+
clone.id(node.id() + '_clone')
35+
36+
if (!copyResult) {
37+
layer.add(clone)
38+
}
39+
40+
return clone
41+
})
42+
}

src/actions/drag-select.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
import Konva from 'konva'
22
import { Layer } from 'konva/lib/Layer'
3-
import { closestSelectable, isSelectable, isShape, listenOn, useEventTarget, usePoinerPosition } from './helper'
4-
import { input } from './input'
3+
import {
4+
closestSelectable,
5+
isSelectable,
6+
isShape,
7+
listenOn,
8+
ShapeOrGroup,
9+
useEventTarget,
10+
usePoinerPosition,
11+
} from './helper'
12+
import { mouseInput } from './input'
513

614
export const dragSelect = async (layer: Layer) => {
715
const stage = layer.getStage()
8-
const startPoint = await input(stage, 'mousedown').then(useEventTarget).then(usePoinerPosition)
16+
const startPoint = await mouseInput(stage, 'mousedown').then(useEventTarget).then(usePoinerPosition)
917

1018
const selectionRect = new Konva.Rect({
1119
...startPoint,
@@ -27,13 +35,13 @@ export const dragSelect = async (layer: Layer) => {
2735
})
2836

2937
// cancel if drag move event happens on stage.
30-
input(stage, 'dragmove').then(() => {
38+
mouseInput(stage, 'dragmove').then(() => {
3139
stopMove()
3240
selectionRect.destroy()
3341
clearTimeout(delayId)
3442
})
3543

36-
const mouseupEvent = await input(stage, 'mouseup').then(stopMove)
44+
const mouseupEvent = await mouseInput(stage, 'mouseup').then(stopMove)
3745

3846
const shapes = layer.children ?? []
3947
const clientConfig = { skipShadow: true, skipStroke: true }
@@ -50,7 +58,9 @@ export const dragSelect = async (layer: Layer) => {
5058
return []
5159
}
5260

53-
return shapes
61+
const result = shapes
5462
.filter(isSelectable)
5563
.filter(shape => Konva.Util.haveIntersection(box, shape.getClientRect(clientConfig)))
64+
65+
return result as ShapeOrGroup[]
5666
}

0 commit comments

Comments
 (0)