苹果 visionOS for web

苹果的 Vision Pro 已经发布了,虽然没有拿到手,但我还是对它的操作界面充满了好奇。

我看到很多小伙伴写了 Windows for Web,Mac OS for Web,所以我也想来实现一下 Vision Pro 的系统主页。

一开始,我以为这不会太难,当头一棒的就是苹果祖传优势: 动画。

这动画,这模糊,还是从中心点开始逐渐向外层扩散,应该根据人眼的视觉特征进行设计的。

问题是,该如何实现呢?

模糊我知道怎么实现,

css 复制代码
filter: blur(15px);

从中心点开始逐渐向外层扩散的效果,我直接来个 transition-delay: 0.1s;

一通操作之下,也实现就似是而非的效果。而且边缘处app图标的缓缓落下的效果也不好。

然后就是光影效果的实现,因为它的很美,让人很难忽略。

在 Vision Pro 系统演示中可以看出,为了模拟菜单栏使用了磨砂玻璃材质,而为了营造真实感,会模拟光照射到玻璃上而形成的光线边框。

我不知道这是不是菲涅尔效应,但问题是,这又该如何在前端实现呢?

我想到了 CSS Houdini,可以利用 Houdini 开放的底层能力 paint 函数来实现一个菜单栏效果。

ini 复制代码
if ('paintWorklet' in CSS) {
    CSS.paintWorklet.addModule('data:text/javascript,' + encodeURIComponent(`
    
        class FresnelAppRectPainter {
            static get inputProperties() { return ['--light-angle']; }

            paint(ctx, size, properties) {
                const borderRadius = 30;
                const fresnelColor = 'rgba(255, 255, 255, .9)';
                const lightAngle = parseFloat(properties.get('--light-angle')[0]) || 0;

                // 绘制圆角矩形
                ctx.beginPath();
                ctx.moveTo(borderRadius, 0);
                ctx.lineTo(size.width - borderRadius, 0);
                ctx.arcTo(size.width, 0, size.width, borderRadius, borderRadius);
                ctx.lineTo(size.width, size.height - borderRadius);
                ctx.arcTo(size.width, size.height, size.width - borderRadius, size.height, borderRadius);
                ctx.lineTo(borderRadius, size.height);
                ctx.arcTo(0, size.height, 0, size.height - borderRadius, borderRadius);
                ctx.lineTo(0, borderRadius);
                ctx.arcTo(0, 0, borderRadius, 0, borderRadius);
                ctx.closePath();
                ctx.fillStyle = 'rgba(163, 163, 163)';
                ctx.fill();

                // 模拟光照效果
                const gradient = create360Gradient(ctx, size, lightAngle)
                ctx.fillStyle = gradient;
                ctx.fill();

                // 添加菲涅尔效果
                const borderGradient = ctx.createLinearGradient(0, 0, size.width, size.height);
                borderGradient.addColorStop(0, fresnelColor);
                borderGradient.addColorStop(0.2, 'rgba(255,255,255, 0.7)');
                borderGradient.addColorStop(1, fresnelColor);

                ctx.strokeStyle = borderGradient;
                ctx.lineWidth = 1.5;
                ctx.stroke();
            }
        }

        registerPaint('fresnelAppRect', FresnelAppRectPainter);
    `));
}

结果效果还可以,我甚至可以接收一个光的入射角度,来实时绘制光影效果。

arduino 复制代码
 function create360Gradient(ctx, size, angle) {
      // 将角度转换为弧度
      const radians = angle * Math.PI / 180;

      // 计算渐变的起点和终点
      const x1 = size.width / 2 + size.width / 2 * Math.cos(radians);
      const y1 = size.height / 2 + size.height / 2 * Math.sin(radians);
      const x2 = size.width / 2 - size.width / 2 * Math.cos(radians);
      const y2 = size.height / 2 - size.height / 2 * Math.sin(radians);

      // 创建线性渐变
      const gradient = ctx.createLinearGradient(x1, y1, x2, y2);
      gradient.addColorStop(0, 'rgba(255, 255, 255, 0.2)');
      gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');

      return gradient;
   }

演示效果图

哦对了,还有一个弹层底边角的缩放效果,我目前还没想到什么好办法来实现,年底还得抓紧搬砖,只能先搁置了,如果小伙伴们有好办法,欢迎告知或者讨论。

最终效果图

这里是 Demo 地址。

本来是冲着纯粹娱乐(蹭流量)来写的,但写着写着就发现好像没那么简单,三个晚上过去,也只写了个首页,不得不感慨苹果真的太细了呀。

以上。

相关推荐
FungLeo2 小时前
成为全栈·Vue 管理后台篇·Vue Router如何守住多角色后台边界
前端·javascript·vue.js·rbac·vue router·路由守卫·前端权限
FungLeo2 小时前
成为全栈·Vue 管理后台篇·分页只显示第一页:从接口total 定位到前端修复
前端·vue.js·状态模式·分页·naive ui·服务端分页·前端问题排查
ZhengEnCi2 小时前
苹果滑动为什么有「肉感」:橡皮筋越界阻尼与弹簧动画的物理原理
前端
Moment2 小时前
不会后期的我,发现了一个开源胶片神器😍😍😍
前端·后端·开源
L1mbo2 小时前
Lite模式 缓解微信开发者工具卡顿,附 uni-app 实践方案
前端·微信小程序
FungLeo2 小时前
成为全栈·Vue管理后台篇·仪表盘不是几张卡片:组合统计接口与角色视图
前端·javascript·vue.js·rbac·仪表盘·管理后台·tanstack query
SameX2 小时前
HarmonyOS 做桌面卡片 onAddForm 要同步返回但数据在数据库里的解法
前端
deli0073 小时前
手机夜景模式为什么要举 3 秒?8 帧 RAW 堆栈 SNR 实测
前端
松仔log3 小时前
Kotlin中级——callbackFlow
java·前端·kotlin
三小河3 小时前
SWC 凭什么比 Babel 快 ?项目优化迁移踩坑记录
前端·后端