返回

如何旋转父元素时同步旋转子元素?

javascript

旋转父元素时如何旋转子元素

问题

有时我们需要让父元素及其所有子元素一起旋转,即使子元素没有附加到父元素中,并且它们相对于父元素的位置是固定的。旋转时,它们与父元素左侧和顶部位置 (x, y) 的距离和角度应该保持不变。

解决方法

要实现这一效果,需要使用 JavaScript 和 CSS 中的变换属性。具体步骤如下:

  1. 获取鼠标角度: 当用户按下鼠标按钮并开始拖动时,计算鼠标相对于父元素中心点的角度。
  2. 获取父元素的初始旋转: 使用 CSS 的 getComputedStyle() 方法,获取父元素初始旋转角度。
  3. 计算旋转增量: 监听鼠标移动事件,不断计算当前鼠标角度和初始鼠标角度之间的增量。
  4. 应用旋转: 使用 CSS 的 transform 属性,将旋转增量应用于父元素。
  5. 计算子元素的新位置: 对于每个子元素,根据初始距离和旋转增量计算其相对于父元素中心的新位置。
  6. 应用子元素旋转: 使用 CSS 的 transform 属性,将父元素的旋转角度应用于每个子元素。
  7. 更新子元素位置: 将计算的新位置应用于子元素,使其在旋转的同时保持相对于父元素的位置。

代码示例

// 获取元素
const parentElement = document.getElementById("parentElement");
const childElements = document.querySelectorAll(".childElement");

// 鼠标拖动相关变量
let isDragging = false;
let initialMouseAngle = 0;
let initialRotation = 0;
let initialDistance = {};

// 获取鼠标相对于父元素中心点的角度
const getMouseAngle = (e) => {
  const rect = parentElement.getBoundingClientRect();
  const centerX = rect.left + rect.width / 2;
  const centerY = rect.top + rect.height / 2;
  const mouseX = e.clientX - centerX;
  const mouseY = e.clientY - centerY;
  return Math.atan2(mouseY, mouseX) * (180 / Math.PI);
};

// 获取父元素的初始旋转角度
const getRotationDegrees = () => {
  const transformValue = window.getComputedStyle(parentElement).getPropertyValue("transform");
  const matrix = new DOMMatrixReadOnly(transformValue);
  return Math.atan2(matrix.b, matrix.a) * (180 / Math.PI);
};

// 鼠标按下开始拖动
const startDrag = (e) => {
  isDragging = true;
  initialMouseAngle = getMouseAngle(e);
  initialRotation = getRotationDegrees();
  childElements.forEach(child => {
    const parentRect = parentElement.getBoundingClientRect();
    const childRect = child.getBoundingClientRect();
    initialDistance[child.id] = Math.hypot(childRect.left - parentRect.left, childRect.top - parentRect.top);
  });
};

// 鼠标移动处理
const handleDrag = (e) => {
  if (isDragging) {
    const currentMouseAngle = getMouseAngle(e);
    const rotationChange = currentMouseAngle - initialMouseAngle;
    const newRotation = initialRotation + rotationChange;
    const parentRect = parentElement.getBoundingClientRect();
    const angleInRadians = newRotation * (Math.PI / 180);

    // 应用旋转给父元素
    parentElement.style.transform = `rotate(${newRotation}deg)`;

    // 计算子元素新位置并应用旋转
    childElements.forEach(child => {
      const distance = initialDistance[child.id];
      const newX = parentRect.left + distance * Math.cos(angleInRadians);
      const newY = parentRect.top + distance * Math.sin(angleInRadians);
      child.style.left = `${newX}px`;
      child.style.top = `${newY}px`;
      child.style.transform = `rotate(${newRotation}deg)`;
    });
  }
};

// 鼠标释放结束拖动
const stopDrag = () => {
  isDragging = false;
};

// 事件监听
document.addEventListener("mousedown", startDrag);
document.addEventListener("mouseup", stopDrag);
document.addEventListener("mousemove", handleDrag);

常见问题解答

1. 如何让子元素保持相对于父元素的相对位置?

通过计算每个子元素相对于父元素中心点的初始距离,并使用此距离来计算子元素在旋转后的新位置。

2. 如何将旋转应用于子元素?

通过将父元素的旋转角度应用于每个子元素的 transform 属性。

3. 如何处理多个子元素?

上面的代码中使用了一个循环来遍历所有的子元素,并根据其 ID 计算每个子元素的初始距离。

4. 如何将子元素拖出父元素范围?

由于子元素相对于父元素的距离保持不变,因此它们无法拖出父元素范围。

5. 如何应用额外的变换效果?

可以根据需要添加额外的变换效果,例如平移或缩放,通过修改 childElement.style.transform 属性。