使当前活动侧边栏元素的背景透明

Make the background of the current active sidebar element transparent

我最近创建了这个侧边栏,但是我不知道我应该如何使白色背景(仅当前活动元素的)透明,以便如果主要内容有背景图像,它将显示在侧边栏后面(否则看起来像第二张图,这不是我想要实现的)。

这个侧边栏的工作方式是,如果一个项目是 'active' 它被赋予白色背景颜色(在深色侧边栏之上),它的左边框是圆形的,然后它的 ::after 和 ::在元素绝对定位并给出一个圆角框阴影(也是白色)之前,它给出了圆角右角的外观。

The sidebar with a white background

The sidebar with an image as a background (not what I was looking for)

An example of how it should look like, using mix-blend-mode (This is still not what I'm looking for too many things have a semi-transparent background)

我已经用 mix-blend-mode 创建了一个半工作版本,但是这并没有做我想做的一切(它还改变了其他元素的颜色,那些不是'活跃)。 我也尝试过使用 mask 和 clip 属性进行试验,并且我很确定更可靠的解决方案需要其中之一,但是我不确定我将如何去做。

如果有人知道如何让我知道,我将不胜感激。提前致谢!

代码(苗条):

<script lang="ts">
    import { Ripple } from '$lib/ripple/Ripple';
    import { isThemeDark } from '$lib/theme';
    import { Tooltip } from '$lib/tooltip/Tooltip';

    // The props for the background and foreground colors
    export let backgroundColorLight: string = 'rgb(30 41 59)';
    export let foreColorLight: string = 'white';
    export let backgroundColorDark: string = 'rgb(241, 245, 241)';
    export let foreColorDark: string = 'rgb(30 41 59)';

    let sidebar: HTMLElement;

    // Depending on the theme sets the color
    onMount(() => {
        if ($isThemeDark) {
            sidebar.style.setProperty('--forecolor', foreColorDark);
            sidebar.style.setProperty('--background-color', backgroundColorDark);
        } else {
            sidebar.style.setProperty('--forecolor', foreColorLight);
            sidebar.style.setProperty('--background-color', backgroundColorLight);
        }
    });

    // The Material Design Icons
    import { mdiCog, mdiExitToApp, mdiHomeOutline, mdiMenu, mdiViewDashboard } from '@mdi/js';
    import { onMount } from 'svelte';

    import MdiIcon from './MdiIcon.svelte';

    let open = false;

    // The sidebar items (apart from the burger menu and the logout button)
    let sidebarItems: {
        label: string;
        iconPath: string;
        path: string;
    }[] = [
        {
            label: 'Home',
            iconPath: mdiHomeOutline,
            path: '/'
        },
        {
            label: 'Dashboard',
            iconPath: mdiViewDashboard,
            path: '/dashboard'
        },
        {
            label: 'Settings',
            iconPath: mdiCog,
            path: '/account/settings'
        }
    ];
</script>

<!--The sidebar code-->
<!--use:Ripple and use:Tooltip don't matter for this question so I haven't included the code.-->
<aside class="sidebar" class:open bind:this={sidebar}>
    <button class="sidebar-item">
        <span
            class="sidebar-icon"
            on:mousedown={() => {
                open = !open;
            }}
            use:Ripple={{ backgroundColor: 'white' }}><MdiIcon path={mdiMenu} /></span
        >
    </button>
    <!--Loops through the sidebar items and places an <a> for each one-->
    {#each sidebarItems as item}
        <a class="sidebar-item" class:active={window.location.pathname == item.path} href={item.path}>
            <span
                class="sidebar-icon"
                use:Tooltip={{
                    tippy: { content: item.label, arrow: false, placement: 'right' },
                    canShow: !open
                }}
                use:Ripple={{ backgroundColor: 'white' }}><MdiIcon path={item.iconPath} /></span
            >
            <p class="sidebar-text">{item.label}</p>
        </a>
    {/each}

    <div class="sidebar-item">
        <span
            class="sidebar-icon"
            use:Tooltip={{
                tippy: { content: 'Log out', arrow: false, placement: 'right' },
                canShow: !open
            }}
            use:Ripple={{ backgroundColor: 'white' }}><MdiIcon path={mdiExitToApp} /></span
        >
        <p class="sidebar-text">Log out</p>
    </div>
</aside>

<style lang="scss">
    .sidebar {
        padding: 0;
        color: var(--forecolor);
        background-color: var(--background-color);
        margin: 0;
        width: 5rem;
        height: 100vh;
        position: fixed;
        z-index: 100;
        display: flex;
        flex-direction: column;
        transition: 0.2s ease;
    }

    .sidebar.open {
        width: 16rem;
    }

    .sidebar-item {
        text-decoration: none;
        display: flex;
        position: relative;
        align-items: center;
        height: 5rem;
        // This is what makes the left border rounded
        border-top-left-radius: 16px;
        border-bottom-left-radius: 16px;
    }

    .sidebar-item:nth-last-child(1) {
        margin-top: auto;
    }

    .sidebar-item.active {
        margin-left: 0.2rem;
        color: var(--background-color);
        background-color: var(--forecolor);
        // The ::before and ::after are absolutely positioned and a box shadow is given to them to make the right white rounded border for active elements
        &::before,
        &::after {
            content: '';
            position: absolute;
            right: 0;
            width: 30px;
            height: 30px;
            border-radius: 50%;
        }
        &::before {
            top: -30px;
            clip-path: polygon(0% 100%, 100% 0%, 100% 100%);
            box-shadow: 15px 15px 0 var(--forecolor);
        }
        &::after {
            bottom: -30px;
            box-shadow: 15px -15px 0 var(--forecolor);
            clip-path: polygon(0% 0%, 100% 0%, 100% 100%);
        }
    }

    // Secondary code, just to prevent the sidebar icons from looking crazy, and to make it so if the sidebar is closed the text won't show
    .sidebar-icon {
        width: 3.2rem;
        height: 3.2rem;
        display: flex;
        flex-shrink: 0;
        border-radius: 50%;
        padding: 0.6rem;
        margin: -0.6rem 0.9rem;
    }
    .sidebar-text {
        display: none;
    }
    .sidebar.open .sidebar-text {
        display: block;
    }
</style>

我已经看过其他 Whosebug 问题,但是 none 中讨论的只是让部分背景透明,而不是整个背景。

这是一个相当复杂的解决方案,可能仍需要根据您的目的进行一些调整,但希望它符合您的需求。本质上,您需要活动元素的背景是透明的。如果没有圆形切口,这将不是问题。

我的解决方案涉及一组伪元素来覆盖隐藏部分。一个用于顶部,一个用于底部,另一个在左侧的包装纸中。尽管如此,元素本身变得相当大。

编辑:我无法使此处的预览正常工作,所以这是我的 pen。 (编辑 2:现在有效。)

/* Just some setup for the example */
body {
  margin: 0;
}

.page {
  background: url('https://i.ibb.co/jzfvkR8/wp5961294-aesthetic-minecraft-pc-wallpapers.png');
  height: 300px;
}

.sidebar {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 100%;
  width: 64px;
}

.sidebar::after {
  content: '';
  display: block;
  flex: 1 0 auto;
  background: white;
  width: 100%;
}

/* Relevant stuff */
.item {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  background: white;
  font-weight: 700;
}

/* Rounded corners top/bottom */
.item::before,
.item::after {
  content: '';
  display: block;
  background: white;
  height: 16px;
  width: 100%;
}

/* Bottom-right rounded corner for top pseudo-element */
.item.active::before {
  border-radius: 0 0 16px 0;
}

/* Top-right rounded corner for bottom pseudo-element */
.item.active::after {
  border-radius: 0 16px 0 0;
}

/* Account for top/bottom pseudo-element space */
.item > div {
  padding: 16px 0;
}

/* Let background image show through */
.item.active {
  background: none;
}

/* Rounded inset corners on the left side */
.item.active > div::before {
  /* Make sure to not block the interactive menu item */
  pointer-events: none;
  content: '';
  display: block;
  position: absolute;
  top: 16px; 
  left: 1px;
  background: transparent;
  border-radius: 16px 0 0 16px;
  /* Account for top/bottom pseudo-elements */
  height: calc(100% - 32px);
  width: 16px;
  box-shadow: -16px 0 0 16px #ffffff;
}
<div class="page">
  <div class="sidebar">
    <div class="item">
      <div>FOO</div>
    </div>
    <div class="item active">
      <div>BAR</div>
    </div>
    <div class="item">
      <div>BAZ</div>
    </div>
  </div>
</div>

多亏了 Tetrakern 的回答,我才能够弄清楚我做错了什么。经过一些修补后,我找到了一个解决方案,它实现了我需要的一切,同时也避免了对元素本身有很大的填充。 我对原始设计所做的更改:

  1. 使用 .sidebar::after 使侧边栏的底部中间具有白色背景色。
  2. 从 .sidebar
  3. 中删除了 color: 和 background-color: 属性
  4. 删除了用于呈现圆角边框的旧代码
  5. 用JavaScript到select上一个和下一个children(我本来可以用css来下一个child但是前一个会使用 :has 这是实验性的,所以我选择坚持使用 JS),然后给那些边界半径。
  6. 使用 .sidebar-item.active::before 来创建我想要的左圆角边框。

The codepen

它并不完美(因为我制作它只是为了展示新代码的样子),但它确实有效 ;-)

HTML:

<!-- Inter Font -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" />
<!--Code-->
<div class="page">
  <aside class="sidebar">
    <div class="sidebar-menu">Menu</div>
    <div class="sidebar-item">
      <p class="sidebar-text">Home</p>
    </div>
    <div class="sidebar-item active">
      <p class="sidebar-text">View</p>
    </div>
    <div class="sidebar-item">
      <p class="sidebar-text">Quit</p>
    </div>
    <div class="sidebar-item">
      <p class="sidebar-text">Extra</p>
    </div>
  </aside>
</div>

CSS:

body {
  margin: 0;
  padding: 0;
}

.page {
  font-family: "Inter";
  width: 100%;
  height: 100vh;
  background-size: cover;
  background-image: linear-gradient(
      rgba(0, 23, 238, 0.63),
      rgba(48, 0, 239, 0.4)
    ),
    url("https://cdn.discordapp.com/attachments/533643380790394881/912783535067254824/index-background-light.jpg");
}

.sidebar {
  padding: 0;
  margin: 0;
  width: 5rem;
  height: 100vh;
  position: fixed;
  z-index: 100;
  display: flex;
  flex-direction: column;
}
.sidebar::after {
  content: "";
  display: block;
  flex: 1 0 auto;
  background: white;
  width: 100%;
}

.sidebar-menu {
    text-decoration: none;
  background-color: white;
  display: flex;
  position: relative;
  align-items: center;
  justify-content: center;
  height: 2.5rem;
  padding: 1rem 0rem;
}
.sidebar-item {
  text-decoration: none;
  background-color: white;
  display: flex;
  position: relative;
  align-items: center;
  justify-content: center;
  height: 2.5rem;
  padding: 1rem 0rem;
}

.sidebar-item.active {
  color: white;
  background-color: transparent;
}

.sidebar-item.active::before {
  content: "";
  display: block;
  position: absolute;
  left: 2px;
  background: transparent;
  border-radius: 16px 0 0 16px;
  height: 100%;
  width: 16px;
  box-shadow: -16px 0 0 16px #fff;
}

JS:

let activeGuy = document.querySelector(".sidebar-item.active");
let sidebarItems = document.querySelectorAll(".sidebar > *");
for (let i = 0; i < sidebarItems.length; i++) {
  sidebarItems[i].addEventListener("mouseenter", (ev) => {
    activeGuy.classList.remove("active");
    activeGuy = ev.target;
    activeGuy.classList.add("active");
    calculateBorderRadius();
  });
}
calculateBorderRadius();
function calculateBorderRadius() {
  for (let i = 0; i < sidebarItems.length; i++) {
    sidebarItems[i].style.borderRadius = "0";
  }
  let prev = activeGuy.previousElementSibling;
  let next = activeGuy.nextElementSibling;
  prev.style.borderBottomRightRadius = "20px";
  next.style.borderTopRightRadius = "20px";
}