纯CSS实现阳光透过窗户洒入的效果

Lobsters Hottest 工具

摘要

一个纯CSS实现的阳光透过窗户洒入的效果,包含渐进模糊、带有SVG滤镜的飘动树叶,以及日出/日落过渡。包含每个视觉效果的详细代码解释。

<p><a href="https://lobste.rs/s/lkp7x3/pure_css_implementation_some_sunlight">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/08/10 17:00

jackyzha0/sunlit

源码:https://github.com/jackyzha0/sunlit https://github.com/user-attachments/assets/b4b2a21b-cbfa-4d09-8475-0946cb96cb62

灵感来自 https://daylightcomputer.com/ 和 https://www.sunlit.place/

组件

基础(base)

  • 这里全部只是带背景的 div
  • 叶片素材来自 Adobe Stock 图片,我将其缩小到约 300x500,因为我知道反正最终会把它模糊掉
...

Screenshot 2024-11-11 at 1 51 49 PM

渐进模糊(progressive blur)

  • 得让扩散效果足够逼真
  • 离墙越近的东西应该比离墙远的东西更清晰(模糊程度更低)
  • 这里使用多层模糊,每层都有一个蒙版图像来限制其作用区域
#progressive-blur {
    position: absolute;
    height: 100%;
    width: 100%;
}

#progressive-blur>div {
    position: absolute;
    height: 100%;
    width: 100%;
    inset: 0;
    backdrop-filter: blur(var(--blur-amount));
    mask-image: linear-gradient(252deg, transparent, transparent var(--stop1), black var(--stop2), black);
}

#progressive-blur>div:nth-child(1) {
    --blur-amount: 6px;
    --stop1: 0%;
    --stop2: 0%;
}

#progressive-blur>div:nth-child(2) {
    --blur-amount: 12px;
    --stop1: 40%;
    --stop2: 80%;
}

#progressive-blur>div:nth-child(3) {
    --blur-amount: 48px;
    --stop1: 40%;
    --stop2: 70%;
}

#progressive-blur>div:nth-child(4) {
    --blur-amount: 96px;
    --stop1: 70%;
    --stop2: 80%;
}

Screenshot 2024-11-11 at 1 52 01 PM

叶片飘动(leaf billowing)

  • 使用小幅度的旋转和缩放制作了叶片飘动效果
  • 为了增加些许动感,我使用了 SVG 滤镜以及较少为人知的 feTurbulence 和 feDisplacementMap 标签,为单个叶片的飘动添加更高频的噪声
#leaves {
    position: absolute;
    background-size: cover;
    background-repeat: no-repeat;
    bottom: -20px;
    right: -700px;
    width: 1600px;
    height: 1400px;
    background-image: url("./leaves.png");
    filter: url(#wind);
    animation: billow 8s ease-in-out infinite;
}

@keyframes billow {
    0% {
        transform: perspective(400px) rotateX(0deg) rotateY(0deg) scale(1);
    }
    25% {
        transform: perspective(400px) rotateX(1deg) rotateY(2deg) scale(1.02);
    }
    50% {
        transform: perspective(400px) rotateX(-4deg) rotateY(-2deg) scale(0.97);
    }
    75% {
        transform: perspective(400px) rotateX(1deg) rotateY(-1deg) scale(1.04);
    }
    100% {
        transform: perspective(400px) rotateX(0deg) rotateY(0deg) scale(1);
    }
}

https://github.com/user-attachments/assets/854f4321-f40e-465f-9b26-4fe663d628fa

日出/日落过渡

  • 亮/暗切换有点太突兀,所以我决定在两者之间加入过渡
  • 颜色的线性插值不够有趣,所以我决定制作一个日出/日落色彩动画
  • 这些颜色只是花了很多时间看日落并做了些实验后手工挑选出来的,没什么特别花哨的
  • 还有一个微妙的“光晕”,这是对光线从地面反弹的粗略近似

