【问题标题】:How to make draggable div elements not stack upon each other如何使可拖动的 div 元素不相互堆叠
【发布时间】:2021-12-14 22:03:27
【问题描述】:

我正在使用 let divEx = document.createElement('div') 创建各种可拖动元素,然后使用 main.appendChild(divEx) 将其添加到主 div,最后 使用memDiv.setAttribute('draggable', 'true') 将属性draggable 设置为true。整个功能都在一个函数中,当按下按钮时在主 Js 文件中调用该函数。虽然拖放功能可以正常工作,但如果位置样式属性设置为绝对,则 div 元素会设置在每个元素上,或者如果在 CSS 中将其设置为相对,则会在每个元素上设置 div 元素。在 DOM 中,它显示了不同的 div 元素,但是它们的位置都是相同的。我希望下面的截图能解释这个问题:

绝对案例:

There are three div elements (class=memDiv) on the screen two on top of each other and a newly created

Here is displayed after I move the third element, it is also stacked on top of the others

这个问题的最佳解决方案是什么?

【问题讨论】:

    标签: javascript html css draggable


    【解决方案1】:

    答案:关键方法/解决方案是Collision Detection div 元素之间

    看起来你正在尝试做一个游戏或类似的东西,请省去你的痛苦/不要重新发明轮子:),只需利用一个库并查找 collision detection 或将 Z 索引设置为 1 和检查过圈,我更喜欢 sat.js 库。

    注意:这里是sat.js,请查看来自here 的来自第 3 方/归功于 OSU blake 的样本

    console.clear();
    var log = console.log.bind(console);
    
    
    
    // Alias a few things in SAT.js to make the code shorter
    var V = function (x, y) { return new SAT.Vector(x, y); };
    var P = function (pos, points) { return new SAT.Polygon(pos, points); };
    var C = function (pos, r) { return new SAT.Circle(pos, r); };
    var B = function (pos, w, h) { return new SAT.Box(pos, w, h); };
    
    // Converts a SAT.Polygon into a SVG path string.
    function poly2path(polygon) {
      var pos = polygon.pos;
      var points = polygon.calcPoints;
      var result = 'M' + pos.x + ' ' + pos.y;
      result += 'M' + (pos.x + points[0].x) + ' ' + (pos.y + points[0].y);
      for (var i = 1; i < points.length; i++) {
        var point = points[i];
        result += 'L' + (pos.x + point.x) + ' ' + (pos.y + point.y);
      }
      result += 'Z';
      return result;
    }
    
    // Create a Raphael start drag handler for specified entity
    function startDrag(entity) {
      return function () {
        this.ox = entity.data.pos.x;
        this.oy = entity.data.pos.y;
      };
    }
    // Create a Raphael move drag handler for specified entity
    function moveDrag(entity, world) {
      return function (dx, dy) {
        // This position updating is fairly naive - it lets objects tunnel through each other, but it suffices for these examples.
        entity.data.pos.x = this.ox + dx;
        entity.data.pos.y = this.oy + dy;
        world.simulate();
      };
    }
    // Create a Raphael end drag handler for specified entity
    function endDrag(entity) {
      return function () {
        entity.updateDisplay();
      };
    }
    
    var idCounter = 0;
    
    function noop() {}
    
    function Entity(data, display, options) {
      options = _.defaults(options || {}, {
        solid: false, // Whether this object is "solid" and therefore should participate in responses.
        heavy: false, // Whether this object is "heavy" and can't be moved by other objects.
        displayAttrs: {}, // Raphael attrs to set on the display object
        onCollide: noop, // Function to execute when this entity collides with another - arguments are (otherEntity, response)
        onTick: noop // Function called at the start of every simulation tick - no arguments
      });
      this.id = idCounter++;
      this.data = data;
      this.display = display;
      this.displayAttrs = _.extend({
        fill: '#CCC',
        stroke: '#000'
      }, options.displayAttrs);
      this.isSolid = options.solid;
      this.isHeavy = options.heavy;
      this.onCollide = options.onCollide;
      this.onTick = options.onTick;
    }
    Entity.prototype = {
      remove: function () {
        this.display.remove();
      },
      // Call this to update the display after changing the underlying data.
      updateDisplay: function () {
        if (this.data instanceof SAT.Circle) {
          this.displayAttrs.cx = this.data.pos.x;
          this.displayAttrs.cy = this.data.pos.y;
          this.displayAttrs.r = this.data.r;
        } else {
          this.displayAttrs.path = poly2path(this.data);
        }
        this.display.attr(this.displayAttrs);
      },
      tick: function () {
        this.onTick();
      },
      respondToCollision: function (other, response) {
        this.onCollide(other, response);
        // Collisions between "ghostly" objects don't matter, and
        // two "heavy" objects will just remain where they are.
        if (this.isSolid && other.isSolid &&
          !(this.isHeavy && other.isHeavy)) {
          if (this.isHeavy) {
            // Move the other object out of us
            other.data.pos.add(response.overlapV);
          } else if (other.isHeavy) {
            // Move us out of the other object
            this.data.pos.sub(response.overlapV);
          } else {
            // Move equally out of each other
            response.overlapV.scale(0.5);
            this.data.pos.sub(response.overlapV);
            other.data.pos.add(response.overlapV);
          }
        }
      }
    };
    
    function World(canvas, options) {
      options = _.defaults(options || {},  {
        loopCount: 1 // number of loops to do each time simulation is called. The higher the more accurate the simulation, but slowers.
      });
      this.canvas = canvas; // A raphael.js canvas
      this.response = new SAT.Response(); // Response reused for collisions
      this.loopCount = options.loopCount;
      this.entities = {};
    }
    World.prototype = {
      addEntity: function(data, options) {
        var entity = new Entity(
          data,
          data instanceof SAT.Circle ? this.canvas.circle() : this.canvas.path(),
          options
        );
        // Make the display item draggable if requested.
        if (options.draggable) {
          entity.display.drag(moveDrag(entity, this), startDrag(entity), endDrag(entity));
        }
        entity.updateDisplay();
        this.entities[entity.id] = entity;
        return entity;
      },
      removeEntity: function (entity) {
        entity.remove();
        delete this.entities[entity.id];
      },
      simulate: function () {
        var entities = _.values(this.entities);
        var entitiesLen = entities.length;
        // Let the entity do something every simulation tick
        _.each(entities, function (entity) {
          entity.tick();
        });
        // Handle collisions - loop a configurable number of times to let things "settle"
        var loopCount = this.loopCount;
        for (var i = 0; i < loopCount; i++) {
          // Naively check for collision between all pairs of entities 
          // E.g if there are 4 entities: (0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)
          for (var aCount = 0; aCount < entitiesLen; aCount++) {
            var a = entities[aCount];
            for (var bCount = aCount + 1; bCount < entitiesLen; bCount++) {
              var b = entities[bCount];
              this.response.clear();
              var collided;
              var aData = a.data;
              var bData = b.data;
              if (aData instanceof SAT.Circle) {
                if (bData instanceof SAT.Circle) {
                  collided = SAT.testCircleCircle(aData, bData, this.response);
                } else {
                  collided = SAT.testCirclePolygon(aData, bData, this.response);
                }
              } else {
                if (bData instanceof SAT.Circle) {
                  collided = SAT.testPolygonCircle(aData, bData, this.response);
                } else {
                  collided = SAT.testPolygonPolygon(aData, bData, this.response);
                }
              }
              if (collided) {
                a.respondToCollision(b, this.response);
              }
            }
          }
        }
        // Finally, update the display of each entity now that the simulation step is done.
        _.each(entities, function (entity) {
          entity.updateDisplay();
        });
      }
    };
    
    
    
    
    
    (function () {
          var canvas = Raphael('example1', 640, 480);
          var world = new World(canvas);
          var poly = world.addEntity(P(V(160,120), [V(0,0), V(60, 0), V(100, 40), V(60, 80), V(0, 80)]).translate(-50, -40), { solid: true, draggable: true });
          var poly2 = world.addEntity(P(V(10,10), [V(0,0), V(30, 0), V(30, 30), V(0, 30)]), { solid: true, draggable: true });
          var circle1 = world.addEntity(C(V(50, 200), 30), { solid: true, heavy: true, draggable: true });
          function doRotate() {
            poly.data.setAngle(poly.data.angle + (Math.PI / 60)); // Assuming 60fps this will take ~2 seconds for a full rotation
            world.simulate();
            window.requestAnimationFrame(doRotate);
          }
          window.requestAnimationFrame(doRotate);
        }());
    
    
    (function () {
          var canvas = Raphael('example2', 640, 640);
          var world = new World(canvas, {
            loopCount: 5
          });
          for (var i = 0; i < 16; i++) {
            for (var j = 0; j < 16; j++) {
              var displayAttrs = {
                fill: 'rgba(' + Math.floor(Math.random() * 255) + ',' +  Math.floor(Math.random() * 255) + ',' +  Math.floor(Math.random() * 255) + ')'
              }
              var c = world.addEntity(C(V((40 * i) + 20, (40 * j) + 20), 18), { solid: true, draggable: true, displayAttrs: displayAttrs });
            }
          }
        }());
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-06
      • 2021-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多