camera-plugin.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. import { Matrix4, Vector3 } from "three";
  2. import { Container } from "../packages";
  3. import { openShapeDrag } from "../shared";
  4. import mitt from "mitt";
  5. export type CameraQueryPluginProps = {
  6. move?: boolean;
  7. wheel?: boolean;
  8. bound?: number[];
  9. padding?: number | number[];
  10. size?: number[];
  11. };
  12. export class CameraQueryPlugin {
  13. bound: number[];
  14. padding: number[];
  15. tree: Container;
  16. props: Omit<CameraQueryPluginProps, "bound">;
  17. cameraMat: Matrix4;
  18. clipMat: Matrix4 = new Matrix4();
  19. bus = mitt<{ cameraChange: number[] }>();
  20. constructor(props: CameraQueryPluginProps = {}) {
  21. this.props = props;
  22. this.props.move = props.move || false;
  23. this.props.wheel = props.wheel || false;
  24. this.cameraMat = new Matrix4();
  25. if (props.size) {
  26. this.setSize(props.size[0], props.size[1]);
  27. }
  28. if (props.bound) {
  29. this.setBound(props.bound, props.padding);
  30. }
  31. }
  32. private dragDestory?: () => void;
  33. disableMove() {
  34. this.props.move = false;
  35. this.dragDestory && this.dragDestory();
  36. }
  37. enableMove() {
  38. this.disableMove();
  39. this.props.move = true;
  40. if (!this.tree) return;
  41. const { stage } = this.tree;
  42. this.dragDestory = openShapeDrag(
  43. stage,
  44. {
  45. readyHandler: () => this.cameraMat.clone(),
  46. moveHandler: (move, initMat) => {
  47. const mat = this.clipMat.clone().multiply(initMat).invert();
  48. const v2 = new Vector3(move[0], move[1], 0).applyMatrix4(mat);
  49. this.move([v2.x, v2.y], initMat);
  50. },
  51. },
  52. false
  53. );
  54. }
  55. private wheelDestory?: () => void;
  56. disableWheel() {
  57. this.props.wheel = false;
  58. this.wheelDestory && this.wheelDestory();
  59. }
  60. enableWheel() {
  61. this.disableWheel();
  62. this.props.wheel = true;
  63. if (!this.tree) return;
  64. const {
  65. config: { dom },
  66. } = this.tree;
  67. const whellHandler = (ev: WheelEvent) => {
  68. const scale = 1 - ev.deltaY / 1000;
  69. const center = new Vector3(ev.offsetX, ev.offsetY, 0).applyMatrix4(
  70. this.clipMat.clone().multiply(this.cameraMat).invert()
  71. );
  72. this.scale([center.x, center.y], scale);
  73. };
  74. dom.addEventListener("wheel", whellHandler);
  75. this.wheelDestory = () => dom.removeEventListener("wheel", whellHandler);
  76. }
  77. move(movePosition: number[], initMat = this.cameraMat) {
  78. this.mutMat(
  79. new Matrix4().setPosition(movePosition[0], movePosition[1], 0),
  80. initMat
  81. );
  82. }
  83. scale(center: number[], scale: number, initMat = this.cameraMat) {
  84. this.mutMat(
  85. new Matrix4()
  86. .makeTranslation(center[0], center[1], 0)
  87. .multiply(
  88. new Matrix4()
  89. .makeScale(scale, scale, 1)
  90. .multiply(new Matrix4().makeTranslation(-center[0], -center[1], 0))
  91. ),
  92. initMat
  93. );
  94. }
  95. rotate(center: number[], angleRad: number, initMat = this.cameraMat) {
  96. this.mutMat(
  97. new Matrix4()
  98. .makeTranslation(center[0], center[1], 0)
  99. .multiply(
  100. new Matrix4()
  101. .makeRotationAxis(new Vector3(0, 0, 1), angleRad)
  102. .multiply(new Matrix4().makeTranslation(-center[0], -center[1], 0))
  103. ),
  104. initMat
  105. );
  106. }
  107. mutMat(mat: number[] | Matrix4, initMat = this.cameraMat) {
  108. if (mat instanceof Matrix4) {
  109. initMat = initMat.clone().multiply(mat);
  110. } else {
  111. initMat = initMat.multiply(new Matrix4().fromArray(mat));
  112. }
  113. this.setCameraMat(initMat);
  114. this.bus.emit("cameraChange", this.cameraMat.toArray());
  115. }
  116. setCameraMat(mat: number[] | Matrix4) {
  117. if (mat instanceof Matrix4) {
  118. this.cameraMat = mat;
  119. } else {
  120. this.cameraMat.fromArray(mat);
  121. }
  122. this.update();
  123. }
  124. autoBound(padding?: number | number[], stageNames = [".adsord-point"]) {
  125. const positions = stageNames.flatMap((item) =>
  126. this.tree.stage.find(item).map((s) => {
  127. return s.position();
  128. })
  129. );
  130. if (positions.length < 2) return;
  131. const bound = [
  132. Number.MAX_VALUE,
  133. Number.MAX_VALUE,
  134. -Number.MAX_VALUE,
  135. -Number.MAX_VALUE,
  136. ];
  137. for (const position of positions) {
  138. bound[0] = Math.min(bound[0], position.x);
  139. bound[1] = Math.min(bound[1], position.y);
  140. bound[2] = Math.max(bound[2], position.x);
  141. bound[3] = Math.max(bound[3], position.y);
  142. }
  143. this.setBound(bound, padding);
  144. }
  145. setBound(bound: number[], padding?: number | number[]) {
  146. padding = !Array.isArray(padding) ? [padding || 0, padding || 0] : padding;
  147. if (padding.length === 1) {
  148. padding.push(padding[0]);
  149. }
  150. const realWidth = bound[2] - bound[0];
  151. const realHeight = bound[3] - bound[1];
  152. const screenWidth = this.tree.stage.width();
  153. const screenHeight = this.tree.stage.height();
  154. // testPoint(this.tree.stage, [
  155. // [bound[0], bound[1]],
  156. // [bound[2], bound[3]],
  157. // ]);
  158. // 计算包含padding的新屏幕尺寸
  159. const effectiveWidth = this.tree.stage.width() - padding[0] * 2;
  160. const effectiveHeight = this.tree.stage.height() - padding[1] * 2;
  161. // 计算缩放比例
  162. const scaleX = effectiveWidth / realWidth;
  163. const scaleY = effectiveHeight / realHeight;
  164. const scale = Math.min(scaleX, scaleY); // 选择较小的比例以保持内容比例
  165. const offsetX = (screenWidth - realWidth * scale) / 2 - bound[0] * scale;
  166. const offsetY = (screenHeight - realHeight * scale) / 2 - bound[1] * scale;
  167. // 创建矩阵并应用缩放
  168. const matrix = new Matrix4()
  169. .scale(new Vector3(scale, scale, 1))
  170. .setPosition(offsetX, offsetY, 0);
  171. this.bound = bound;
  172. this.padding = padding;
  173. this.clipMat = matrix;
  174. this.update();
  175. }
  176. setSize(width: number, height: number) {
  177. this.tree.stage.width(width);
  178. this.tree.stage.height(height);
  179. if (this.bound) {
  180. this.setBound(this.bound, this.padding);
  181. }
  182. }
  183. setTree(tree: Container) {
  184. this.tree = tree;
  185. if (this.props.move) {
  186. this.enableMove();
  187. }
  188. if (this.props.wheel) {
  189. this.enableWheel();
  190. }
  191. }
  192. update() {
  193. this.tree.updateViewMat(this.clipMat.clone().multiply(this.cameraMat));
  194. }
  195. }