Editor.ts 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. import { ref } from 'vue'
  2. import { TransformControler } from '../demo/lmm/controler/TransformControler'
  3. import { OrbitControler } from '../demo/lmm/controler/OrbitControler'
  4. import { Scene } from '../demo/lmm/core/Scene'
  5. import { Group } from '../demo/lmm/objects/Group'
  6. import { Img2D } from '../demo/lmm/objects/Img2D'
  7. import { Vector2 } from '../demo/lmm/math/Vector2'
  8. import { EventDispatcher } from '../demo/lmm/core/EventDispatcher'
  9. import { SelectObj } from '../demo/lmm/objects/ObjectUtils'
  10. import { Object2D } from '../demo/lmm/objects/Object2D'
  11. class Editor extends EventDispatcher {
  12. /* 编辑器场景 */
  13. editorScene = new Scene()
  14. /* 编辑器中的图案 */
  15. group = new Group()
  16. /* 图案控制器 */
  17. transformControler = new TransformControler()
  18. /* 相机轨道控制器 */
  19. orbitControler = new OrbitControler(this.editorScene.camera)
  20. /* 鼠标划入的图形 */
  21. objHover: Object2D | null = null
  22. /* 鼠标状态 */
  23. cursor = ref('default')
  24. /* 设计尺寸 */
  25. designSize = 0
  26. /* 设计图 */
  27. designImg = new Img2D({
  28. index: 1000,
  29. })
  30. /* 虚拟场景 */
  31. resultScene = new Scene()
  32. /* 虚拟场景里的图案集合 */
  33. resultGroup = new Group()
  34. constructor() {
  35. super()
  36. const {
  37. editorScene,
  38. orbitControler,
  39. group,
  40. transformControler,
  41. designImg,
  42. resultScene,
  43. resultGroup,
  44. } = this
  45. /* 编辑器场景*/
  46. editorScene.add(group, transformControler, designImg)
  47. /* 虚拟场景 */
  48. resultScene.add(resultGroup)
  49. /* 渲染编辑器和虚拟场景 */
  50. transformControler.addEventListener('change', () => {
  51. this.render()
  52. })
  53. orbitControler.addEventListener('change', () => {
  54. this.render()
  55. })
  56. /* 虚拟场景的图案同步编辑器场景的图案 */
  57. // 添加图案
  58. group.addEventListener('add', ({ obj }) => {
  59. if (obj instanceof Img2D) {
  60. const { image, position, rotate, scale, offset, size, uuid } = obj
  61. resultGroup.add(
  62. new Img2D({
  63. image,
  64. position,
  65. rotate,
  66. scale,
  67. offset,
  68. size,
  69. uuid,
  70. })
  71. )
  72. }
  73. })
  74. // 变换图案
  75. transformControler.addEventListener('transformed', ({ obj }) => {
  76. const { position, rotate, scale, offset } = obj
  77. const resultImg = resultGroup.children[group.children.indexOf(obj)]
  78. if (resultImg instanceof Img2D) {
  79. resultImg.setOption({
  80. position,
  81. rotate,
  82. scale,
  83. offset,
  84. })
  85. }
  86. })
  87. // 删除图案
  88. group.addEventListener('remove', ({ obj }) => {
  89. resultGroup.getObjectByProperty('uuid', obj.uuid)?.remove()
  90. })
  91. }
  92. onMounted(editorDom: HTMLDivElement, effectDom: HTMLDivElement) {
  93. const {
  94. editorScene: { canvas },
  95. resultScene: { canvas: resultCanvas },
  96. resultGroup,
  97. } = this
  98. /* 编辑器 */
  99. editorDom.append(canvas)
  100. const { clientWidth: dx, clientHeight: dy } = editorDom
  101. canvas.width = dx
  102. canvas.height = dy
  103. /* 设计尺寸 */
  104. const designSize = Math.min(dx, dy) * 0.5
  105. this.designSize = designSize
  106. /* 编辑器事件监听 */
  107. canvas.addEventListener('pointerdown', this.pointerdown.bind(this))
  108. canvas.addEventListener('pointermove', this.pointermove.bind(this))
  109. window.addEventListener('pointerup', this.pointerup.bind(this))
  110. window.addEventListener('keydown', this.keydown.bind(this))
  111. window.addEventListener('keyup', this.keyup.bind(this))
  112. canvas.addEventListener('wheel', this.wheel.bind(this))
  113. canvas.addEventListener('contextmenu', this.contextmenu.bind(this))
  114. /* 虚拟场景 */
  115. const { clientWidth: fx, clientHeight: fy } = effectDom
  116. resultCanvas.width = fx
  117. resultCanvas.height = fy
  118. resultGroup.setOption({
  119. scale: new Vector2(fx / designSize),
  120. position: new Vector2(0, fx * 0.12),
  121. })
  122. }
  123. onUnmounted() {
  124. const {
  125. editorScene: { canvas },
  126. } = this
  127. /* 删除canvas,避免onMounted时重复添加 */
  128. canvas.remove()
  129. /* 取消事件监听 */
  130. canvas.removeEventListener('pointerdown', this.pointerdown)
  131. canvas.removeEventListener('pointermove', this.pointermove)
  132. window.removeEventListener('pointerup', this.pointerup)
  133. window.removeEventListener('keydown', this.keydown)
  134. window.removeEventListener('keyup', this.keyup)
  135. canvas.removeEventListener('wheel', this.wheel)
  136. canvas.removeEventListener('contextmenu', this.contextmenu)
  137. }
  138. /* 鼠标按下 */
  139. pointerdown(event: PointerEvent) {
  140. const { editorScene, transformControler, group, orbitControler } = this
  141. const { button, clientX, clientY } = event
  142. const mp = editorScene.clientToClip(clientX, clientY)
  143. switch (button) {
  144. case 0:
  145. this.objHover = SelectObj(editorScene)(group.children, mp)
  146. transformControler.pointerdown(this.objHover, mp)
  147. this.updateMouseCursor()
  148. break
  149. case 1:
  150. orbitControler.pointerdown(clientX, clientY)
  151. break
  152. }
  153. }
  154. /* 鼠标移动 */
  155. pointermove(event: PointerEvent) {
  156. const { editorScene, transformControler, group, orbitControler } = this
  157. const { clientX, clientY } = event
  158. const mp = editorScene.clientToClip(clientX, clientY)
  159. orbitControler.pointermove(clientX, clientY)
  160. transformControler.pointermove(mp)
  161. this.objHover = SelectObj(editorScene)(group.children, mp)
  162. this.updateMouseCursor()
  163. }
  164. /* 鼠标抬起 */
  165. pointerup({ button }: PointerEvent) {
  166. switch (button) {
  167. case 0:
  168. this.transformControler.pointerup()
  169. break
  170. case 1:
  171. this.orbitControler.pointerup()
  172. break
  173. }
  174. }
  175. /* 键盘按下 */
  176. keydown({ key, altKey, shiftKey }: KeyboardEvent) {
  177. this.transformControler.keydown(key, altKey, shiftKey)
  178. this.updateMouseCursor()
  179. }
  180. /* 键盘抬起 */
  181. keyup({ altKey, shiftKey }: KeyboardEvent) {
  182. this.transformControler.keyup(altKey, shiftKey)
  183. }
  184. /* 滑动滚轮 */
  185. wheel({ deltaY }: WheelEvent) {
  186. this.orbitControler.doScale(deltaY)
  187. }
  188. /* 取消右键的默认功能 */
  189. contextmenu(event: MouseEvent) {
  190. event.preventDefault()
  191. }
  192. /* 更新鼠标样式 */
  193. updateMouseCursor() {
  194. const { transformControler, cursor, objHover } = this
  195. if (transformControler.mouseState) {
  196. cursor.value = 'none'
  197. } else if (objHover) {
  198. cursor.value = 'pointer'
  199. } else {
  200. cursor.value = 'default'
  201. }
  202. }
  203. /* 设计图和效果图的渲染 */
  204. render() {
  205. this.editorScene.render()
  206. this.resultScene.render()
  207. this.dispatchEvent({ type: 'render' })
  208. }
  209. /* 添加图案 */
  210. addImg(image: HTMLImageElement) {
  211. const {
  212. group: { children },
  213. } = this
  214. /* 图案序号,基于最大序号递增 */
  215. const maxNum = Math.max(...children.map((obj) => obj.layerNum))
  216. const layerNum = (children.length ? maxNum : 0) + 1
  217. /* 建立Img2D对象 */
  218. const img2D = new Img2D({
  219. image,
  220. layerNum,
  221. name: '图层' + layerNum,
  222. })
  223. /* 基于设计尺寸设置图案尺寸 */
  224. this.setImg2DSize(img2D, 0.5)
  225. /* 添加图案 */
  226. this.group.add(img2D)
  227. /* 选择图案 */
  228. this.transformControler.obj = img2D
  229. return img2D
  230. }
  231. /* 设置图案尺寸 */
  232. setImg2DSize(img2D: Img2D, ratio = 1) {
  233. const { designSize } = this
  234. const { width, height } = img2D.image as HTMLImageElement
  235. const w = designSize * ratio
  236. const h = (w * width) / height
  237. img2D.size.set(w, h)
  238. img2D.offset.set(-w / 2, -h / 2)
  239. }
  240. /* 配置设计图 */
  241. setDesignImg(src: string) {
  242. const { designImg, designSize } = this
  243. /* 图案尺寸随设计尺寸而定,位置居中 */
  244. designImg.setOption({
  245. src,
  246. size: new Vector2(designSize),
  247. offset: new Vector2(-designSize / 2),
  248. })
  249. /* 渲染 */
  250. ; (designImg.image as HTMLImageElement).onload = () => {
  251. this.editorScene.render()
  252. }
  253. }
  254. /* 基于uuid 选择图案 */
  255. selectImgByUUID(uuid: string) {
  256. const { group, transformControler } = this
  257. const obj = group.getObjectByProperty('uuid', uuid)
  258. if (obj instanceof Img2D) {
  259. transformControler.obj = obj
  260. }
  261. }
  262. /* 基于图案索引置换图案 */
  263. replaceImg(a: number, b: number) {
  264. const { group, resultGroup } = this
  265. for (let { children } of [group, resultGroup]) {
  266. ;[children[a], children[b]] = [children[b], children[a]]
  267. }
  268. this.render()
  269. }
  270. }
  271. export { Editor }