use-transformer.ts 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  1. import { useMouseShapeStatus } from "./use-mouse-status.ts";
  2. import { computed, nextTick, Ref, ref, toRaw, watch, watchEffect } from "vue";
  3. import { DC, EntityShape } from "../../deconstruction";
  4. import {
  5. installGlobalVar,
  6. usePointerIntersections,
  7. useStage,
  8. useTransformIngShapes,
  9. } from "./use-global-vars.ts";
  10. import { useCan, useMode } from "./use-status";
  11. import { Mode } from "../../constant/mode.ts";
  12. import { Transform } from "konva/lib/Util";
  13. import { Pos, vector } from "@/utils/math.ts";
  14. import { useConversionPosition } from "./use-coversion-position.ts";
  15. import { getOffset, listener } from "@/utils/event.ts";
  16. import {
  17. debounce,
  18. flatPositions,
  19. mergeFuns,
  20. round,
  21. } from "@/utils/shared.ts";
  22. import { Line } from "konva/lib/shapes/Line";
  23. import { setShapeTransform } from "@/utils/shape.ts";
  24. import { Transformer } from "../transformer.ts";
  25. import { TransformerConfig } from "konva/lib/shapes/Transformer";
  26. import { themeColor } from "@/constant";
  27. import { useComponentSnap } from "./use-snap.ts";
  28. import {
  29. useViewer,
  30. useViewerInvertTransform,
  31. useViewerTransform,
  32. } from "./use-viewer.ts";
  33. import { Rect } from "konva/lib/shapes/Rect";
  34. import { Text } from "konva/lib/shapes/Text";
  35. import { Group } from "konva/lib/Group";
  36. import { BaseItem } from "../components/util.ts";
  37. import { useGetComponentData } from "./use-component.ts";
  38. import { usePause } from "./use-pause.ts";
  39. import { getSvgContent, parseSvgContent } from "@/utils/resource.ts";
  40. import { Path } from "konva/lib/shapes/Path";
  41. import { Circle } from "konva/lib/shapes/Circle";
  42. export type TransformerExtends = Transformer & {
  43. queueShapes: Ref<EntityShape[]>;
  44. };
  45. export const useTransformer = installGlobalVar(() => {
  46. const anchorCornerRadius = 5;
  47. const transformer = new Transformer({
  48. borderStrokeWidth: 2,
  49. borderStroke: themeColor,
  50. anchorCornerRadius,
  51. anchorSize: anchorCornerRadius * 2,
  52. borderEnabled: true,
  53. rotationSnaps: [0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330, 360],
  54. rotationSnapTolerance: 3,
  55. anchorStrokeWidth: 2,
  56. anchorStroke: themeColor,
  57. anchorFill: "#fff",
  58. flipEnabled: false,
  59. padding: 0,
  60. useSingleNodeRotation: true,
  61. }) as TransformerExtends;
  62. transformer.queueShapes = ref([]);
  63. transformer.on("transformstart.attachText", (e) => {
  64. const operateType = transformer.getActiveAnchor() as TransformerVectorType;
  65. if (operateType !== "rotater") return;
  66. const $node = transformer.findOne<Rect>(".rotater")!;
  67. const $g = new Group();
  68. const $text = new Text({
  69. listening: false,
  70. fill: themeColor,
  71. fontSize: 6,
  72. width: 100,
  73. align: "center",
  74. offset: { x: 50, y: 0 },
  75. });
  76. $g.add($text);
  77. $node.parent!.add($g);
  78. setShapeTransform($g, $node.getTransform());
  79. $g.y($g.y() - 4 * $text.fontSize());
  80. const updateText = () => {
  81. const rotation = (transformer.rotation() + 360) % 360;
  82. $text.rotation(-rotation).text(` ${round(rotation, 1)}°`);
  83. };
  84. updateText();
  85. transformer.on("transform.attachText", updateText);
  86. transformer.on("transformend.attachText", () => {
  87. transformer.off("transform.attachText transformend.attachText");
  88. $g.destroy();
  89. });
  90. });
  91. const cleanups: (() => void)[] = [];
  92. const getData = useGetComponentData();
  93. const viewer = useViewer();
  94. getSvgContent("./icons/m_rotate.svg").then((svgContent) => {
  95. const svg = parseSvgContent(svgContent);
  96. const group = new Group({ listening: false });
  97. const rotateRect = transformer.findOne<Rect>(".rotater")!;
  98. const size = 8;
  99. rotateRect.scale({ x: 2, y: 2 });
  100. const bg = new Circle({ radius: 6, fill: themeColor, x: 0.5, y: 0.5 });
  101. group.add(bg);
  102. svg.paths.forEach((path) => {
  103. const $path = new Path({ ...path, fill: "#ffffff" });
  104. const scale = size / svg.width;
  105. $path.scale({ x: scale, y: scale });
  106. $path.offset({ x: svg.width / 2, y: svg.height / 2 });
  107. group.add($path);
  108. });
  109. rotateRect.opacity(0);
  110. rotateRect.parent!.add(group);
  111. const update = async () => {
  112. await nextTick();
  113. setShapeTransform(group, rotateRect.getTransform());
  114. group.x(group.x() + 8);
  115. group.y(group.y() + 8);
  116. group.visible(rotateRect.visible());
  117. };
  118. viewer.viewer.bus.on("transformChange", update);
  119. const stopShapeJoin = watch(
  120. () => transformer.queueShapes.value[0],
  121. (shape, _b, onCleanup) => {
  122. if (shape) {
  123. const data = getData(computed(() => shape));
  124. update();
  125. shape.on("bound-change", update);
  126. onCleanup(() => shape.off("bound-change", update));
  127. onCleanup(watch(data, update));
  128. }
  129. },
  130. { immediate: true }
  131. );
  132. cleanups.push(
  133. watch(() => transformer.queueShapes.value, update, { flush: "sync" }),
  134. () => viewer.viewer.bus.off("transformChange", update),
  135. stopShapeJoin
  136. );
  137. });
  138. return {
  139. var: transformer,
  140. onDestroy: () => mergeFuns(cleanups)(),
  141. };
  142. }, Symbol("transformer"));
  143. export const usePointerIsTransformerInner = () => {
  144. const transformer = useTransformer();
  145. const stage = useStage();
  146. const hitShapes = usePointerIntersections();
  147. return () => {
  148. const $stage = stage.value!.getStage();
  149. const pos = $stage.pointerPos;
  150. if (!pos) return false;
  151. if (!hitShapes.value.length) return false;
  152. const selfShapes = [
  153. ...transformer.children,
  154. ...transformer.queueShapes.value.map(toRaw),
  155. ] as any;
  156. for (const shape of hitShapes.value) {
  157. if (selfShapes.includes(shape)) return true;
  158. }
  159. return false;
  160. };
  161. };
  162. export type ScaleVectorType =
  163. | "middle-left"
  164. | "middle-right"
  165. | "top-center"
  166. | "bottom-center"
  167. | "top-right"
  168. | "top-left"
  169. | "bottom-right"
  170. | "bottom-left";
  171. export type TransformerVectorType = ScaleVectorType | "rotater";
  172. export const useGetTransformerOperType = () => {
  173. const transformer = useTransformer();
  174. return () => {
  175. if (!transformer.nodes().length) return undefined;
  176. return transformer.getActiveAnchor() as TransformerVectorType;
  177. };
  178. };
  179. export const useGetTransformerVectors = () => {
  180. const viewerInvertTransform = useViewerInvertTransform();
  181. const transformer = useTransformer();
  182. return (type: TransformerVectorType): Pos | null => {
  183. if (!transformer.nodes().length) return null;
  184. const merTransform = viewerInvertTransform.value
  185. .copy()
  186. .multiply(transformer.getTransform());
  187. const getVector = (operateType: TransformerVectorType): Pos => {
  188. if (operateType === "rotater") {
  189. return vector(getVector("bottom-left"))
  190. .add(getVector("bottom-right"))
  191. .add(getVector("top-left"))
  192. .add(getVector("top-right"))
  193. .divideScalar(4);
  194. } else {
  195. const centerNode = transformer.findOne(`.${operateType}`)!;
  196. return {
  197. x: centerNode.x(),
  198. y: centerNode.y(),
  199. };
  200. }
  201. };
  202. return merTransform.point(getVector(type));
  203. };
  204. };
  205. export const useGetTransformerOperDirection = () => {
  206. const getTransformerOperType = useGetTransformerOperType();
  207. const getTransformerVectors = useGetTransformerVectors();
  208. const originTypeMap = {
  209. "middle-left": "middle-right",
  210. "middle-right": "middle-left",
  211. "top-center": "bottom-center",
  212. "bottom-center": "top-center",
  213. "top-right": "bottom-left",
  214. "top-left": "bottom-right",
  215. "bottom-right": "top-left",
  216. "bottom-left": "top-right",
  217. rotater: "rotater",
  218. } as const;
  219. return () => {
  220. const operateType = getTransformerOperType();
  221. if (!operateType || !originTypeMap[operateType]) return null;
  222. const origin = getTransformerVectors(originTypeMap[operateType]);
  223. const operTarget = getTransformerVectors(operateType);
  224. return origin && operTarget && ([origin, operTarget] as const);
  225. };
  226. };
  227. export const useShapeDrag = (shape: Ref<DC<EntityShape> | undefined>) => {
  228. const offset = ref<Pos>();
  229. const mode = useMode();
  230. const can = useCan();
  231. const conversion = useConversionPosition(true);
  232. const transformIngShapes = useTransformIngShapes();
  233. const status = useMouseShapeStatus(shape);
  234. const init = (shape: EntityShape) => {
  235. const dom = shape.getStage()!.container();
  236. let start: Pos | undefined;
  237. const enter = (position: Pos) => {
  238. mode.push(Mode.update);
  239. if (!start) {
  240. start = position;
  241. if (!can.dragMode) return;
  242. mode.add(Mode.draging);
  243. transformIngShapes.value.push(shape);
  244. }
  245. };
  246. const leave = () => {
  247. if (start) {
  248. offset.value = void 0;
  249. mode.pop();
  250. start = void 0;
  251. const ndx = transformIngShapes.value.indexOf(shape);
  252. ~ndx && transformIngShapes.value.splice(ndx, 1);
  253. }
  254. };
  255. shape.draggable(true);
  256. shape.dragBoundFunc((_, ev) => {
  257. if (!start) {
  258. return shape.absolutePosition();
  259. } else if (can.dragMode) {
  260. const end = conversion(getOffset(ev, dom));
  261. offset.value = {
  262. x: end.x - start.x,
  263. y: end.y - start.y,
  264. };
  265. }
  266. return shape.absolutePosition();
  267. });
  268. shape.on("pointerdown.mouse-drag", (ev) => {
  269. if (ev.evt.button !== 0) return;
  270. enter(conversion(getOffset(ev.evt)));
  271. });
  272. return mergeFuns([
  273. () => {
  274. shape.draggable(false);
  275. shape.off("pointerdown.mouse-drag");
  276. start && leave();
  277. },
  278. listener(document.documentElement, "pointerup", (ev) => {
  279. if (ev.button !== 0) return;
  280. start && leave();
  281. }),
  282. ]);
  283. };
  284. const result = usePause(offset);
  285. watch(
  286. () =>
  287. (can.editMode || mode.include(Mode.update)) &&
  288. (status.value.active || status.value.press || status.value.hover) &&
  289. !result.isPause,
  290. (canEdit, _, onCleanup) => {
  291. canEdit && onCleanup(init(shape.value!.getNode()));
  292. },
  293. { immediate: true }
  294. );
  295. return result;
  296. };
  297. type Rep<T> = {
  298. tempShape: T;
  299. init?: () => void;
  300. update?: () => void;
  301. destory: () => void;
  302. };
  303. const emptyFn = () => {};
  304. export const useShapeTransformer = <T extends EntityShape>(
  305. shape: Ref<DC<T> | undefined>,
  306. transformerConfig: TransformerConfig = {},
  307. replaceShape?: (shape: T) => Rep<T>,
  308. handlerTransform?: (transform: Transform) => Transform
  309. ) => {
  310. const offset = useShapeDrag(shape);
  311. const transform = ref<Transform>();
  312. const status = useMouseShapeStatus(shape);
  313. const getData = useGetComponentData();
  314. const mode = useMode();
  315. const transformer = useTransformer();
  316. const transformIngShapes = useTransformIngShapes();
  317. const viewTransform = useViewerTransform();
  318. const can = useCan();
  319. const init = ($shape: T) => {
  320. let isRun = false;
  321. let rep: Rep<T>;
  322. if (replaceShape) {
  323. rep = replaceShape($shape);
  324. } else {
  325. rep = {
  326. tempShape: $shape,
  327. destory: emptyFn,
  328. update: emptyFn,
  329. };
  330. }
  331. let selfFire = false;
  332. const set = (appleTransform: Transform | undefined) => {
  333. transform.value = appleTransform;
  334. selfFire = true;
  335. $shape.fire("bound-change");
  336. selfFire = false;
  337. };
  338. const updateTransform = () => {
  339. if (!can.dragMode) return;
  340. let appleTransform = rep.tempShape.getTransform().copy();
  341. if (appleTransform.m.some((m) => m === null || Number.isNaN(m))) {
  342. return;
  343. }
  344. if (handlerTransform) {
  345. appleTransform = handlerTransform(appleTransform);
  346. setShapeTransform(rep.tempShape, appleTransform);
  347. }
  348. set(appleTransform);
  349. };
  350. rep.tempShape.on("transform.shapemer", updateTransform);
  351. const rect = ref(rep.tempShape.getClientRect());
  352. const boundHandler = () => {
  353. if (!selfFire) {
  354. rep.update && rep.update();
  355. // transformer.forceUpdate()
  356. }
  357. rect.value = rep.tempShape.getClientRect();
  358. };
  359. $shape.on("bound-change", boundHandler);
  360. // 拖拽时要更新矩阵
  361. let prevMoveTf: Transform | null = null;
  362. const stopDragWatch = watch(
  363. offset,
  364. (translate, oldTranslate) => {
  365. if (translate) {
  366. if (!oldTranslate) {
  367. isRun = true;
  368. rep.init && rep.init();
  369. rep.update && rep.update();
  370. }
  371. const moveTf = new Transform().translate(translate.x, translate.y);
  372. const finalTf = moveTf.copy();
  373. prevMoveTf && finalTf.multiply(prevMoveTf.invert());
  374. finalTf.multiply(rep.tempShape.getTransform());
  375. prevMoveTf = moveTf;
  376. setShapeTransform(rep.tempShape, finalTf);
  377. rep.tempShape.fire("transform");
  378. } else {
  379. prevMoveTf = null;
  380. transform.value = void 0;
  381. isRun = false;
  382. }
  383. },
  384. { immediate: true }
  385. );
  386. const data = getData(computed(() => $shape));
  387. const stopTransformerWatch = watch(
  388. () =>
  389. status.value.active &&
  390. !data.value?.disableTransformer &&
  391. rect.value.width > 0.5 &&
  392. rect.value.height > 0.5,
  393. (active, _, onCleanup) => {
  394. const parent = $shape.parent;
  395. if (!active || !parent) return;
  396. const oldConfig: TransformerConfig = {};
  397. for (const key in transformerConfig) {
  398. oldConfig[key] = (transformer as any)[key]();
  399. (transformer as any)[key](transformerConfig[key]);
  400. }
  401. transformer.nodes([rep.tempShape]);
  402. transformer.queueShapes.value = [$shape];
  403. parent.add(transformer);
  404. rep.init && rep.init();
  405. let isEnter = false;
  406. const downHandler = () => {
  407. isRun = true;
  408. if (isEnter) {
  409. mode.pop();
  410. }
  411. mode.push(Mode.update);
  412. isEnter = true;
  413. if (!can.dragMode) return;
  414. rep.update && rep.update();
  415. mode.add(Mode.draging);
  416. transformIngShapes.value.push($shape);
  417. };
  418. transformer.on("pointerdown.shapemer", downHandler);
  419. const stopPointupListener = listener(
  420. document.documentElement,
  421. "pointerup",
  422. () => {
  423. isRun = false;
  424. if (isEnter) {
  425. mode.pop();
  426. transform.value = void 0;
  427. isEnter = false;
  428. const ndx = transformIngShapes.value.indexOf($shape);
  429. ~ndx && transformIngShapes.value.splice(ndx, 1);
  430. }
  431. }
  432. );
  433. const stopTransformerForceUpdate = watch(
  434. viewTransform,
  435. () => transformer.forceUpdate(),
  436. { flush: "post" }
  437. );
  438. const stopLeaveUpdate = watch(
  439. data,
  440. debounce(() => rep.update && rep.update(), 16),
  441. { flush: "post", deep: true }
  442. );
  443. onCleanup(() => {
  444. try {
  445. for (const key in oldConfig) {
  446. (transformer as any)[key](oldConfig[key]);
  447. }
  448. } catch (e) {
  449. console.error("页面销毁?");
  450. console.error(e);
  451. }
  452. stopTransformerForceUpdate();
  453. stopPointupListener();
  454. stopLeaveUpdate();
  455. // TODO: 有可能transformer已经转移
  456. if (transformer.queueShapes.value.includes($shape)) {
  457. transformer.nodes([]);
  458. transformer.queueShapes.value = [];
  459. }
  460. transform.value = void 0;
  461. if (isEnter) {
  462. mode.pop();
  463. const ndx = transformIngShapes.value.indexOf($shape);
  464. ~ndx && transformIngShapes.value.splice(ndx, 1);
  465. }
  466. transformer.off("pointerdown.shapemer", downHandler);
  467. });
  468. },
  469. { immediate: true, flush: "post" }
  470. );
  471. return () => {
  472. $shape.off("bound-change", boundHandler);
  473. rep.tempShape.off("transform.shapemer", updateTransform);
  474. stopDragWatch();
  475. stopTransformerWatch();
  476. rep.destory();
  477. };
  478. };
  479. watch(
  480. () => shape.value,
  481. (shape, _, onCleanup) => {
  482. if (!shape) return;
  483. onCleanup(
  484. watch(
  485. () =>
  486. (can.editMode || mode.include(Mode.update)) &&
  487. (status.value.active || status.value.hover || status.value.press),
  488. (canEdit, _, onCleanup) => {
  489. if (canEdit) {
  490. const stop = init(shape.getNode());
  491. onCleanup(stop);
  492. } else {
  493. onCleanup(() => {});
  494. }
  495. },
  496. { immediate: true }
  497. )
  498. );
  499. }
  500. );
  501. return transform;
  502. };
  503. export const cloneRepShape = <T extends EntityShape>(
  504. shape: T
  505. ): ReturnType<GetRepShape<T, any>> => {
  506. shape =
  507. ((shape as Group)?.findOne &&
  508. ((shape as Group)?.findOne(".repShape") as T)) ||
  509. shape;
  510. return {
  511. shape: shape.clone({
  512. fill: themeColor,
  513. visible: false,
  514. strokeWidth: 0,
  515. }),
  516. update: (_, rep) => {
  517. setShapeTransform(rep, shape.getTransform());
  518. },
  519. };
  520. };
  521. export const transformerRepShapeHandler = <T extends EntityShape>(
  522. shape: T,
  523. repShape: T
  524. ) => {
  525. if (import.meta.env.DEV) {
  526. repShape.visible(true);
  527. repShape.opacity(0.1);
  528. }
  529. shape.parent!.add(repShape);
  530. repShape.zIndex(shape.getZIndex());
  531. repShape.listening(false);
  532. shape.repShape = repShape;
  533. return [
  534. repShape,
  535. () => {
  536. shape.repShape = undefined;
  537. repShape.remove();
  538. },
  539. ] as const;
  540. };
  541. type GetRepShape<T extends EntityShape, K extends object> = (shape: T) => {
  542. shape: T;
  543. update?: (data: K, shape: T) => void;
  544. init?: (data: K, shape: T) => void;
  545. destory?: () => void;
  546. };
  547. export type CustomTransformerProps<
  548. T extends BaseItem,
  549. S extends EntityShape
  550. > = {
  551. openSnap?: boolean;
  552. getRepShape?: GetRepShape<S, T>;
  553. start?: () => void;
  554. beforeHandler?: (data: T, mat: Transform) => T;
  555. handler?: (
  556. data: T,
  557. mat: Transform,
  558. raw?: Transform
  559. ) => Transform | void | boolean;
  560. callback?: (data: T, mat: Transform) => void;
  561. transformerConfig?: TransformerConfig;
  562. };
  563. export const useCustomTransformer = <T extends BaseItem, S extends EntityShape>(
  564. shape: Ref<DC<S> | undefined>,
  565. data: Ref<T>,
  566. props: CustomTransformerProps<T, S>
  567. ) => {
  568. const { getRepShape, handler, callback, openSnap, transformerConfig } = props;
  569. const needSnap = openSnap && useComponentSnap(data.value.id);
  570. const transformer = useTransformer();
  571. let repResult: ReturnType<GetRepShape<S, T>>;
  572. const transform = useShapeTransformer(
  573. shape,
  574. transformerConfig,
  575. getRepShape &&
  576. ((shape) => {
  577. repResult = getRepShape(shape);
  578. const [_, destory] = transformerRepShapeHandler(shape, repResult.shape);
  579. return {
  580. tempShape: repResult.shape,
  581. update: () => {
  582. repResult.update && repResult.update(data.value, repResult.shape);
  583. },
  584. init: () => {
  585. repResult.init && repResult.init(data.value, repResult.shape);
  586. },
  587. destory: () => {
  588. repResult.destory && repResult.destory();
  589. destory();
  590. },
  591. };
  592. })
  593. );
  594. let callMat: Transform;
  595. watch(transform, (current, oldTransform) => {
  596. if (!oldTransform) {
  597. props.start && props.start();
  598. }
  599. if (current) {
  600. if (!handler) return;
  601. const snapData = props.beforeHandler
  602. ? props.beforeHandler(data.value, current)
  603. : data.value;
  604. let nTransform;
  605. const raw = current;
  606. if (needSnap && (nTransform = needSnap[0](snapData))) {
  607. current = nTransform.multiply(current);
  608. }
  609. callMat = current;
  610. const mat = handler(data.value, current, raw);
  611. if (mat) {
  612. if (repResult.update) {
  613. repResult.update(data.value, repResult.shape);
  614. } else if (mat !== true) {
  615. setShapeTransform(repResult.shape, mat);
  616. callMat = mat;
  617. }
  618. transformer.forceUpdate();
  619. }
  620. } else if (oldTransform) {
  621. needSnap && needSnap[1]();
  622. callback && callback(data.value, callMat);
  623. }
  624. });
  625. return transform;
  626. };
  627. export type LineTransformerData = BaseItem & {
  628. points: Pos[];
  629. attitude: number[];
  630. };
  631. export const useLineTransformer = <T extends LineTransformerData>(
  632. shape: Ref<DC<Line> | undefined>,
  633. data: Ref<T>,
  634. callback: (data: T) => void,
  635. genRepShape?: ($shape: Line) => Line
  636. ) => {
  637. let inverAttitude: Transform;
  638. let stableVs = data.value.points;
  639. let tempVs = data.value.points;
  640. const transformer = useTransformer();
  641. useCustomTransformer(shape, data, {
  642. openSnap: true,
  643. beforeHandler(data, mat) {
  644. const transfrom = mat.copy().multiply(inverAttitude);
  645. return {
  646. ...data,
  647. points: stableVs.map((v) => transfrom.point(v)),
  648. };
  649. },
  650. handler(data, mat) {
  651. // 顶点更新
  652. const transfrom = mat.copy().multiply(inverAttitude);
  653. data.points = tempVs = stableVs.map((v) => transfrom.point(v));
  654. data.attitude = mat.m;
  655. },
  656. callback(data, mat) {
  657. data.attitude = mat.m;
  658. data.points = stableVs = tempVs;
  659. callback(data);
  660. },
  661. getRepShape($shape) {
  662. let repShape: Line;
  663. if (genRepShape) {
  664. repShape = genRepShape($shape);
  665. } else {
  666. repShape = cloneRepShape($shape).shape;
  667. }
  668. repShape = (repShape as any).points
  669. ? repShape
  670. : (repShape as unknown as Group).findOne<Line>(".line")!;
  671. const update = (data: T) => {
  672. const attitude = new Transform(data.attitude);
  673. const inverMat = attitude.copy().invert();
  674. setShapeTransform(repShape, attitude);
  675. const initVs = data.points.map((v) => inverMat.point(v));
  676. stableVs = tempVs = data.points;
  677. repShape.points(flatPositions(initVs));
  678. repShape.closed(true);
  679. inverAttitude = inverMat;
  680. transformer.forceUpdate();
  681. };
  682. update(data.value);
  683. return {
  684. update,
  685. shape: repShape,
  686. };
  687. },
  688. });
  689. };
  690. export const useMatCompTransformer = <T extends BaseItem & { mat: number[] }>(
  691. shape: Ref<DC<EntityShape> | undefined>,
  692. data: Ref<T>,
  693. callback: (data: T) => void,
  694. getRepShape = cloneRepShape
  695. ) => {
  696. return useCustomTransformer(shape, data, {
  697. beforeHandler(data, mat) {
  698. return { ...data, mat: mat.m };
  699. },
  700. handler(data, mat) {
  701. data.mat = mat.m;
  702. // return true
  703. },
  704. getRepShape,
  705. callback,
  706. openSnap: false,
  707. });
  708. };