如何使用InteractJs使可拖动元素在触摸移动坐标上跟随光标。

huangapple go评论57阅读模式
英文:

How to make draggable element follow cursor on touchmove coords with InteractJs

问题

我有一个可拖动的元素,位于容器内并受到其边界的限制。我使用InteractJs来实现这个功能。可拖动的元素默认是隐藏的,当触摸开始时变为可见。

然而,我想要使可拖动的元素跟随用户的光标/触摸位置,以便它可以出现在第一个用户点击/触摸的位置正下方,然后在拖动/触摸移动时跟随它,并在触摸结束时消失。

请问您能否建议我如何修改我的代码以实现这种行为?我尝试应用transform样式属性与触摸开始时的坐标,但没有成功。

示例:
https://codepen.io/moogeek/pen/oNQWwaN

<!DOCTYPE html>
<html>
<head>
  <title></title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/interact.js/1.10.17/interact.min.js"></script>
  <style>
    body { padding-top: 100px; }
    .container {
      width: 368px;
      height: 120px;
      background-color: red;
      margin: 0 auto;
      z-index: 2;
    }
    .draggable {
      visibility: hidden;
      height: 60px;
      width: 80px;
      background-color: #fff;
      display: inline-block;
      touch-action: none;
      z-index: 1;
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="draggable"></div>
  </div>
</body>
</html>

<script>
let dragMoveListener = (event) => {
  let draggable = document.getElementsByClassName("draggable")[0],
    target = event.target,
    // keep the dragged position in the data-x/data-y attributes
    x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx,
    y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;

  draggable.style.visibility = 'visible';
  target.style.backgroundPosition = x + 'px ' + y + 'px';
  target.style.webkitTransform = target.style.transform = 'translate(' + x + 'px, ' + y + 'px)';
  // update the position attributes
  target.setAttribute('data-x', x);
  target.setAttribute('data-y', y);
}

let draggables = document.querySelectorAll(".draggable"),
  containers = document.querySelectorAll(".container");

containers.forEach(function(element){
  interact(element).on("tap down", function(e){
    var wt = element.querySelector('.draggable');
    var wect = wt.getBoundingClientRect();
    wt.style.visibility = 'visible';
  });
});

draggables.forEach(function(element){
  interact(element)
    .on("dragstart", function(e){
      var x = element.getAttribute("data-x");
      var y = element.getAttribute("data-y");
      if (x != null && y != null) {
        // draggable.webkitTransform = draggable.style.transform = 'translate(' + x + 'px, ' + y + 'px)';
      }
    })
    .on("dragmove", function(e){
    })
    .on("dragend", function(e){
      element.style.visibility = 'hidden';
      // draggable.webkitTransform = draggable.style.transform = 'translate(0px, 0px)';
    }).draggable({
      onmove: dragMoveListener,
      modifiers: [
        interact.modifiers.restrict({
          restriction: 'parent',
          endOnly: false
        })
      ]
    });
});
</script>
</html>
英文:

I have one draggable element inside container and restricted to its bounds. I use InteractJs to implement this. Draggable element is hidden by default and becomes visible as touch starts.

However I want to make the draggable element to follow the cursor/touch position of the user so that it can appear right under the position of the first users click/touch and then follow it while drag/touchmove and dissapear on touchend.

Could you please advise how can I modify my code to reach such behavior? I've tried to apply the transform style property with the coordinates of touchstart but no luck.

Example:
https://codepen.io/moogeek/pen/oNQWwaN

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;&lt;/title&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/interact.js/1.10.17/interact.min.js&quot;&gt;&lt;/script&gt;
&lt;style&gt;
body{padding-top:100px;}
.container {
width:368px;
height:120px;
background-color:red;
margin:0 auto;
z-index:2
}
.draggable {
visibility:hidden;
height:60px;
width:80px;
background-color:#fff;
display:inline-block;
touch-action:none;
z-index:1;
}
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div class=&quot;container&quot;&gt;
&lt;div class=&quot;draggable&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;

js:

let dragMoveListener=(event) =&gt; {
let draggable=document.getElementsByClassName(&quot;draggable&quot;)[0],
target = event.target,
// keep the dragged position in the data-x/data-y attributes
x = (parseFloat(target.getAttribute(&#39;data-x&#39;)) || 0) + event.dx,
y = (parseFloat(target.getAttribute(&#39;data-y&#39;)) || 0) + event.dy;
draggable.style.visibility=&#39;visible&#39;;
target.style.backgroundPosition=x + &#39;px &#39; + y + &#39;px&#39;;
target.style.webkitTransform = target.style.transform
= &#39;translate(&#39; + x + &#39;px, &#39; + y + &#39;px)&#39;;
// update the posiion attributes
target.setAttribute(&#39;data-x&#39;, x);
target.setAttribute(&#39;data-y&#39;, y);
}
let draggables=document.querySelectorAll(&quot;.draggable&quot;),
containers=document.querySelectorAll(&quot;.container&quot;);
containers.forEach(function(element){
interact(element).on(&quot;tap down&quot;,function(e){
var wt=element.querySelector(&#39;.draggable&#39;);
var wect=wt.getBoundingClientRect();
wt.style.visibility=&#39;visible&#39;;
});
});
draggables.forEach(function(element){
interact(element)
.on(&quot;dragstart&quot;,function(e){
var x=element.getAttribute(&quot;data-x&quot;);
var y=element.getAttribute(&quot;data-y&quot;);
if(x!=null &amp;&amp; y!=null){
//draggable.webkitTransform = draggable.style.transform = &#39;translate(&#39; + x + &#39;px, &#39; + y + &#39;px)&#39;;
}
}).
on(&quot;dragmove&quot;,function(e){
})
.on(&quot;dragend&quot;,function(e){
element.style.visibility=&#39;hidden&#39;;
//draggable.webkitTransform = draggable.style.transform = &#39;translate(0px, 0px)&#39;;
}).draggable({
onmove: dragMoveListener,
modifiers: [
interact.modifiers.restrict({
restriction: &#39;parent&#39;,
endOnly: false
})
]})
});

答案1

得分: 1

我已经修改了你的代码,以实现你所需要的功能,但我建议你可以使用纯粹的 JavaScript 来达到相同的效果,这样可以避免使用库带来的开销。请参考 MDN 拖放 API

此外,如果你打算在多个实例中使用这个示例,最好使用 event.target 而不是全局变量来处理。

JS

let dragMoveListener = (event) => {
  let draggable = document.getElementsByClassName("draggable")[0],
    target = event.target,
    // 保持拖动位置在 data-x/data-y 属性中
    x = (parseFloat(target.getAttribute("data-x")) || 0) + event.dx,
    y = (parseFloat(target.getAttribute("data-y")) || 0) + event.dy;

  //draggable.style.visibility = "visible";
  target.style.backgroundPosition = x + "px " + y + "px";
  target.style.webkitTransform = target.style.transform =
    "translate(" + x + "px, " + y + "px)";
  // 更新位置属性
  target.setAttribute("data-x", x);
  target.setAttribute("data-y", y);
};

let draggables = document.querySelectorAll(".draggable"),
  containers = document.querySelectorAll(".container");

containers.forEach(function (element) {
  interact(element).on("tap down", function (e) {
    const dragElem = element.querySelector(".draggable");
    const dragBCR = dragElem.getBoundingClientRect();
    const contBCR = element.getBoundingClientRect();
    if (
      dragBCR.left < e.x && e.x < dragBCR.right &&
      dragBCR.top < e.y && e.y < dragBCR.bottom &&
      !(contBCR.top == dragBCR.top && contBCR.left == dragBCR.left)
    ) return true;
    dragElem.setAttribute("data-x", e.x - contBCR.x);
    dragElem.setAttribute("data-y", e.y - contBCR.y);
    dragMoveListener({ target: dragElem, dx: 0, dy: 0 });
    dragElem.style.visibility = "visible";
  });
});

draggables.forEach(function (element) {
  interact(element)
    .on("dragstart", function (e) {
      var x = element.getAttribute("data-x");
      var y = element.getAttribute("data-y");
      if (x != null && y != null) {
        //draggable.webkitTransform = draggable.style.transform = 'translate(' + x + 'px, ' + y + 'px)';
      }
    })
    .on("dragmove", function (e) {})
    .on("dragend", function (e) {
      const contElem = element.closest(".container");
      const contBCR = contElem.getBoundingClientRect();
      element.style.visibility = "hidden";
      element.setAttribute("data-x", 0);
      element.setAttribute("data-y", 0);
      dragMoveListener({ target: element, dx: 0, dy: 0 });
    })
    .draggable({
      onmove: dragMoveListener,
      modifiers: [
        interact.modifiers.restrict({
          restriction: "parent",
          endOnly: false
        })
      ]
    });
});
英文:

I've modified your code to do what you are looking for, but I would suggest that you can achieve the same result in plain vanilla javascript which would remove the overhead of using a library. See MDN Drag and Drop.

Also you should probably be working with event.target instead of global variables if you are going to have multiple instances of this example.

JS

let dragMoveListener = (event) =&gt; {
  let draggable = document.getElementsByClassName(&quot;draggable&quot;)[0],
    target = event.target,
    // keep the dragged position in the data-x/data-y attributes
    x = (parseFloat(target.getAttribute(&quot;data-x&quot;)) || 0) + event.dx,
    y = (parseFloat(target.getAttribute(&quot;data-y&quot;)) || 0) + event.dy;

  //draggable.style.visibility = &quot;visible&quot;;
  target.style.backgroundPosition = x + &quot;px &quot; + y + &quot;px&quot;;
  target.style.webkitTransform = target.style.transform =
    &quot;translate(&quot; + x + &quot;px, &quot; + y + &quot;px)&quot;;
  // update the posiion attributes
  target.setAttribute(&quot;data-x&quot;, x);
  target.setAttribute(&quot;data-y&quot;, y);
};

let draggables = document.querySelectorAll(&quot;.draggable&quot;),
  containers = document.querySelectorAll(&quot;.container&quot;);

containers.forEach(function (element) {
  interact(element).on(&quot;tap down&quot;, function (e) {
    const dragElem = element.querySelector(&quot;.draggable&quot;);
    const dragBCR = dragElem.getBoundingClientRect();
    const contBCR = element.getBoundingClientRect();
    if (
      dragBCR.left &lt; e.x &amp;&amp; e.x &lt; dragBCR.right &amp;&amp;
      dragBCR.top &lt; e.y &amp;&amp; e.y &lt; dragBCR.bottom &amp;&amp;
      !( contBCR.top == dragBCR.top &amp;&amp; contBCR.left == dragBCR.left )
    ) return true;
    dragElem.setAttribute(&quot;data-x&quot;, e.x - contBCR.x);
    dragElem.setAttribute(&quot;data-y&quot;, e.y - contBCR.y);
    dragMoveListener({ target: dragElem, dx: 0, dy: 0 });
    dragElem.style.visibility = &quot;visible&quot;;
  });
});

draggables.forEach(function (element) {
  interact(element)
    .on(&quot;dragstart&quot;, function (e) {
      var x = element.getAttribute(&quot;data-x&quot;);
      var y = element.getAttribute(&quot;data-y&quot;);
      if (x != null &amp;&amp; y != null) {
        //draggable.webkitTransform = draggable.style.transform = &#39;translate(&#39; + x + &#39;px, &#39; + y + &#39;px)&#39;;
      }
    })
    .on(&quot;dragmove&quot;, function (e) {})
    .on(&quot;dragend&quot;, function (e) {
      const contElem = element.closest(&quot;.container&quot;);
      const contBCR = contElem.getBoundingClientRect();
      element.style.visibility = &quot;hidden&quot;;
      element.setAttribute(&quot;data-x&quot;, 0);
      element.setAttribute(&quot;data-y&quot;, 0);
      dragMoveListener({ target: element, dx: 0, dy: 0 });
    })
    .draggable({
      onmove: dragMoveListener,
      modifiers: [
        interact.modifiers.restrict({
          restriction: &quot;parent&quot;,
          endOnly: false
        })
      ]
    });
});

答案2

得分: 1

为了实现这一点,你可以尝试以下步骤:

  1. 在你的容器元素内添加一个带有类名 "follower" 的新元素。这个元素将充当跟随者元素。
  2. 当容器被点击/按下时,将跟随者元素的位置设置为用户点击/触摸的位置,并将其设置为可见。
  3. 在容器被拖动时,更新跟随者元素的位置,以便可拖动的元素在拖动时能够跟随它。
  4. 在容器拖动结束时,隐藏跟随者元素并重置其位置。

请参考下面的演示代码:

<!DOCTYPE html>
<html>
<head>
  <title>可拖动元素</title>
</head>
<body>
  <div class="container">
    <div class="draggable">可拖动元素</div>
    <div class="follower"></div>
  </div>

  <script src="https://unpkg.com/interactjs"></script>
  <script>
    let dragMoveListener = (event) => {
      let draggable = document.querySelector(".draggable"),
          follower = document.querySelector(".follower"),
          target = event.target,
          x = event.clientX,
          y = event.clientY;

      follower.style.visibility = 'visible';
      follower.style.transform = 'translate(' + x + 'px, ' + y + 'px)';

      target.style.backgroundPosition = x + 'px ' + y + 'px';
    };

    let containers = document.querySelectorAll(".container");

    containers.forEach(function (element) {
      interact(element).on("tap down", function (e) {
        var wt = element.querySelector('.draggable');
        var wect = wt.getBoundingClientRect();
        var follower = element.querySelector('.follower');

        // 将跟随者元素的位置设置为用户点击/触摸的位置
        var x = e.clientX - wect.left;
        var y = e.clientY - wect.top;
        follower.style.transform = 'translate(' + x + 'px, ' + y + 'px)';
        follower.style.visibility = 'visible';
      });
    });

    containers.forEach(function (element) {
      interact(element)
        .on("dragstart", function (e) {
          var x = element.getAttribute("data-x");
          var y = element.getAttribute("data-y");
          if (x != null && y != null) {
            // draggable.webkitTransform = draggable.style.transform = 'translate(' + x + 'px, ' + y + 'px)';
          }
        })
        .on("dragmove", dragMoveListener)
        .on("dragend", function (e) {
          var draggable = document.querySelector(".draggable");
          var follower = document.querySelector(".follower");
          draggable.style.visibility = 'hidden';
          follower.style.visibility = 'hidden';
          // draggable.webkitTransform = draggable.style.transform = 'translate(0px, 0px)';
        })
        .draggable({
          onmove: dragMoveListener,
          modifiers: [
            interact.modifiers.restrict({
              restriction: 'parent',
              endOnly: false
            })
          ]
        });
    });
  </script>
</body>
</html>

JSBIN DEMO

英文:

To achieve this you can try this.

  1. Add a new element with the class "follower" inside your container element. This element will act as the follower element.
  2. On container tap/down, set the position of the follower element to the position of the user's click/touch and make it visible.
  3. On container dragmove, update the position of the follower element so that the draggable element can follow it while dragging.
  4. On container dragend, hide the follower element and reset its position.

See the demo below

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

    let dragMoveListener = (event) =&gt; {
let draggable = document.querySelector(&quot;.draggable&quot;),
follower = document.querySelector(&quot;.follower&quot;),
target = event.target,
x = event.clientX,
y = event.clientY;
follower.style.visibility = &#39;visible&#39;;
follower.style.transform = &#39;translate(&#39; + x + &#39;px, &#39; + y + &#39;px)&#39;;
target.style.backgroundPosition = x + &#39;px &#39; + y + &#39;px&#39;;
};
let containers = document.querySelectorAll(&quot;.container&quot;);
containers.forEach(function (element) {
interact(element).on(&quot;tap down&quot;, function (e) {
var wt = element.querySelector(&#39;.draggable&#39;);
var wect = wt.getBoundingClientRect();
var follower = element.querySelector(&#39;.follower&#39;);
// Set the position of the follower element to the position of the user&#39;s click/touch
var x = e.clientX - wect.left;
var y = e.clientY - wect.top;
follower.style.transform = &#39;translate(&#39; + x + &#39;px, &#39; + y + &#39;px)&#39;;
follower.style.visibility = &#39;visible&#39;;
});
});
containers.forEach(function (element) {
interact(element)
.on(&quot;dragstart&quot;, function (e) {
var x = element.getAttribute(&quot;data-x&quot;);
var y = element.getAttribute(&quot;data-y&quot;);
if (x != null &amp;&amp; y != null) {
//draggable.webkitTransform = draggable.style.transform = &#39;translate(&#39; + x + &#39;px, &#39; + y + &#39;px)&#39;;
}
})
.on(&quot;dragmove&quot;, dragMoveListener)
.on(&quot;dragend&quot;, function (e) {
var draggable = document.querySelector(&quot;.draggable&quot;);
var follower = document.querySelector(&quot;.follower&quot;);
draggable.style.visibility = &#39;hidden&#39;;
follower.style.visibility = &#39;hidden&#39;;
//draggable.webkitTransform = draggable.style.transform = &#39;translate(0px, 0px)&#39;;
})
.draggable({
onmove: dragMoveListener,
modifiers: [
interact.modifiers.restrict({
restriction: &#39;parent&#39;,
endOnly: false
})
]
});
});

<!-- language: lang-css -->

    .container {
width: 300px;
height: 300px;
position: relative;
background-color: red;
}
.draggable {
width: 100px;
height: 100px;
background-color: #fff;
color: white;
text-align: center;
line-height: 100px;
user-select: none;
cursor: move;
position: absolute;
top: 0;
left: 0;
visibility: hidden;
}
.follower {
width: 100px;
height: 100px;
background-color: #fff;
opacity: 1;
position: absolute;
top: 0;
left: 0;
visibility: hidden;
}

<!-- language: lang-html -->

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Draggable Element&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div class=&quot;container&quot;&gt;
&lt;div class=&quot;draggable&quot;&gt;Draggable Element&lt;/div&gt;
&lt;div class=&quot;follower&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;script src=&quot;https://unpkg.com/interactjs&quot;&gt;&lt;/script&gt;  
&lt;/body&gt;
&lt;/html&gt;

<!-- end snippet -->

JSBIN DEMO

huangapple
  • 本文由 发表于 2023年7月13日 15:35:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76676973.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定