实现一个设计,我遇到了一个棘手的问题。
概述
我有一个包含项目的菜单,每个项目都包含一个悬停时打开的面板。其中每一个的内容都是动态的,因此每个面板的宽度可以(并且是)不同的。面板有渐变作为背景颜色:
background: linear-gradient(135deg, #579AD1 0%, #1F679D 100%);
我想在面板正上方显示一个向上指向菜单项的箭头。我通过在菜单项元素中放置一个 DIV 并定位它来做到这一点,使其位于减去箭头宽度的 50% 处,从而在菜单项下方整齐地居中。如果面板超过外部容器的宽度,它会使用负
margin-left
向左移动,效果很好,确保面板在页面的外部容器内。
问题
箭头的颜色应该平滑地融入面板。然而,正如您在我的示例中看到的那样,箭头的颜色(作为固定的背景颜色)将或多或少地融入面板的背景中。面板越宽,距离左侧越远,渐变颜色与箭头颜色的差异就越大,这意味着箭头不能很好地融入渐变背景。
到目前为止我尝试了什么
试图解决这个问题,我考虑/尝试了三件事:
在箭头元素上使用
clip-path
,裁剪出一个三角形,箭头元素的宽度和梯度与面板相同。然而,这不起作用,因为我需要箭头在左上角和右上角有一个 1px 的白色边框。
将面板复制到画布中,然后读取所需位置的像素颜色。这完全失败了,它似乎只适用于图像。
编写一个可以传递两个参数的函数:宽度和箭头位置。然后,该函数将根据面板的背景渐变,通过近似给定箭头位置处的渐变颜色来计算箭头的最佳背景颜色。
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>
谢谢!
以下是否符合您的需求?我用了
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>