vue.js 当在created()钩子中创建元素时,如何防止随机放置的div在小屏幕/移动的屏幕上重叠

u3r8eeie  于 2023-03-31  发布在  Vue.js
关注(0)|答案(2)|浏览(186)

我有这样的代码(在小提琴中,圆圈重叠,但它在我的PC上工作正常-很可能是由于小提琴中的像素宽度关闭?):https://codesandbox.io/s/objective-hamilton-zspre8?file=/src/App.vue

TestCircle.vue

<template>
  <div class="circle" :style="{left: this.left,
                 top: this.top}">
    {{value}}
  </div>
</template>

<script>
export default {
name: "TestCircle",

props:  {
  value: String,
  left: Number,
  top: Number
}
}
</script>

<style scoped>
  .circle  {
    background-color: red;

    min-height: 49px;
    height: 49px;

    min-width: 49px;
    width: 49px;

    border-radius: 20em;
    display: inline;
    padding: 1em;
    position: absolute;

    border: 1px solid black;

  }
</style>

TestComponent.vue

<template>
  <div id="wrapper" ref="wrapper">

    <TestCircle v-for="(circle, i) in this.circles.length"
                 :value = this.circles[i] :key="i"
                :left="this.coordinates[i].left"
                :top="this.coordinates[i].top"

    />

  </div>
</template>

<script>
import TestCircle from "@/app/test/TestCircle";

export default {
  name: "TestComponent",
  components: {TestCircle},
  data() {
    return {
      circles: ['A', 'B', 'C', 'D', 'E', 'F'],
      coordinates: []
    }
  },

    created() {
    console.log("circles: " + this.circles);

    let wrapperWidth = 500;
    let wrapperHeight = 100;

    let circleWidth = 50;
    let circleHeight = 50;

    for (let i = 0; i < this.circles.length; i++) {
      let circleCoordinates = {};

      let isValid = false;
      do {
        isValid = false;

        circleCoordinates.left = (this.randomInRange(0, wrapperWidth - circleWidth)).toString() + 'px';
        circleCoordinates.top = (this.randomInRange(0, wrapperHeight - circleHeight)).toString() + 'px';
        circleCoordinates.width = 50;
        circleCoordinates.height = 50;

        isValid = !this.overlaps(this.coordinates, i, circleCoordinates);
      } while (!isValid);

      this.coordinates.push(circleCoordinates);
    }
  },

  methods: {
    randomInRange: function(min, max)  {
      return Math.random() * (max - min + 1) + min;
    },

    overlaps(coordinates, size, rectangle)  {
      // Loop through all the rectangles in the coordinates array
      for (let i = 0; i < size; i++) {
        // Create a rectangle object for the other rectangle

        // Check whether the two rectangles overlap using the rectanglesOverlap function
        if (this.rectanglesOverlap(rectangle, coordinates[i])) {
          return true;
        }
      }

      // None of the rectangles overlap
      return false;
    },

    rectanglesOverlap(first, second) {
      console.log("rectanglesOverlap: " + JSON.stringify(first)
          + " and "+ JSON.stringify(second));

      // Determine the coordinates of the edges of both rectangles
      const firstLeft = parseFloat(first.left);
      const firstRight = firstLeft + first.width;
      const firstTop = parseFloat(first.top);
      const firstBottom = firstTop + first.height;

      const secondLeft = parseFloat(second.left);
      const secondRight = secondLeft + second.width;
      const secondTop = parseFloat(second.top);
      const secondBottom = secondTop + second.height;

      // Check if any of the edges of the two rectangles overlap
      if (firstLeft < secondRight &&
          firstRight > secondLeft &&
          firstTop < secondBottom &&
          firstBottom > secondTop) {
        // The rectangles overlap
        console.log("rectanglesOverlap: true");
        return true;
      }

      // The rectangles don't overlap
      console.log("rectanglesOverlap: false");
      return false;
    }
  },

  mounted() {

  }

}
</script>

<style scoped>

  #wrapper  {
    margin: 2em;

    height: 100px;
    min-height: 100px;

    width: 500px;
    min-width: 500px;

    background-color: darkblue;
    position: relative;
  }
</style>

测试页面版本

<template>
  <div id="test-page">
    <TestComponent/>
   </div>
</template>

<script>

 import TestComponent from "@/app/test/TestComponent";
 export default {
  name: "TestPage",
  components: {TestComponent },

   async beforeCreate() {

   },

   methods: {

   },
}
</script>

<style scoped>
  #test-page  {
    background-color: yellow;
    width: 50%;

    display: flex;
    justify-content: center;
  }
</style>

当我访问TestPage时,我看到:

如果我更新页面:

