GalleryMap.vue 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. <template>
  2. <div class="gallery-map wh-100">
  3. <div class="gallery-map-wrapper" :style="`--max: ${max}px;`">
  4. <div class="rows" v-if="dots && max">
  5. <div
  6. v-for="(row, y) in dots" :key="y"
  7. class="row"
  8. :class="{ sub: y % 2 === 0 }"
  9. >
  10. <div
  11. v-for="(dot, x) in row" :key="`${y}-${x}`"
  12. class="dot" :class="{ current: dot === node }"
  13. >
  14. <img
  15. v-if="dot"
  16. :src="dot.image.url"
  17. :alt="dot.image.alt"
  18. @click="$emit('open-creation', dot.id)"
  19. >
  20. </div>
  21. </div>
  22. </div>
  23. </div>
  24. </div>
  25. </template>
  26. <script>
  27. import { shuffle } from '@/helpers/common'
  28. export default {
  29. name: 'GalleryMap',
  30. props: {
  31. nodes: { type: Array, required: true },
  32. node: { type: Object, required: true },
  33. visible: { type: Boolean, required: true }
  34. },
  35. data () {
  36. return {
  37. dots: undefined,
  38. size: [0, 0],
  39. max: undefined
  40. }
  41. },
  42. watch: {
  43. visible (visible) {
  44. if (visible) {
  45. this.defineSize()
  46. }
  47. }
  48. },
  49. methods: {
  50. setDots () {
  51. const nodesLen = this.nodes.length
  52. const sqrt = Math.ceil(Math.sqrt(nodesLen))
  53. const rows = Math.floor(Math.sqrt(Math.pow(sqrt, 2) / 2))
  54. const len = rows + 1
  55. let max = rows * len * 2
  56. let extra = 0
  57. while (max - nodesLen < 0) {
  58. extra += 1
  59. max += extra % 2 === 0 ? rows : len
  60. }
  61. const nodes = shuffle([...this.nodes, ...Array(max - nodesLen).fill(undefined)])
  62. const dots = []
  63. for (var y = 0; y < len + rows + extra; y++) {
  64. const x = y % 2 === 0 ? rows : len
  65. dots.push(nodes.splice(0, x))
  66. }
  67. this.size = [len, len + rows + extra]
  68. this.dots = dots
  69. },
  70. defineSize () {
  71. const [x, y] = this.size
  72. const { offsetWidth, offsetHeight } = document.querySelector('.gallery-map-wrapper')
  73. const h = offsetHeight / (y * 2 - 1)
  74. const w = offsetWidth / (x * 2 - 1)
  75. this.max = Math.min(Math.min(h, w), 100)
  76. }
  77. },
  78. created () {
  79. this.setDots()
  80. if (this.visible) {
  81. this.$nextTick().then(() => {
  82. this.defineSize()
  83. })
  84. }
  85. }
  86. }
  87. </script>
  88. <style lang="scss" scoped>
  89. .gallery-map {
  90. &-wrapper {
  91. width: 100%;
  92. position: relative;
  93. height: 100%;
  94. }
  95. .rows {
  96. position: absolute;
  97. display: flex;
  98. flex-direction: column;
  99. justify-content: space-between;
  100. align-items: center;
  101. height: 100%;
  102. width: 100%;
  103. }
  104. .row {
  105. display: flex;
  106. justify-content: space-between;
  107. width: 100%;
  108. &.sub {
  109. justify-content: space-around;
  110. }
  111. }
  112. .dot {
  113. display: flex;
  114. width: var(--max);
  115. height: var(--max);
  116. img {
  117. width: 100%;
  118. height: 100%;
  119. object-fit: cover;
  120. margin: auto;
  121. cursor: pointer;
  122. }
  123. &.current img {
  124. border: 5px solid theme-color('creation');
  125. }
  126. }
  127. }
  128. </style>