最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 对角线分割的圆锥渐变式可变长宽比卡片

    正文概述 掘金(霜羽Hoarfroster)   2021-07-19   516

    我最近遇到了一个有趣的问题 —— 我需要实现一个具有可变纵横比(由用户决定)的卡片,且纵横比值定义在了 --ratio 这个自定义属性中。具有特定纵横比的卡片是 CSS 中的一个经典问题,也是近年来变得容易解决的问题,尤其是有了 aspect-ratio 之后,但这里棘手的部分是我们需要在每张卡片沿对角线交点处分别添加一个圆锥渐变,如图:

    对角线分割的圆锥渐变式可变长宽比卡片

    用户设置的纵横比卡片。

    这里的挑战是,用 linear-gradient() 对沿着可变纵横比框的对角线做唐突的改变,比较容易,例如使用像“向左上角”这样的方向随纵横比变化,但 conic-gradient() 需要一个角度或一个百分比来表示它绕了一整圈走了多远的渐变并不好构建。

    简单的解决方案

    CSS 规范现在包括了 三角函数和反三角函数,它可以在这里帮助我们 —— 对角线与垂直线的角度是纵横比 atan(var(--ratio)) 的反正切(矩形的左边缘和上边缘与对角线形成直角三角形,其中对角线与垂直线形成的角度的切线是宽度超过高度 —— 正是我们的纵横比)。

    对角线分割的圆锥渐变式可变长宽比卡片

    对角线与垂直线(边)的夹角。

    把它写成代码,我们有:

    --ratio: 3/ 2;
    aspect-ratio: var(--ratio);
    --angle: atan(var(--ratio));
    background:
        /* below the diagonal */
        conic-gradient(from var(--angle) at 0 100%,
            #319197, #ff7a18, #af002d  calc(90deg - var(--angle)), transparent 0%),
        /* above the diagonal */
        conic-gradient(from calc(.5turn + var(--angle)) at 100% 0,
            #ff7a18, #af002d, #319197 calc(90deg - var(--angle)));
    

    然而,目前没有浏览器实现三角函数和反三角函数,所以这个简单的解决方案我们也只能想想,留着未来实现。

    JavaScript 解决方案

    我们当然可以使用 JavaScript 中的 --ratio 值来计算 --angle

    let angle = Math.atan(1 / ratio.split('/').map(c => +c.trim()).reduce((a, c) => c / a, 1));
    document.body.style.setProperty('--angle', `${+(180 * angle / Math.PI).toFixed(2)}deg`)
    

    但是如果使用 JavaScript 不行呢?如果我们真的需要一个纯 CSS 解决方案怎么办?好吧,这有点麻烦,但我们还是可以做到!

    hacky CSS 解决方案

    这是我从 SVG 渐变的特殊性中得到的一个想法,老实说,当我第一次看到这个问题 时,我发现它非常令人沮丧.

    假设我们有一个从底部到顶部的 50% 的渐变(因为在 CSS 中,这是一个角度为 的渐变)。现在假设我们在 SVG 中有相同的渐变,我们将两个渐变的角度更改为相同的值。

    在 CSS 中,这是:

    linear-gradient(45deg, var(--stop-list));
    

    在 SVG 中,我们有:

    <linearGradient id='g' y1='100%' x2='0%' y2='0%'
                    gradientTransform='rotate(45 .5 .5)'>
        <!-- 渐变停止 -->
    </linearGradient>
    

    如下所示,这两个不会给我们相同的结果。虽然 CSS 渐变实际上是在 45°,旋转了相同 45° 的 SVG 渐变沿着对角线在橙色和红色之间有明显的过渡,即使我们的盒子不是方形的,所以对角线不是在 45°

    对角线分割的圆锥渐变式可变长宽比卡片

    45° CSS 与 SVG 渐变(例子)。

    这是因为我们的 SVG 渐变被绘制在一个 1x1 方形框内,旋转了 45°,这使得沿着方形对角线从橙色突然变为红色。然后这个正方形被拉伸以适应矩形,这基本上改变了对角线的角度。

    基本思路

    我们不能在这里使用 SVG,因为它只有线性和径向渐变,而没有圆锥渐变。但是,我们可以将 CSS 圆锥渐变放在一个方形框中,并使用 45° 角使它们沿对角线相交:

    aspect-ratio: 1/ 1;
    width: 19em;
    background: 
      /* 对角线之下 */
      conic-gradient(from 45deg at 0 100%, 
          #319197, #ff7a18, #af002d 45deg, transparent 0%), 
      /* 对角线之上 */
      conic-gradient(from calc(.5turn + 45deg) at 100% 0, 
          #ff7a18, #af002d, #319197 45deg);
    

    然后我们可以使用缩放 transform 来拉伸这个方框 —— 诀窍是 3/2 中的 / 在用作 aspect-ratio 值时是一个分隔符,但在 calc() 中被解析为除法:

    --ratio: 3/ 2;
    transform: scaley(calc(1/(var(--ratio))));
    

    我们可以在下面嵌入的可编辑代码中更改 --ratio 的值来查看,这样,两个圆锥渐变总是沿对角线相交:CodePen

    对角线分割的圆锥渐变式可变长宽比卡片

    在 Firefox 中启用标志。

    这种方法的问题以及如何解决这些问题

    不过,缩放实际的 .card 元素很少是一个好主意。对于我的用例,卡片位于网格上并且在它们上设置方向比例会弄乱布局(网格单元仍然是方形的,即使我们已经缩放了其中的 .card 元素)。它们也有被 scaley() 函数奇怪地拉伸的文本内容。

    对角线分割的圆锥渐变式可变长宽比卡片

    缩放实际卡片的问题(例子)

    解决方案是为实际卡片提供所需的 aspect-ratio,并使用绝对定位的 ::before 放置在文本内容后面(z-index: -1)以创建我们的 background。这个伪元素获得它的 .card 父元素的 width 并且最初是正方形的。我们还设置了之前的方向缩放和圆锥梯度。请注意,由于我们绝对定位的 ::before 与它的 .card 父级的上边缘顶部对齐,我们也应该相对于这条边缘缩放它(transform-origin 需要有一个值沿 y 轴为 0,而 x 轴值无关紧要,可以是任何值)。

    body {
        --ratio: 3/ 2;
        /* 其他装饰用的布局样式 */
    }
    
    .card {
        position: relative;
        aspect-ratio: var(--ratio);
    
        &::before {
            position: absolute;
            z-index: -1; /* 移到文字下方 */
    
            aspect-ratio: 1/ 1; /* 让卡片成为正方形 */
            width: 100%;
    
            /* 让它缩放到它所对其的顶部边缘 */
            transform-origin: 0 0;
            /* 使用 Transform 给他指定的缩放比 */
            transform: scaley(calc(1 / (var(--ratio))));
            /* 设置背景 */
            background: /* 对角线之下 */
                    conic-gradient(from 45deg at 0 100%,
                            #319197, #af002d, #ff7a18 45deg, transparent 0%),
                        /* 对角线之上 */
                    conic-gradient(from calc(.5turn + 45deg) at 100% 0,
                            #ff7a18, #af002d, #319197 45deg);
            content: '';
        }
    }
    

    这要好得多,因为它可以在下面的嵌入中看到,它也是可编辑的,因此我们可以修改 --ratio 并查看当我们更改其值时一切将如何完美地适应。

    CodePen 供代码参考与效果预览。

    内边距问题

    由于我们没有在卡片上设置 padding,文本可能会一直延伸到边缘,甚至稍微超出边界,因为它有点倾斜。

    对角线分割的圆锥渐变式可变长宽比卡片

    缺少 padding 会导致问题。

    这应该不会太难修复,对吧?我们只是添加了一个 padding,对吧?好吧,当我们这样做时,我们发现布局失效了!

    对角线分割的圆锥渐变式可变长宽比卡片

    添加 padding 会破坏布局。(例子)

    这是因为我们在 .card 元素上设置的 aspect-ratio 是由 box-sizing 指定的 .card 框的纵横比。由于我们没有明确设置任何 box-sizing 值,它的当前值是默认值也就是 content-box。在这个框周围添加一个相同值的 padding 会给我们一个不同纵横比的 padding-box,让它不再与其它的元素的 ::before 重合。

    为了更好地理解这一点,假设我们的 aspect-ratio4/1content-box 的宽度为 16rem256px)。这意味着 content-box 的高度是这个宽度的四分之一,计算得出结果是 4rem (64px)。所以 content-box 是一个 16rem×4rem256px×64px)大的矩形。

    现在假设我们沿着每条边添加一个 1rem16px)的 padding。现在,padding-box 的宽度为 18rem288px,如上面的动画 GIF 所示)—— 计算得出 content-box 的宽度为 16rem 256px) ,再加上左侧的 1rem (16px) 和来自 padding 的右侧的 1rem。类似地,padding-box 的高度为 6rem96px)—— 计算得出 content-box 的高度,即 4rem64px),再加上顶部的 1rem 16px)和底部的 1rem padding

    这意味着 padding-box 是一个 18rem×6rem288px×96px)的矩形,并且由于 18 = 3⋅6,它的纵横比为 3/1,与我们为 aspect-ratio 属性设置的 4/1 值不一样!同时,::before 伪元素的宽度等于其父元素 padding-box 的宽度(我们计算为 18rem288px),而且它的纵横比(通过缩放设置的)仍然是 4/1,所以它的视觉高度计算可得是 4.5rem72px)。这就解释了为什么用这个伪元素创建的 background —— 垂直缩小到一个 18rem×4.5rem288px×72px)的矩形 —— 现在却比实际的卡片 —— 一个带有 padding18rem×6rem288px× 96px) 矩形 —— 要小。

    因此,看起来解决方案非常简单 —— 我们只需要将 box-sizing 设置为 border-box 就可以解决我们的问题,因为这会在这个盒子上应用 aspect-ratio(与 padding-box 当我们没有 border 时一致)。

    果然,这可以解决问题……但仅限于 Firefox!

    对角线分割的圆锥渐变式可变长宽比卡片

    显示 Chromium(上图)和 Firefox(下图)之间的区别。

    文本应该垂直居中对齐,因为我们给了 .card 元素一个网格布局,并在它们上设置了 place-content: center。然而,这不会发生在 Chromium 浏览器中。当我们删除最后一个声明时,这变得更加明显 —— 不知何故,卡片网格中的单元格也获得了 3/1 的纵横比,并溢出了卡片的 content-box

    对角线分割的圆锥渐变式可变长宽比卡片

    使用 place-content: center 与否来检查卡片的网格。(例子)

    幸运的是,这是一个 已知的 Chromium 错误,并且应该可以在未来几个月内得到修复。

    与此同时,我们可以做的就是从 .card 元素中移除 box-sizingpaddingplace-content 声明,移动子元素(或用 ::after 伪元素 —— 我指的是,如果只需要一行代码,不过我们很懒惰。当然如果我们希望文本保持可以被选择,则实际的子元素应该是更好的主意)并使其成为带有 paddinggrid

    .card {
        /* 和以前一致,
           减去 box-sizing、place-content 和内边距定义
           最后两个我们移动的子元素 */
    
        &__content {
            place-content: center;
            padding: 1em
        }
    }
    

    CodePen 供代码参考与效果预览。

    添加圆角

    假设我们也希望我们的卡片有圆角。由于像 ::before 伪元素上我们用 scaley 这样的定向的 transform 创建的 background 也会扭曲圆角,因此实现这一点的最简单方法是设置一个 border- radius 在实际的 .card 元素上,并使用 overflow: hidden 切掉卡片之外的所有内容。

    对角线分割的圆锥渐变式可变长宽比卡片

    非均匀缩放会扭曲圆角。(例子)

    但是,如果在某些时候我们希望我们的 .card 的其他子代在它之外可见,这就会成为问题。所以,我们要做的是直接在创建卡片背景的 ::before 伪元素上设置 border-radius,并在这个 border-radius 上沿 y 轴反转方向缩放 transform

    $r: .5rem;
    
    .card {
        /* 和之前一致 */
    
        &::before {
            border-radius: #{$r}/ calc(#{$r}*var(--ratio));
            transform: scaley(calc(1 / (var(--ratio))));
            /* 和之前一致 */
        }
    }
    

    CodePen 供代码参考与效果预览。

    最终效果

    把上面的代码合并起来,这是一个交互式演示,允许通过拖动滑块来更改纵横比 —— 每次滑块值更改时,--ratio 变量都会更新:CodePen。



    起源地下载网 » 对角线分割的圆锥渐变式可变长宽比卡片

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元