SketchRuler.vue 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. <template>
  2. <div
  3. id="wrapper"
  4. class="wrapper vue-ruler-wrapper"
  5. :style="{
  6. width: width + thick + 'px',
  7. height: height + thick + 'px'
  8. }"
  9. >
  10. <i
  11. :class="{
  12. 'iconfont-bigscreen': true,
  13. 'icon-eye': isShowReferLine,
  14. 'icon-eye-close': !isShowReferLine
  15. }"
  16. @click="handleCornerClick"
  17. />
  18. <SketchRule
  19. :key="scale"
  20. :lang="lang"
  21. :thick="thick"
  22. :scale="scale"
  23. :width="width"
  24. :height="height"
  25. :start-x="startX"
  26. :start-y="startY"
  27. :shadow="shadow"
  28. :hor-line-arr="[...lines.h, ...presetLines.h]"
  29. :ver-line-arr="[...lines.v, ...presetLines.v]"
  30. :palette="Palette"
  31. :is-show-refer-line="isShowReferLine"
  32. :corner-active="cornerActive"
  33. @handleLine="handleLine"
  34. @onCornerClick="handleCornerClick"
  35. />
  36. <div
  37. id="screens"
  38. ref="screensRef"
  39. :style="{
  40. width: innerWidth + 'px',
  41. height: innerHeight + 'px'
  42. }"
  43. @scroll="throttleScroll"
  44. >
  45. <div
  46. ref="containerRef"
  47. class="screen-container grid-bg"
  48. :style="containerRefStyle"
  49. >
  50. <div
  51. id="canvas"
  52. :style="canvasStyle"
  53. >
  54. <slot />
  55. </div>
  56. </div>
  57. </div>
  58. </div>
  59. </template>
  60. <script>
  61. import SketchRule from 'vue-sketch-ruler'
  62. import { mapState, mapMutations } from 'vuex'
  63. import throttle from 'lodash/throttle'
  64. export default {
  65. components: {
  66. SketchRule
  67. },
  68. props: {
  69. width: {
  70. type: Number,
  71. default: 500
  72. },
  73. height: {
  74. type: Number,
  75. default: 400
  76. },
  77. pageWidth: {
  78. type: Number,
  79. default: 1920
  80. },
  81. pageHeight: {
  82. type: Number,
  83. default: 1080
  84. }
  85. },
  86. data () {
  87. return {
  88. startX: 0,
  89. startY: 0,
  90. lines: {
  91. h: [],
  92. v: []
  93. },
  94. thick: 20,
  95. lang: 'zh-CN', // 中英文
  96. isShowRuler: true, // 显示标尺
  97. isShowReferLine: true, // 显示参考线
  98. cornerActive: true, // 左上角激活状态
  99. Palette: {
  100. bgColor: 'rgba(225,225,225, 0)',
  101. longfgColor: '#BABBBC',
  102. shortfgColor: '#C8CDD0',
  103. fontColor: '#7D8694',
  104. shadowColor: 'transparent',
  105. lineColor: '#0089d0',
  106. borderColor: '#transparent',
  107. cornerActiveColor: 'rgb(235, 86, 72, 0.6)'
  108. },
  109. containerRefStyle: {
  110. width: this.width + 'px',
  111. height: this.height + 'px'
  112. },
  113. innerHeight: 0,
  114. innerWidth: 0
  115. }
  116. },
  117. watch: {
  118. // 缩放改变的时候,改变startX,startY
  119. scale (scale) {
  120. // 防抖调用方法
  121. this.throttleScroll()
  122. },
  123. pageWidth (pageWidth) {
  124. if (this.fitZoom === this.zoom) {
  125. this.initZoom()
  126. }
  127. },
  128. pageHeight (pageHeight) {
  129. if (this.fitZoom === this.zoom) {
  130. this.initZoom()
  131. }
  132. }
  133. },
  134. computed: {
  135. ...mapState('bigScreen', {
  136. scale: state => state.zoom / 100,
  137. fitZoom: state => state.fitZoom,
  138. zoom: state => state.zoom
  139. }),
  140. presetLines () {
  141. const presetLine = this.$store.state.bigScreen.presetLine
  142. // { type: 'h', site: y || 0 },
  143. const v = presetLine?.filter(p => p.type === 'h')?.map(p => p.site)
  144. const h = presetLine?.filter(p => p.type === 'v')?.map(p => p.site)
  145. return {
  146. h,
  147. v
  148. }
  149. },
  150. shadow () {
  151. return {
  152. x: 0,
  153. y: 0,
  154. width: this.width,
  155. height: this.height
  156. }
  157. },
  158. canvasStyle () {
  159. return {
  160. width: this.width + 'px',
  161. height: this.height + 'px',
  162. transform: `scale(${this.scale})`,
  163. transformOrigin: '0 0 0'
  164. }
  165. }
  166. },
  167. mounted () {
  168. // 监听屏幕改变
  169. this.listenSize()
  170. this.initRuleHeight()
  171. this.throttleScroll()
  172. },
  173. methods: {
  174. ...mapMutations('bigScreen', [
  175. 'changeZoom',
  176. 'changeFitZoom'
  177. ]),
  178. listenSize () {
  179. window.onresize = throttle(() => {
  180. this.initRuleHeight()
  181. }, 100)
  182. },
  183. initRuleHeight () {
  184. setTimeout(() => {
  185. const screensRect = document
  186. .querySelector('.grid-wrap-box')
  187. ?.getBoundingClientRect()
  188. if (!screensRect) {
  189. return
  190. }
  191. // 30是grid-wrap-box的底部工具栏高度
  192. this.innerHeight = screensRect.height
  193. this.innerWidth = screensRect.width
  194. this.diffX = this.width - screensRect.width
  195. this.diffY = this.height - screensRect.height
  196. this.containerRefStyle = {
  197. width: this.diffX > 0 ? ((this.width + this.diffX + this.thick + 30) + 'px') : (this.width + 'px'),
  198. height: this.diffY > 0 ? ((this.height + this.diffY + this.thick + 30) + 'px') : (this.height + 'px')
  199. }
  200. if (this.fitZoom === this.zoom) {
  201. this.initZoom()
  202. }
  203. })
  204. },
  205. handleLine (lines) {
  206. this.lines = lines
  207. },
  208. handleCornerClick () {
  209. this.isShowReferLine = !this.isShowReferLine
  210. this.cornerActive = !this.cornerActive
  211. },
  212. throttleScroll () {
  213. throttle(() => {
  214. this.handleScroll()
  215. }, 100)()
  216. },
  217. handleScroll () {
  218. const screensRect = document
  219. .querySelector('#screens')
  220. .getBoundingClientRect()
  221. const canvasRect = document
  222. .querySelector('#canvas')
  223. .getBoundingClientRect()
  224. // 标尺开始的刻度
  225. const startX = (screensRect.left + this.thick - canvasRect.left) / this.scale
  226. const startY = (screensRect.top + this.thick - canvasRect.top) / this.scale
  227. this.startX = startX >> 0
  228. this.startY = startY >> 0
  229. this.$emit('changeStart', {
  230. x: this.startX * this.scale + 50 - this.thick,
  231. y: this.startY * this.scale + 50 - this.thick
  232. })
  233. },
  234. // 保证画布能完整展示大屏
  235. initZoom () {
  236. // 横向比例
  237. const xRadio = this.innerWidth / (this.pageWidth + 120)
  238. // 纵向比例
  239. const yRadio = this.innerHeight / (this.pageHeight + 120)
  240. // 取最小的适应比例
  241. const scale = Math.floor(Math.min(xRadio * 100, yRadio * 100))
  242. if (scale < 100) {
  243. this.changeZoom(scale)
  244. this.changeFitZoom(scale)
  245. } else {
  246. this.changeZoom(100)
  247. this.changeFitZoom(100)
  248. }
  249. }
  250. }
  251. }
  252. </script>
  253. <style lang="scss" scoped>
  254. @import '../../BigScreenDesign/fonts/iconfont.css';
  255. .wrapper {
  256. box-sizing: border-box;
  257. position: absolute;
  258. .iconfont-bigscreen {
  259. position: absolute;
  260. left: 0;
  261. top: 0;
  262. font-size: 16px;
  263. color: #fff;
  264. z-index: 999;
  265. cursor: pointer;
  266. }
  267. }
  268. #screens {
  269. position: absolute;
  270. overflow: scroll;
  271. // 滚动条美化,始终在最下方和最右方
  272. &::-webkit-scrollbar {
  273. width: 10px;
  274. height: 10px;
  275. }
  276. &::-webkit-scrollbar-thumb {
  277. border-radius: 10px;
  278. background-color: var(--bs-el-background-2) !important;
  279. }
  280. &::-webkit-scrollbar-track {
  281. border-radius: 10px;
  282. background-color: transparent !important;
  283. }
  284. }
  285. .screen-container {
  286. position: absolute;
  287. width: 6000px;
  288. height: 6000px;
  289. }
  290. .scale-value {
  291. position: absolute;
  292. left: 0;
  293. bottom: 100%;
  294. }
  295. .button {
  296. position: absolute;
  297. left: 100px;
  298. bottom: 100%;
  299. }
  300. .button-ch {
  301. position: absolute;
  302. left: 200px;
  303. bottom: 100%;
  304. }
  305. .button-en {
  306. position: absolute;
  307. left: 230px;
  308. bottom: 100%;
  309. }
  310. #canvas {
  311. position: absolute;
  312. top: 50px;
  313. left: 50px;
  314. }
  315. ::v-deep .line {
  316. border-left: 1px dashed #0089d0 !important;
  317. border-top: 1px dashed #0089d0 !important;
  318. }
  319. ::v-deep.action {
  320. .value {
  321. background: var(--bs-el-color-primary);
  322. padding: 4px;
  323. color: #fff;
  324. }
  325. .del {
  326. color: var(--bs-el-color-primary);
  327. }
  328. }
  329. ::v-deep .ruler, ::v-deep .corner {
  330. background: var(--bs-background-1);
  331. }
  332. ::v-deep .corner {
  333. z-index: 999;
  334. background: var(--bs-background-1) !important;
  335. }
  336. ::v-deep .mb-ruler {
  337. z-index: 998
  338. }
  339. .grid-bg {
  340. background-color: #2a2e33 !important;
  341. background-image: url(./images/canvas-bg.png);
  342. background-repeat: repeat;
  343. word-spacing: 10px;
  344. }
  345. </style>