基于SVG/鼠标位置定位Bootstrap弹出框

3
我希望重新定位弹出框位置 - 目前在圆形 SVG 右侧 - 基于用户在 CircleSVG 内的鼠标位置(单击时)或将其移动到 SVG 的正中心(同时移动弹出框内容和箭头)。能否动态更改?

const popover = new bootstrap.Popover(document.getElementById("test"), {
                        html: true,
                        sanitize: false,
                        trigger: 'manual',
                        content: 'Nice'
                    });
                    
function togglePopover() {
  popover.toggle();
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"></script>

<svg onclick="togglePopover()"  height="100" width="100">
  <circle id="test" cx="50" cy="50" r="40" stroke="black" stroke width="3" fill="red" />
</svg>


你想让它只在点击时出现吗?如果不是,我建议使用工具提示,这样它会在悬停时显示。 - BeerusDev
@BeerusDev 是的,遗憾的是,对于我的应用程序目的在于点击。这可行吗? - DreadedSlug
1
“基于...用户在CircleSVG中的鼠标位置(单击时)”--这是什么意思?您的意思是如果鼠标在圆形底部附近,您将在底部显示弹出窗口,如果在左侧,则在左侧显示弹出窗口,以此类推吗? - Alexander Nied
@AlexanderNied,不完全是这样。我的意思是,如果用户的鼠标在圆形的中心,那么箭头(来自弹出窗口)将从用户点击的位置(在这种情况下-中心)开始。如果这不太清楚,我可以画个图!让我知道。 - DreadedSlug
2个回答

3

是的,可以根据鼠标位置更改弹出框的位置。您可以通过在每次单击SVG并显示弹出框时向HTML文档附加一个带有动态CSS的<style>元素来实现。

const popover = new bootstrap.Popover(document.getElementById("test"), {
  html: true,
  sanitize: false,
  trigger: 'manual',
  content: 'Nice'
});

function addStyle(css) {
  const existingStyleElement = document.querySelector('#popoverCustomStyle');

  if (existingStyleElement) {
    existingStyleElement.remove();
  }

  document.head.insertAdjacentHTML('beforeend', `
    <style id="popoverCustomStyle">${css}</style>
  `);
}

document.querySelector('#mySVG').addEventListener('click', function togglePopover(e) {
  popover.toggle();

  if (popover._hoverState) {
    const popoverArrowWidth = popover.tip.querySelector('.popover-arrow').getBoundingClientRect().width;

    const popoverPlacementSettings = {
      left: {
        x: `calc(${e.pageX - popoverArrowWidth}px - 100%)`,
        y: `calc(${e.pageY}px - 50%)`
      },
      right: {
        x: `${e.pageX + popoverArrowWidth}px`,
        y: `calc(${e.pageY}px - 50%)`
      },
      top: {
        x: `calc(${e.pageX}px - 50%)`,
        y: `calc(${e.pageY - popoverArrowWidth / 2}px - 100%)`
      },
      bottom: {
        x: `calc(${e.pageX}px - 50%)`,
        y: `${e.pageY + popoverArrowWidth / 2}px`
      }
    }

    setTimeout(() => {
      addStyle(`
      .my-popover {
        inset: 0 auto auto 0 !important;
        transform: translate(${popoverPlacementSettings[popover._popper.state.placement].x}, ${popoverPlacementSettings[popover._popper.state.placement].y}) !important;
      }
    `);
      popover.tip.classList.add('my-popover');
    }, 0)
  }
})
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"></script>

<svg id="mySVG" height="100" width="100">
  <circle id="test" cx="50" cy="50" r="40" stroke="black" stroke width="3" fill="red" />
</svg>


我觉得这很不专业,但它能用!谢谢。 - DreadedSlug
我发现了一个错误,如果您能够更新的话...当弹出窗口靠近边缘时,它不会显示出来。我猜测这是因为它正在尝试重新定位自己,但没有成功。 - DreadedSlug
你是指圆的边缘吗? - user11229156
页面边缘...这是我实际问题的非常简化版本(我的页面上有多个SVG)。如果用户切换到一个距离页面“左侧”超过50%的SVG,则不会显示。 - DreadedSlug
1
我已经编辑了代码,添加了一些适应不同位置的调整。现在试试看。 - user11229156
显示剩余3条评论

1

所以你问了几件事情,我相信都可以通过在点击事件时生成弹出窗口来实现。

将弹出窗口放置在元素中间的位置可以通过计算并提供一个“偏移量”选项来实现。请参见示例中的红色圆圈和togglePopover_toCenter函数

指向鼠标单击发生位置的弹出窗口可以通过附加一个事件侦听器来实现,该侦听器使用鼠标位置并在生成弹出窗口时调整其位置。请参见示例中的蓝色圆圈和togglePopover_toMouse函数

function togglePopover_toCenter(event) {
  const targetElement = event.target;
  
  // if a popover already exisits for this element. Dispose of it.
  const oldPopover = bootstrap.Popover.getInstance(targetElement);
  if (oldPopover){
    oldPopover.dispose();
  }
  
  const generatePopover = (element) => {
    // get it's width
    const boundingRect = element.getBoundingClientRect();
    const elementWidth = boundingRect.width;

    // calculate the "distance" value needed by the offset option
    // half of element width, converted to a negative, adding back the default 8 for the triangle
    const offsetDistance = (elementWidth/2 * -1) + 8;
  
    // set up the popover using the offset attribute
    const popover = new bootstrap.Popover(
      element, 
      {
        html: true,
        sanitize: false,
        trigger: 'manual',
        content: 'Nice',
        offset: [0, offsetDistance]
      }
    );
    return popover;
} 
  const newPopover = generatePopover(targetElement);
  newPopover.toggle();
}


                    
function togglePopover_toMouse(event) {
  
  // if a popover already exisits for this element. Dispose of it.
  const oldPopover = bootstrap.Popover.getInstance(event.target);
  if (oldPopover){
      oldPopover.dispose();
  }
  
  const generatePopover_toMouse = (event) =>{
    // get the clicked element
    const targetElement = event.target;

    // set up the popover 
    const popover = new bootstrap.Popover(
      targetElement, 
      {
        html: true,
        sanitize: false,
        trigger: 'manual',
        content: 'Nice'
      }
    );
  
    
    // get the clicked elements boundRect
    const boundingRect = targetElement.getBoundingClientRect();
    const x = event.clientX - boundingRect.left; //x position within the element.
    const y = event.clientY - boundingRect.top;  //y position within the element.
  
    // set up an event listerner to move the popover after it is shown
    targetElement.addEventListener('shown.bs.popover',() => {
      if (popover.tip){
        popover.tip.style.left = `${x - boundingRect.width}px`;
        popover.tip.style.top = `${y - (boundingRect.height/2)}px`;
      }
    });

    popover.toggle();
  } 
  
  // create a new popover by passing in the click event
  generatePopover_toMouse(event);
}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"/>


<svg onclick="togglePopover_toCenter(event)"  height="100" width="100">
  <circle id="test" cx="50" cy="50" r="40" stroke="black" stroke width="3" fill="red" />
</svg>

<br />


<svg onclick="togglePopover_toMouse(event)"  height="100" width="100">
  <circle id="test" cx="50" cy="50" r="40" stroke="black" stroke width="3" fill="blue" />
</svg>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接