3D 变换

  • 最后,skew + rotate + transform 并没有达到我想要的效果,因为它只能提供仿射变换,而我想要那种漂亮的透视畸变
  • 决定推导一个变换矩阵(为了好玩),这需要打开 Desmos 绘制一些形状
  • 然后,我们可以解出一个 matrix3d 可以在 CSS 中使用的 4D 透视变换矩阵

matrix3d 的值通过以下 Python 脚本计算:

import numpy as np

def compute_homography(points_src, points_dst):
    if points_src.shape != (4, 2) or points_dst.shape != (4, 2):
        raise ValueError("Input arrays must be of shape (4,2)")

    A = np.zeros((8, 8))
    b = np.zeros(8)

    for i in range(4):
        x, y = points_src[i]
        x_prime, y_prime = points_dst[i]

        A[i * 2] = [x, y, 1, 0, 0, 0, -x * x_prime, -y * x_prime]
        b[i * 2] = x_prime

        A[i * 2 + 1] = [0, 0, 0, x, y, 1, -x * y_prime, -y * y_prime]
        b[i * 2 + 1] = y_prime

    h = np.linalg.solve(A, b)

    H = np.array([
        [h[0], h[1], 0, h[2]],
        [h[3], h[4], 0, h[5]],
        [0.0, 0.0, 1.0, 0.0],
        [h[6], h[7], 0.0, 1.0]
    ])
    return H

if __name__ == "__main__":
    src = np.array([[-1, 0], [0, 0], [0, -1], [-1, -1]]) * 500  # day
    dst = np.array([[-1, -0.1], [0, 0], [0, -1], [-1, -1.7]]) * 500  # night

    # dst = np.array([[-1, 0.1], [0, 0], [0, -1], [-1, -1.1]]) * 500

    # Flip y-axis in src and dst
    src[:, 1] = -src[:, 1]
    dst[:, 1] = -dst[:, 1]

    H = compute_homography(src, dst)

    print("Homography matrix:")
    print(H)

    print("Homography matrix as matrix3d in column-major order:")
    print("transform: matrix3d(")
    for col in range(4):
        values = ", ".join(f"{H[row, col]:.4f}" for row in range(4))
        print(f"  {values}," if col < 3 else f"  {values}")
    print(");")

相似文章

@Jolyne_AI: Ambient CSS 很惊艳,我几乎没见过类似的库:把物理光照模型搬进 CSS,让 React 组件也能有真实的高光、阴影和表面材质。作者用同一套几何与灯光在 Blender 里先渲染一遍,再在浏览器里用 box-shadow 和 CS…

X AI KOLs Timeline

Ambient CSS 是一个将物理光照模型引入 CSS 的库,允许 React 组件实现真实的高光、阴影和表面材质效果。作者通过 Blender 渲染和 CSS 变量来复刻光照,提供丰富的组件如按钮、旋钮等。

@VincentLogic: 以前做前端,为了个加载动画,要么到处找 GIF,要么自己写 CSS 写到头秃。 最近看到这个 math-curve-loaders 开源库,完全是用数学公式生成的动效。 我看了一下,玫瑰曲线、李萨如曲线这些数学图形,动效极其优雅。纯 HT…

X AI KOLs Timeline

介绍了一个名为 math-curve-loaders 的开源前端库,利用数学公式(如玫瑰曲线、李萨如曲线)生成优雅的加载动画,纯 HTML+CSS 实现,零依赖,并带有可视化调试面板,可以实时调整参数并一键复制代码。

使用CSS进行抖动处理

Hacker News Top

本文演示如何使用CSS滤镜和SVG feTurbulence对图像应用抖动效果,以保持一致的视觉风格。

通过实时示例解析Shadow Roots

Simon Willison's Blog

该工具通过交互式示例解析CSS中的Shadow Roots,展示样式封装、继承、插槽及JavaScript访问方式。