使用背景渐变计算放置在 DIV 上方的元素的匹配背景颜色

问题描述 投票:0回答:1

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

概述

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

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

我想在面板正上方显示一个向上指向菜单项的箭头。我通过在菜单项元素中放置一个 DIV 并定位它来做到这一点,使其位于减去箭头宽度的 50% 处,从而在菜单项下方整齐地居中。如果面板超过外部容器的宽度,它会使用负

margin-left
向左移动,效果很好,确保面板在页面的外部容器内。

问题

箭头的颜色应该平滑地融入面板。然而,正如您在我的示例中看到的那样,箭头的颜色(作为固定的背景颜色)将或多或少地融入面板的背景中。面板越宽,距离左侧越远,渐变颜色与箭头颜色的差异就越大,这意味着箭头不能很好地融入渐变背景。

到目前为止我尝试了什么

试图解决这个问题,我考虑/尝试了三件事:

  1. 在箭头元素上使用

    clip-path
    ,裁剪出一个三角形,箭头元素的宽度和梯度与面板相同。然而,这不起作用,因为我需要箭头在左上角和右上角有一个 1px 的白色边框。

  2. 将面板复制到画布中,然后读取所需位置的像素颜色。这完全失败了,它似乎只适用于图像。

  3. 编写一个可以传递两个参数的函数:宽度和箭头位置。然后,该函数将根据面板的背景渐变,通过近似给定箭头位置处的渐变颜色来计算箭头的最佳背景颜色。

     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>

谢谢!

javascript html jquery css clip-path
1个回答
0
投票

以下是否符合您的需求?我用了

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>

© www.soinside.com 2019 - 2024. All rights reserved.