jquery 计算放置在DIV上方的具有背景渐变的元素的匹配背景颜色

enyaitl3  于 2023-04-29  发布在  jQuery
关注(0)|答案(2)|浏览(115)

在实现一个设计时,我遇到了一个棘手的问题。

概览

我有一个菜单的项目,其中每一个包含一个面板,打开后悬停。每个面板的内容都是动态的,因此每个面板的宽度可以(并且是)不同。面板具有渐变色作为背景色:

background: linear-gradient(135deg, #579AD1 0%, #1F679D 100%);

在面板的正上方,我想显示一个向上指向菜单项的箭头。我通过在菜单项元素中放置一个DIV并对其进行定位,使其位于箭头宽度减去50%的位置,从而在菜单项下方整齐地居中。如果面板超出了外部容器的宽度,它会使用负的margin-left向左移动,这可以正常工作,确保面板在页面的外部容器内。

问题

箭头的颜色应该平滑地融入面板。但是,正如你在我的例子中看到的,箭头的颜色(作为一个固定的背景颜色)将或多或少地融入面板的背景中。面板越宽,并且越靠左,渐变颜色与箭头颜色的差异就越大,这意味着箭头不能很好地融入渐变背景。

迄今为止我所做的一切

为了解决这个问题,我考虑/尝试了三件事:
1.在箭头元素上使用clip-path,剪切出一个三角形,箭头元素与面板具有相同的宽度和梯度。然而,这并不起作用,因为我需要的箭头有一个1 px的左上角和右上角的白色边框。
1.将面板复制到画布上,然后阅读所需位置的像素颜色。这完全失败了,它似乎只适用于图像。
1.写一个函数,我可以传递两个参数:宽度和箭头位置。然后,该函数将通过基于面板的背景梯度近似给定箭头位置处的梯度颜色来计算箭头的最佳背景颜色。

calculateArrowColor: function(divWidth, arrowPosX) {

     const color1 = [78, 138, 188];
     const color2 = [49, 112, 162];
     const gradientSteps = divWidth / 2;
     const stepSize = 1 / gradientSteps;
     const arrowPosStep = arrowPosX / divWidth;
     const arrowColor = [];

     for (let i = 0; i < 3; i++) {
         const color1Value = color1[i];
         const color2Value = color2[i];
         let arrowColorValue;
         if (arrowPosStep < 0.5) {
             const currentStep = arrowPosStep / stepSize;
             const colorDifference = color1Value - color2Value;
             arrowColorValue = color1Value - (currentStep * colorDifference);
         } else {
             const currentStep = (arrowPosStep - 0.5) / stepSize;
             const colorDifference = color2Value - color1Value;
             arrowColorValue = color1Value + (currentStep * colorDifference);
         }
         arrowColor.push(Math.round(arrowColorValue));
     }

     return `rgb(${arrowColor.join(",")})`;

 };

不幸的是,我的功能不起作用,我不知道为什么。它返回超出范围的值,这些值没有意义。
也许我的方法没那么聪明。
我正在为这个棘手的问题寻找一个好的解决方案。
下面是一个可行的片段:(单击面板可将其向左移动,右键单击可增加其宽度)

$(document).ready(function(){
  $('.panel').on('click', function(){
    $(this).css('margin-left', '-200px');
  }).on('contextmenu', function(e){
    $(this).css('width', '600px');
    e.preventDefault();
  });
});
ul, li {
  list-style: none;
  margin: 0;
  padding: 0;
}
ul {
  width: 600px;
  margin: 0 auto;
  background: #369;
}
ul > li {
  color: #fff;
  padding: 5px 10px;
  display: inline-block;
}
div.label {
  position: relative;
}
div.arrow {
  position: relative;
  top: 20px;
  left: 50%;
  z-index: 10;
}
div.arrow:before, div.arrow:after {
  border: solid transparent;
  content: " ";
  display: block;
  height: 0;
  position: absolute;
  pointer-events: none;
  width: 0;
  bottom: 100%;
  border-color: transparent;
  border-bottom-color: #fff;
  left: 0;
  margin-left: -11px;
  border-width: 11px;
}
div.arrow:after {
  border-color: rgba(255, 255, 255, 0);
  border-bottom-color: #69c;
  left: 0;
  margin-left: -10px;
  border-width: 10px;
}
div.panel {
  background: linear-gradient(135deg, #579AD1 0%, #1F679D 100%);
  min-width: 320px;
  height: 120px;
  position: absolute;
  top: 38px;
  left: 0;
  box-shadow: 0px 0px 9.5px 0.5px rgba(0, 0, 0, 0.24);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li>
    <div class="label">
      <span>MENU ITEM</span>
      <div class="arrow"></div>
      <div class="panel"></div>
    </div>
  </li>
</ul>

谢谢你!

dw1jzc5e

dw1jzc5e1#

因为你打算使用JS来计算颜色,我建议你让CSS来选择颜色,但使用一些JS来找出在哪里画箭头。
这段代码忽略了箭头元素,它可能不应该出现在DOM中,因为它没有特定的含义,只是一个视觉提示。
相反,它在面板上放置了一个after伪元素,具有几乎相同的尺寸和背景梯度,并将其放置在面板上方。然后使用clip-path来创建尖头。
为了得到箭头的白色轮廓,将其作为before伪元素背景白色,并将其裁剪为略大于彩色箭头。[这个片段显示它在红色,因为它是否则很难看到它对相当微弱的阴影]。
注意,箭头有一个渐变色,而不仅仅是一种颜色,但它太小了,以至于不能真正注意到。
你可能想参数化箭头的大小--你会看到14 px和变化在这里到处都是,如果在CSS变量中设置会更灵活。

<style>
  ul,
  li {
    list-style: none;
    margin: 0;
    padding: 0;
  }
  
  ul {
    width: 600px;
    margin: 0 auto;
    background: #369;
  }
  
  ul>li {
    color: #fff;
    padding: 5px 10px;
    display: inline-block;
  }
  
  div.label {
    position: relative;
  }
  
  div.panel {
    min-width: 320px;
    height: 120px;
    position: absolute;
    top: 38px;
    left: 0;
    box-shadow: 0px 0px 9.5px 0.5px rgba(0, 0, 0, 0.24);
    background: linear-gradient(135deg, #579AD1 0%, #1F679D 100%);
  }
  
  div.panel::before {
    content: '';
    background: red;
    position: absolute;
    left: 0;
    top: -15px;
    width: 100%;
    height: calc(100% + 15px);
    z-index: 1;
    clip-path: polygon(0 15px, calc(var(--midpoint) - 14px - 1px) 15px, var(--midpoint) 0, calc(var(--midpoint) + 15px) 15px, 100% 15px, 100% 16px, 0 16px);
  }
  
  div.panel::after {
    rdisplay: none;
    content: '';
    background: linear-gradient(135deg, #579AD1 0%, #1F679D 100%);
    position: absolute;
    left: 0;
    top: -14px;
    width: 100%;
    height: calc(100% + 14px);
    z-index: 1;
    clip-path: polygon(0 14px, calc(var(--midpoint) - 14px) 14px, var(--midpoint) 0, calc(var(--midpoint) + 14px) 14px, 100% 14px, 100% 15px, 0 15px);
  }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li>
    <div class="label">
      <span>MENU ITEM</span>
      <div class="arrow"></div>
      <div class="panel"></div>
    </div>
  </li>
</ul>
<script>
  function positionArrows() {
    const labels = document.querySelectorAll('.label');
    for (let i = 0; i < labels.length; i++) {
      const labelLeft = labels[i].getBoundingClientRect().left;
      const labelWidth = labels[i].getBoundingClientRect().width;
      const panel = labels[i].querySelector('.panel');
      const panelLeft = panel.getBoundingClientRect().left;
      labels[i].style.setProperty('--midpoint', (labelLeft - panelLeft + labelWidth / 2) + 'px');
    }
  }
  $(document).ready(function() {
    $('.panel').on('click', function() {
      $(this).css('margin-left', '-200px');
      positionArrows();
    }).on('contextmenu', function(e) {
      $(this).css('width', '600px');
      e.preventDefault();
      positionArrows();
    });
  });
  positionArrows();
  window.onresize = positionArrows;
</script>
wi3ka0sx

wi3ka0sx2#

以下是否符合您的需求?我使用了clip-path并调整了边框,主要关键是background-attachment: fixed;

$(document).ready(function() {
  $('.panel').on('click', function() {
    $(this).css('margin-left', '-200px');
  }).on('contextmenu', function(e) {
    $(this).css('width', '600px');
    e.preventDefault();
  });
});
ul,
li {
  list-style: none;
  margin: 0;
  padding: 0;
}

ul {
  width: 600px;
  margin: 0 auto;
  background: #369;
}

ul>li {
  color: #fff;
  padding: 5px 10px;
  display: inline-block;
}

div.label {
  position: relative;
}

div.arrow {
  background: linear-gradient(135deg, #579AD1 0%, #1F679D 100%);
  min-width: 22px;
  height: 11px;
  position: absolute;
  top: 28px;
  left: 50%;
  box-shadow: 0px 0px 9.5px 0.5px rgba(0, 0, 0, 0.24);
  clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
  z-index: 10;
  background-attachment: fixed;
  margin-left: -11px;
  /*negative margin-left which is half of the width of the element to be positioned*/
}

div.arrow:before,
div.arrow:after {
  border: solid #fff;
  content: " ";
  display: block;
  height: 0;
  position: relative;
  pointer-events: none;
  width: 0;
  bottom: 100%;
  border-color: #fff;
  border-bottom-color: transparent;
  left: 0px;
  margin-left: 0px;
  border-width: 13px;
  top: -13px;
}

/*
div.arrow:after {
  border-color: rgba(255, 255, 255, 0);
  border-bottom-color: #69c;
  left: 0;
  margin-left: -10px;
  border-width: 10px;
}*/

div.panel {
  background: linear-gradient(135deg, #579AD1 0%, #1F679D 100%);
  min-width: 320px;
  height: 120px;
  position: absolute;
  top: 38px;
  left: 0;
  box-shadow: 0px 0px 9.5px 0.5px rgba(0, 0, 0, 0.24);
  background-attachment: fixed;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li>
    <div class="label">
      <span>MENU ITEM</span>
      <div class="arrow"></div>
      <div class="panel"></div>
    </div>
  </li>
</ul>

相关问题