可以看出,圆圈随机放置在蓝色div内,圆圈不重叠。
蓝色div的宽度500px和高度100px以及每个圆圈的高度和宽度(50px)是硬编码的。
它工作正常(至少在我的PC上),但它不适用于调整浏览器窗口大小,或切换到移动的:

我需要它看起来在移动的/较小的浏览器窗口/规模确定。
但是我不知道该如何处理它。我有一个想法,我需要用适当的值替换created中的wrapperWidthwrapperHeightcircleWidthcircleHeight。但是在created()中,无法访问 Package 器div,因为它还不存在。我应该如何处理它?
在移动的上看起来应该是这样的:
x1c4d 1x到目前为止,我一直在使用媒体查询,使我的网站响应:

@media screen and (max-width: 500px) {
  .wrapper {
    max-width: 90% !important;

  }

  .btn  {
    font-size: 0.8em;
   }
}

因此,假设蓝色 Package 器div的宽度由媒体查询控制,与圆圈的宽度相同,我应该如何响应地将它们放置在蓝色div上?
任何帮助都是感激的!

编辑

一个明确而具体的问题:如何让它在手机大小的屏幕上工作?

6ss1mwsb

6ss1mwsb1#

1.首先,当你想与DOM交互时,你不应该使用created钩子,而应该使用mounted钩子。不同的是,在mounted中,组件已经被挂载(a.k.a. “added to DOM”)。这是因为你需要在下一步中使用当前可用的宽度。
1.第二,在你把任何东西转换成代码之前,你必须在逻辑上定义它,直到最后一步,你现在的代码表明你还没有这样做。
在我看来,要实现所需的功能,你需要定义一个函数,它接受可用的宽度和圆的数量,返回一个宽度可以适合所有圆的盒子的最小高度,***不管每个圆的位置如何。你需要一点三角学来解决这个问题。
如果您在计算公式时遇到困难,请考虑在mathematics***1***上询问它。
1.一旦你有了最小高度公式,你所拥有的应该起作用,只要你修复了overlaps函数。检查两个圆是否相交的正确方法是比较它们的中心之间的距离与它们的半径之和。
如果你不想在数学网站上询问,只想粗略估计一下,这里有一个模式,其中圆圈最分散,实际上不允许在***2***之间有任何额外的圆圈:

中心之间的距离大约是3.5 × r,每一行的高度大约是3 × r。如果它们之间的距离更远,间隙就足够大,可以容纳额外的圆。
如果我必须解决这个问题,我会根据这个模式估计盒子的最小高度***3***,而不是计算精确的三角公式。我给予第一行的高度3 × r(虽然它实际上更短),以简化公式,并确保圆圈将适合,即使在不太可能的事件,他们的位置本身 *“随机”*与上面所示的模式完全一致。

1-你需要问一个与代码无关的问题。不要问他们关于Vue或移动的设备的问题,他们会把你送回这里,没有最小高度公式,这是你实际需要的。
2-如果前12个圆的位置与图中相同,则第13个圆将导致代码中的while无限循环
3-最小高度的粗略公式为:

const height = Math.max(
  (3 * radius) * (circles.length / (containerWidth / (3.5 * radius))),
  radius * 3.5
)

其可以写成:

const height = Math.max(
  10.5 * circles.length * radius ** 2 / containerWidth,
  3.5 * radius
)

为了确保它永远不会无休止地循环,可以在while上设置一个计数器。如果它超过某个值(例如:100 ×相同的圆),你可以简单地重新计算所有的圆,但它会冻结的概率是可以忽略不计的,如果有的话。注意这个护栏没有包括在下面的例子中。
下面是一个使用这个公式的演示:
一个一个二个一个一个一个三个一个一个一个一个一个四个一个
关于resize事件的注意,我只是重新计算先前放置在当前容器外部的圆的位置(我保留了不需要移动的圆)。

xcitsw88

xcitsw882#

如果你想随机地把这些圆放在一个预定义的区域内,css的响应布局机制都无法帮助你,你只需要手动设计数学算法来计算坐标。
也就是说,你的问题可以定义为计算给定边界内的许多圆的坐标,以便它们不重叠:

getCoordinates(numberOfCircles, circleSize, areaWidth, areaHeight)

假设你有算法(看起来你有,虽然非常暴力),剩下的唯一问题是知道实际的areaWidthareaHeight(而不是硬编码它们),并知道它们何时改变(以便你可以重新运行你的算法)。
你可以使用Vue的template ref来访问你的容器div(注意模板引用至少要等到mounted钩子才能被填充),然后你可以使用ResizeObserver来观察那个div的大小变化。每次大小变化,你都要通过再次运行getCoordinates方法来更新坐标,Vue会处理剩下的事情。

相关问题