如何触发包含三个重叠 div 元素的 :hover 过渡(维恩图)

Posted

技术标签:

【中文标题】如何触发包含三个重叠 div 元素的 :hover 过渡(维恩图)【英文标题】:How can I trigger a :hover transition that includes three overlapping div elements (Venn diagram) 【发布时间】:2021-12-05 14:34:32 【问题描述】:

我的问题是我有这个由三个 div 元素组成的维恩图,我想用:hover 缩放它们,这样当我将鼠标悬停在交叉点上时,在交叉点处相遇的所有圆圈都会缩放到我定义的值。目前我只能得到一个圆圈来缩放。

.circles-container 
  position: relative;
  width: 45.625rem;
  height: 45.625rem;


.circle-blue 
  position: absolute;
  left: 0rem;
  top: 0rem;
  width: 28.4375rem;
  height: 28.4375rem;
  background-color: rgba(187, 231, 254, 0.6);
  border-radius: 50%;


.circle-purple 
  position: absolute;
  right: 0rem;
  top: 0rem;
  width: 28.4375rem;
  height: 28.4375rem;
  background-color: rgba(211, 181, 229, 0.6);
  border-radius: 50%;


.circle-pink 
  position: absolute;
  right: 8.59375rem;
  left: 8.59375rem;
  bottom: 0rem;
  width: 28.4375rem;
  height: 28.4375rem;
  background-color: rgba(255, 212, 219, 0.6);
  border-radius: 50%;


.second-section-circle 
  transition: all, 1s;


.second-section-circle:hover 
  transform: scale(1.1);
<div class="circles-container">
  <div class="circle-blue second-section-circle"></div>
  <div class="circle-purple second-section-circle"></div>
  <div class="circle-pink second-section-circle"></div>
</div>

【问题讨论】:

您需要纯 CSS 解决方案吗? 没有 CSS 解决方案。至少我不知道。如果有人能证明我错了,那就太好了。 @DeonRich 检查这个:***.com/a/69623597/8620333 【参考方案1】:

一种纯 CSS 解决方案,需要更多元素和一个 CSS 变量来控制大小:

.circles-container 
  --s:150px; /* adjust this to control the size*/
  width:  var(--s);
  height: var(--s);
  margin:calc(var(--s)/3) auto;
  display:grid;

.circles-container > * 
  grid-area: 1/1;
  transition: all 1s;
  border-radius:50%;
  position:relative;

.circle-blue 
  background: rgba(187, 231, 254, 0.6);
  top:calc(var(--s)/3);

.circle-purple 
  background: rgba(211, 181, 229, 0.6);
  left:calc(0.866*calc(var(--s)/3));
  top: calc(-0.5 *calc(var(--s)/3));

.circle-pink 
  background: rgba(255, 212, 219, 0.6);
  right:calc(0.866*calc(var(--s)/3));
  top:  calc(-0.5 *calc(var(--s)/3));

.circles-container > *:nth-child(1) 
   top:calc(var(--s)/3);
   clip-path:circle(calc(var(--s)/2) at 21% 0%);

.circles-container > *:nth-child(2) 
   right:calc(0.866*calc(var(--s)/3));
   top:  calc(-0.5 *calc(var(--s)/3));
   clip-path:circle(calc(var(--s)/2) at 108% 50%);

.circles-container > *:nth-child(3) 
   left:calc(0.866*calc(var(--s)/3));
   top: calc(-0.5 *calc(var(--s)/3));
   clip-path:circle(calc(var(--s)/2) at 21% 100%);

.circles-container > *:nth-child(4) 
  clip-path: polygon(29% 38%, 50% 34%, 71% 38%, 64% 60%, 50% 74%, 36% 60%);

.circles-container > *:nth-child(-n + 4) 
  z-index:1;

.circles-container > *:nth-child(1):hover ~ .circle-pink,
.circles-container > *:nth-child(1):hover ~ .circle-blue,
.circles-container > *:nth-child(2):hover ~ .circle-pink,
.circles-container > *:nth-child(2):hover ~ .circle-purple,
.circles-container > *:nth-child(3):hover ~ .circle-blue,
.circles-container > *:nth-child(3):hover ~ .circle-purple,
.circles-container > *:nth-child(4):hover ~ *,
.circles-container > *:nth-child(n + 5):hover 
  transform: scale(1.15);
<div class="circles-container">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  
  <div class="circle-blue"></div>
  <div class="circle-purple"></div>
  <div class="circle-pink"></div>
</div>

为额外的 div 添加背景颜色以理解谜题:

.circles-container 
  --s:150px; /* adjust this to control the size*/
  width:  var(--s);
  height: var(--s);
  margin:calc(var(--s)/3) auto;
  display:grid;

.circles-container > * 
  grid-area: 1/1;
  transition: all 1s;
  border-radius:50%;
  position:relative;

.circle-blue 
  background: rgba(187, 231, 254, 0.6);
  top:calc(var(--s)/3);

.circle-purple 
  background: rgba(211, 181, 229, 0.6);
  left:calc(0.866*calc(var(--s)/3));
  top: calc(-0.5 *calc(var(--s)/3));

.circle-pink 
  background: rgba(255, 212, 219, 0.6);
  right:calc(0.866*calc(var(--s)/3));
  top:  calc(-0.5 *calc(var(--s)/3));

.circles-container > *:nth-child(1) 
   top:calc(var(--s)/3);
   clip-path:circle(calc(var(--s)/2) at 21% 0%);

.circles-container > *:nth-child(2) 
   right:calc(0.866*calc(var(--s)/3));
   top:  calc(-0.5 *calc(var(--s)/3));
   clip-path:circle(calc(var(--s)/2) at 108% 50%);

.circles-container > *:nth-child(3) 
   left:calc(0.866*calc(var(--s)/3));
   top: calc(-0.5 *calc(var(--s)/3));
   clip-path:circle(calc(var(--s)/2) at 21% 100%);

.circles-container > *:nth-child(4) 
  clip-path: polygon(29% 38%, 50% 34%, 71% 38%, 64% 60%, 50% 74%, 36% 60%);

.circles-container > *:nth-child(-n + 4) 
  z-index:1;

.circles-container > *:nth-child(1):hover ~ .circle-pink,
.circles-container > *:nth-child(1):hover ~ .circle-blue,
.circles-container > *:nth-child(2):hover ~ .circle-pink,
.circles-container > *:nth-child(2):hover ~ .circle-purple,
.circles-container > *:nth-child(3):hover ~ .circle-blue,
.circles-container > *:nth-child(3):hover ~ .circle-purple,
.circles-container > *:nth-child(4):hover ~ *,
.circles-container > *:nth-child(n + 5):hover 
  transform: scale(1.15);
<div class="circles-container">
  <div style="background:red;"></div>
  <div style="background:green;"></div>
  <div style="background:purple;"></div>
  <div style="background:black;"></div>
  
  <div class="circle-blue"></div>
  <div class="circle-purple"></div>
  <div class="circle-pink"></div>
</div>

【讨论】:

当您稍微移出重叠区域进入其中一个圆圈时,会发生轻微故障。似乎可以通过删除:nth-child(n + 5) 来解决此问题,以便所有额外区域也可以通过自己的悬停扩展。【参考方案2】:

我最终确定了Deon Rich's idea,以便圆圈对越界做出反应,而不是围绕它们描述的正方形的边界。

还添加了一个辅助函数和循环,以免手动列出图表中的所有圆圈。现在脚本代码不依赖于图中的圈数了。

https://codepen.io/glebkema/pen/OJjNwzd

let circlesElements = document.getElementsByClassName("second-section-circle");
let circlesInfo = [];

for (let elem of circlesElements) 
    circlesInfo.push(getCircleInfo(elem));


// console.log(circlesInfo);

window.addEventListener("mousemove", (e) => 
    for (let info of circlesInfo) 
        let deltaX = e.pageX - info.centerX;
        let deltaY = e.pageY - info.centerY;
        if (deltaX * deltaX + deltaY * deltaY <= info.radius2) 
            // if mouse is over element, scale it...
            info.elem.style.transform = "scale(1.2)";
         else 
            // otherwise, dont scale it...
            info.elem.style.transform = "scale(1)";
        
    
);

function getCircleInfo(elem) 
    let rect = elem.getBoundingClientRect();
    let radius = (rect.right - rect.left) / 2;
    return 
        elem: elem,
        centerX: (rect.right + rect.left) / 2,
        centerY: (rect.bottom + rect.top) / 2,
        radius2: radius * radius
    ;
.circles-container 
    position: relative;
    width: 45.625rem;
    height: 45.625rem;


.second-section-circle 
    position: absolute;
    width: 28.4375rem;
    height: 28.4375rem;
    border-radius: 50%;
    transition: all, 1s;


.circle-blue 
    left: 0rem;
    top: 0rem;
    background-color: rgba(187, 231, 254, 0.6);


.circle-pink 
    right: 8.59375rem;
    left: 8.59375rem;
    bottom: 0rem;
    background-color: rgba(255, 212, 219, 0.6);


.circle-purple 
    right: 0rem;
    top: 0rem;
    background-color: rgba(211, 181, 229, 0.6);
