如何实现将拖动物体限制在某个圆形内–实现方式vue3.0

如何实现蓝色小圆可拖动,并且边界限制在灰色大圆内?如下所示

如何实现将拖动物体限制在某个圆形内--实现方式vue3.0如何实现将拖动物体限制在某个圆形内--实现方式vue3.0如何实现将拖动物体限制在某个圆形内--实现方式vue3.0如何实现将拖动物体限制在某个圆形内--实现方式vue3.0

 

需求源自 业务上遇到一个组件需求,设计师设计了一个“脸型整合器”根据可拖动小圆的位置与其它脸型的位置关系计算融合比例

如图

如何实现将拖动物体限制在某个圆形内--实现方式vue3.0

 

我们先把具体的人脸功能去掉再分析

中间的蓝色小圆可由鼠标拖动,但拖动需要限制在大的圆形内

以下代码全部为 vue3.0版本,如果你是vue2.0, react,或者原生js ,实现原理都一样

第一步 先画出UI


我们的蓝色可拖动小圆点 pointer = ref(null) 为 小圆点dom的 引用

<template>   <div class="face-blender-container">     <div class="blender-circle">       <div         class="blend-pointer"         ref="pointer"         :style="{           left: `${pointerPosition.x}px`,           top: `${pointerPosition.y}px`,         }"       ></div>     </div>   </div> </template>  <script> import { onMounted, reactive, ref, toRefs } from "vue";  export default {   setup() {     const BLENDER_BORDER_WIDTH = 2; // 圆形混合器边宽     const BLENDER_RADIUS = 224 * 0.5 - BLENDER_BORDER_WIDTH; // 圆形混合器半径     // 圆形混合器中心点     const center = {       x: BLENDER_RADIUS,       y: BLENDER_RADIUS,     };     const state = reactive({       pointerPosition: { x: center.x, y: center.y },     });     const pointer = ref(null);           return {       ...toRefs(state),       pointer,     };   }, }; </script>  <style lang="less" scoped> @stageDiameter: 360px; @blenderCircleDiameter: 224px; @PointerDiameter: 20px; .face-blender-container {   position: relative;   width: @stageDiameter;   height: @stageDiameter; } .blender-circle {   position: absolute;   left: 50%;   top: 50%;   margin-left: @blenderCircleDiameter * -0.5;   margin-top: @blenderCircleDiameter * -0.5;   width: @blenderCircleDiameter;   height: @blenderCircleDiameter;   border-radius: @blenderCircleDiameter;   background: rgba(255, 255, 255, 0.04);   border: 2px solid rgba(255, 255, 255, 0.08); }  .blend-pointer {   position: absolute;   left: 50%;   top: 50%;   width: @PointerDiameter;   height: @PointerDiameter;   margin-left: @PointerDiameter * -0.5;   margin-top: @PointerDiameter * -0.5;   border-radius: @PointerDiameter;   background: #11bbf5;   border: 2px solid #ffffff;   z-index: 10; } </style>

ui 如图(整体背景为黑色)

如何实现将拖动物体限制在某个圆形内--实现方式vue3.0

第二步 实现小圆点的无限制拖动


此时小圆点是可以拖到圆外的如图

如何实现将拖动物体限制在某个圆形内--实现方式vue3.0

// 可拖动圆型指示器     const initPointer = () => {       const pointerDom = pointer.value;       pointerDom.onmousedown = (e) => {         // 鼠标按下,计算当前元素距离可视区的距离         const originX = e.clientX - pointerDom.offsetLeft - POINTER_RADIUS;         const originY = e.clientY - pointerDom.offsetTop - POINTER_RADIUS;         document.onmousemove = function (e) {           // 通过事件委托,计算移动的距离           const left = e.clientX - originX;           const top = e.clientY - originY;                      state.pointerPosition.x = left           state.pointerPosition.y = top         };         document.onmouseup = function (e) {           document.onmousemove = null;           document.onmouseup = null;         };       };     };      onMounted(() => {       initPointer();     });

注意是在onMouted 勾子内初始化的可拖动代码

第三步 实现小圆点的限制在大圆内拖动


由于是要限制在圆形内,与限制在方形内的通常计算方法不一样

关键点是计算鼠标 mousemove 时与大圆中心点的弧度 radian 与 距离 dist

弧度公式

dx = x2 - x1

dy = y2 - y1

radian = Math.atan2(dy, dx)

距离公式

dist = Math.sqrt(dx * dx + dy * dy)

当计算出了弧度与距离后,则要计算具体位置了

圆形位置公式
x = 半径 * Math.cos(弧度) + 中心点x
y = 半径 * Math.sin(弧度) + 中心点y

可以看出在圆形公式内控制或限制半径,就限制了小圆的可拖动最大半径范围,所以需要判断,当dist距离大于等于半径时,计算圆形公式内的半径设置为大圆半径即可

具体代码

// 计算 x y     const getPositionByRadian = (radian, radius) => {       const x = radius * Math.cos(radian) + center.x;       const y = radius * Math.sin(radian) + center.y;       return { x, y };     };     // 可拖动圆形指示器     const initPointer = () => {       const pointerDom = pointer.value;       pointerDom.onmousedown = (e) => {         // 鼠标按下,计算当前元素距离可视区的距离         const originX = e.clientX - pointerDom.offsetLeft - POINTER_RADIUS;         const originY = e.clientY - pointerDom.offsetTop - POINTER_RADIUS;         document.onmousemove = function (e) {           // 通过事件委托,计算移动的距离           const left = e.clientX - originX;           const top = e.clientY - originY;           const dx = left - center.x;           const dy = top - center.y;           // 计算当前鼠标与中心点的弧度           const radian = Math.atan2(dy, dx);           // 计算当前鼠标与中心点距离           const dist = Math.sqrt(dx * dx + dy * dy);           const radius = dist >= BLENDER_RADIUS ? BLENDER_RADIUS : dist;           // 根据半径与弧度计算 x, y           const { x, y } = getPositionByRadian(radian, radius);           state.pointerPosition.x = x           state.pointerPosition.y = y         };         document.onmouseup = function (e) {           document.onmousemove = null;           document.onmouseup = null;         };       };     };

这样就实现了最大可拖动范围限制在大圆边界

如何实现将拖动物体限制在某个圆形内--实现方式vue3.0如何实现将拖动物体限制在某个圆形内--实现方式vue3.0

 


整体代码

<template>   <div class="face-blender-container">     <div class="blender-circle">       <div         class="blend-pointer"         ref="pointer"         :style="{           left: `${pointerPosition.x}px`,           top: `${pointerPosition.y}px`,         }"       ></div>     </div>   </div> </template>  <script> import { onMounted, reactive, ref, toRefs } from "vue";  export default {   setup() {     const BLENDER_BORDER_WIDTH = 2; // 圆形混合器边宽     const BLENDER_RADIUS = 224 * 0.5 - BLENDER_BORDER_WIDTH; // 圆形混合器半径     const POINTER_RADIUS = 20 * 0.5; // 可拖动指示器半径     // 圆形混合器中心点     const center = {       x: BLENDER_RADIUS,       y: BLENDER_RADIUS,     };     const state = reactive({       pointerPosition: { x: center.x, y: center.y },     });     const pointer = ref(null);     // 计算 x y     const getPositionByRadian = (radian, radius) => {       const x = radius * Math.cos(radian) + center.x;       const y = radius * Math.sin(radian) + center.y;       return { x, y };     };     // 可拖动圆型指示器     const initPointer = () => {       const pointerDom = pointer.value;       pointerDom.onmousedown = (e) => {         // 鼠标按下,计算当前元素距离可视区的距离         const originX = e.clientX - pointerDom.offsetLeft - POINTER_RADIUS;         const originY = e.clientY - pointerDom.offsetTop - POINTER_RADIUS;         document.onmousemove = function (e) {           // 通过事件委托,计算移动的距离           const left = e.clientX - originX;           const top = e.clientY - originY;           const dx = left - center.x;           const dy = top - center.y;           // 计算当前鼠标与中心点的弧度           const radian = Math.atan2(dy, dx);           // 计算当前鼠标与中心点距离           const dist = Math.sqrt(dx * dx + dy * dy);           const radius = dist >= BLENDER_RADIUS ? BLENDER_RADIUS : dist;           // 根据半径与弧度计算 x, y           const { x, y } = getPositionByRadian(radian, radius);           state.pointerPosition.x = x           state.pointerPosition.y = y         };         document.onmouseup = function (e) {           document.onmousemove = null;           document.onmouseup = null;         };       };     };      onMounted(() => {       initPointer();     });      return {       ...toRefs(state),       pointer,     };   }, }; </script>  <style lang="less" scoped> @stageDiameter: 360px; @blenderCircleDiameter: 224px; @faceCircleDiameter: 64px; @PointerDiameter: 20px; .face-blender-container {   position: relative;   width: @stageDiameter;   height: @stageDiameter; } .blender-circle {   position: absolute;   left: 50%;   top: 50%;   margin-left: @blenderCircleDiameter * -0.5;   margin-top: @blenderCircleDiameter * -0.5;   width: @blenderCircleDiameter;   height: @blenderCircleDiameter;   border-radius: @blenderCircleDiameter;   background: rgba(255, 255, 255, 0.04);   border: 2px solid rgba(255, 255, 255, 0.08); }  .blend-pointer {   position: absolute;   left: 50%;   top: 50%;   width: @PointerDiameter;   height: @PointerDiameter;   margin-left: @PointerDiameter * -0.5;   margin-top: @PointerDiameter * -0.5;   border-radius: @PointerDiameter;   background: #11bbf5;   border: 2px solid #ffffff;   z-index: 10; } </style>

至于其它计算距离,计算反比例的脸形整合业务代码,就不放了

脸形容器的位置还是用圆形公式算出来

各个脸形容器与小圆点距离,距离还是用距离公式得出

知道各个距离了就可以根据业务需要算比例了

 


转载入注明博客园池中物 willian12345@126.com sheldon.wang 

github: https://github.com/willian12345


 

 

发表评论

相关文章