基于SVG/鼠标位置的定位引导弹出窗口



我想根据用户在CircleSVG中的鼠标位置(单击时(或将其移动到SVG的正中心(移动popover内容和箭头(来重新定位当前位于Circle SVG右侧的popover。是否可以动态更改?

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>

是的,可以根据鼠标位置更改popover位置。您可以在每次单击SVG并显示popover时,使用动态CSS将<style>元素附加到HTML文档中。

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>

所以你问了几件事,我相信这两件事都可以通过在点击事件时生成popover来完成。

将popover放置为使其指向元素的中间可以通过计算并提供"来实现;偏移";选项见红色圆圈&示例中的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>