<div class="circles-container">
    <div class="second-section-circle circle-blue"></div>
    <div class="second-section-circle circle-purple"></div>
    <div class="second-section-circle circle-pink"></div>
</div>

【讨论】:

我建议不要使用 Math.pow() 对数字进行平方。最好只使用例如:((rect.right - rect.left) / 2) * ((rect.right - rect.left) / 2)。或者预先计算其中一些:let radius = (rect.right - rect.left) / 2,然后使用radius * radius。 Math.pow() 往往会慢一些,因为它设计用于处理任何数字,但平方是一个更简单的操作,因此不需要那么多开销。 @DarrelHoffman 我同意你的观点,直接乘法更快。我已经更新了代码。【参考方案3】:

您可以实现这一点,但您需要一点 javascript。别担心,没什么太复杂的。您可以做的是使用element.getBoundingClientRect() 方法获取每个圆圈的尺寸,就像这样......

let blue = document.querySelector(".circle-blue").getBoundingClientRect();
let purple = document.querySelector(".circle-purple").getBoundingClientRect();
let pink = document.querySelector(".circle-pink").getBoundingClientRect();

然后每次用户移动鼠标时,您可以测试鼠标是否在给定元素上,如果是,您可以缩放它,而不管哪个元素与另一个重叠,因此会导致任何圆圈缩放,包括交叉点中包含的圆圈...

let blue = document.querySelector(".circle-blue").getBoundingClientRect();
let purple = document.querySelector(".circle-purple").getBoundingClientRect();
let pink = document.querySelector(".circle-pink").getBoundingClientRect();

window.addEventListener("mousemove", (e) => 
  let x = e.pageX,y = e.pageY;

  //test blue...
  if (x > blue.left && x < blue.right && y > blue.top && y < blue.bottom) 
    // if mouse is over element, scale it...
    document.querySelector(".circle-blue").style.transform = "scale(1.2)";
   else 
    // otherwise, dont scale it...
    document.querySelector(".circle-blue").style.transform = "scale(1)";
  

  //test purple...
  if (x > purple.left && x < purple.right && y > purple.top && y < purple.bottom) 
    // if mouse is over element, scale it...
    document.querySelector(".circle-purple").style.transform = "scale(1.2)";
   else 
    // otherwise, dont scale it...
    document.querySelector(".circle-purple").style.transform = "scale(1)";
  

  //test pink...
  if (x > pink.left && x < pink.right && y > pink.top && y < pink.bottom) 
    // if mouse is over element, scale it...
    document.querySelector(".circle-pink").style.transform = "scale(1.2)";
   else 
    // otherwise, dont scale it...
    document.querySelector(".circle-pink").style.transform = "scale(1)";
  
);
.circles-container 
  position: relative;
  width: 45.625rem;
  height: 45.625rem;


.circle-blue 
  position: absolute;
  left: 0rem;
  top: 0rem;
  width: 28.4375rem;
  height: 28.4375rem;
  background-color: rgba(187, 231, 254, 0.6);
  border-radius: 50%;


.circle-purple 
  position: absolute;
  right: 0rem;
  top: 0rem;
  width: 28.4375rem;
  height: 28.4375rem;
  background-color: rgba(211, 181, 229, 0.6);
  border-radius: 50%;


.circle-pink 
  position: absolute;
  right: 8.59375rem;
  left: 8.59375rem;
  bottom: 0rem;
  width: 28.4375rem;
  height: 28.4375rem;
  background-color: rgba(255, 212, 219, 0.6);
  border-radius: 50%;


.second-section-circle 
  transition: all, 1s;
<div class="circles-container">
  <div class="circle-blue second-section-circle"></div>
  <div class="circle-purple second-section-circle"></div>
  <div class="circle-pink second-section-circle"></div>
</div>

【讨论】:

这存在碰撞箱问题,因为您将圆圈视为正方形。您可以通过使用欧几里得距离公式来解决此问题 - 如果到圆心的绝对距离小于其宽度的一半,则展开它。 是的,我知道这些盒子稍微限制了它的功能。感谢您的洞察力

以上是关于如何触发包含三个重叠 div 元素的 :hover 过渡(维恩图)的主要内容,如果未能解决你的问题,请参考以下文章

添加类时div重叠

重叠时如何滚动div

WPF中如何让一个元素与另一个元素重叠?

在元素边界出触发不断触发hover事件如何解决?

HTML5开发-如何实现两个div的重叠

css 在optin显示之前,将包含YouTube视频集的div加载到我们手动触发的optin后面进行自动播放。我们将加载重叠