Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【译】叶子——可互动的 Web 玩具 #28

Open
JChehe opened this issue Aug 28, 2018 · 0 comments
Open

【译】叶子——可互动的 Web 玩具 #28

JChehe opened this issue Aug 28, 2018 · 0 comments

Comments

@JChehe
Copy link
Owner

JChehe commented Aug 28, 2018

原文:Leaf Notes – An Interactive Web Toy

Art, story & experience
在浏览器上体验 https://tendril.ca/

我最近为多伦多的设计动画工作室 Tendril 推出了一个可互动的 Web 小玩具。你可以在其 官网首页 亲自体验。该网站会轮流展示数个不同的 Web 玩具,所以可能需要刷新一到两次才能看到它。

<iframe src="https://player.vimeo.com/video/261147357" width="640" height="367" frameborder="0" allowfullscreen></iframe>

玩法非常简单:用鼠标划过植物就能使它们开花,并发出相应音调。

该项目十分有趣,我对目前结果也非常满意。TwitterInstagram 上的热烈反应使我备受鼓舞,其中最让我暖心的是一位年仅四岁的小孩在平板上进行了体验。

本文将阐述我与优秀团队 Tendril 如何创造这个 Web 玩具,并讨论期间遇到的一些技术挑战。

概念

在前一段时间,Tendril 已在其官网推出了独具一格的交互动画(案例:12)。他们想让我创造一种全新的体验,且要体现生殖生长和程序化几何。

译者注:
生殖生长:当植物生长到一定时期以后,便开始分化形成花芽,以后开花、授粉、受精、结果(实),形成种子。——百度百科
程序化几何:通过程序生成的几何图形。

One of Tendril's previous web toys
Tendril 先前的一个 Web 玩具

这个想法十分开放:为 Tendril 官网开发一个可互动的有趣玩具。它与已有的 Web 玩具共存,所以设计要适中、使用要简单、加载速度要快。总的来说:交互方式要轻易上手,整体体验要与 Tendril 的网站一致。

一个充满创作自由的想法对我来说可是一个挑战。在过去几个月里,我一直逼自己在开发前进行更多的搜索、头脑风暴、艺术指导和设计思考和构思。我发现铅笔和笔记本确实是最好的工具,不过像 Pinterest 和 Behance 这类平台则有助于管理参考文献和寻找灵感来源。

在讨论了几个不同想法后,我们选定了“与热带植物交互”的这个方向。

Early mood board
早期 情绪板

我早期的情绪板更倾向于单色而鲜明的视觉方向。这些信息反映出了项目在迭代开发中的变化区间。

💡相关说明:我希望有一个开源工具能将一组图片生成砌体结构风格的情绪板。虽然 InVision Boards 的用户体验很棒,但它是一个付费服务。

植物的生殖生长

生殖生长植物
早期程序化生化的植物几何体 Canvas2D 原型

最初,我使用 Canvas2D 的线来进行植物结构的原型设计。这无疑是快速验证想法和几何实现的好方法,因为这无需关心 WebGL 和 GPU 的复杂性。

我使用了简单的线段和二次贝塞尔曲线建立了植物的程序结构。二次贝塞尔曲线如下图所示,它由起点、控制点和终点构成。

二次贝塞尔曲线的构成

使用简单的基本图形和参数函数(如线、曲线)能让事情变得更可控,如动画、GPU 的快速渲染、鼠标的碰撞检测、甚至是声音设计等。例如:定义变量 t,它是 [0, 1] 区间的数字,然后使用参数函数高效地计算出该值所代表的 2D 点。

结构

为每棵植物定义一个起点(如屏幕边界)和一个终点(如接近屏幕中点的某个位置)。然后,再放置一个稍微偏离两端点间中点的控制点,以形成一种弯曲植物茎的感觉。

弯曲的植物茎

为生成叶子,需按固定间隔遍历曲线,确定每个位置上的垂直法向量,并使用一些函数对法向量进行缩放 & 旋转操作,最终形成像“羽毛”一样的叶子。在最终案例中,我并未使用垂直法向量,而是使用了斜接的法向量(mitered normal)【译者注:mitered normal 翻译有误】。

像羽毛一样的叶子

我提取部分代码到以下 Canvas2D 案例中,你可以在 这里 查看/修改。点击以下案例可修改曲线结构。

Edit Procedural Leaf

学习到的错误及经验教训

在 2D 原型设计阶段,我犯了两个错误。在后续原型设计中应尽量避免:

  • 维数:如果能将这种体验转化到三维空间就更好了,因为拥有深度和更好的互动效果。大多数算法均能转换到 三维空间上,但代码和数据结构是以二维空间作为假设前提而设计的。
  • 单位:在最初 2D 原型制作时,使用了像素单位进行缩放和定位。这使得在适配屏幕分辨率时变得困难。如果在生成植物的代码中使用相对坐标会让上述情况变得更好处理,如 (0, 0) 代表屏幕左上位置,(1, 1) 代表屏幕右下位置,就像上面的 CodeSandbox 案例那样。

动画 & 交互

在几何植物的顶点上使用简单的弹簧效果,而不是使用复杂且 CPU 密集型的物理系统。这使得植物看起来有点像果冻,但不失为一个有趣好玩的互动。

对植物茎和叶子上的顶点,我都指定了 target(即顶点应该弹向的目标位置)、position(即顶点的实时位置)和 velocity(速度和运动方向)。基础物理系统的伪代码如下:

// 1. 为速度 velocity 添加鼠标力
if (鼠标足够靠近顶点) {
  velocity += mouseVelocity * mouseStrength;
}

// 2. 弹向目标位置
const delta = target - position;
velocity += delta * spring;

// 一直存在的且不变的“空气阻力”
velocity *= friction;

// 累加得出顶点位置
position += velocity;

以下是顶点弹簧效果的交互案例,它展示了如何让二次贝塞尔曲线与鼠标产生弹簧的效果。尝试一下案例吧,也可以 点击这里 阅读完整代码:

Edit Springing Curves

对于碰撞,我使用了 point within radius 模块判断顶点是否与鼠标发生碰撞。该碰撞检测的运算速度很快,但并非完美:叶子上存在部分“盲点”,即不会产于交互。为了在划动叶子时产生更精确的声音效果,我在小树叶上使用 point to line segment distance 进行碰撞检测。使用后者能产生更佳的互动体验,但在最终案例中,较大的鼠标半径和较多的植物数量使得难以发现两者差异。

渲染

尽管 Canvas2D 能很好地完成原型阶段的处理,但却不能胜任诸如逐像素着色的工作。

感谢 ThreeJs 及其 OrthographicCamera,它们使得所有 canvas 代码迁移至 WebGL 变得不会太难。每根植物茎由一个 PlaneGeometry(可复用)和一个自定义顶点着色器(vertex shader)组成。顶点着色器将平面几何段(plane segments)沿曲线(或线段,即植物茎或叶子)放置。

译者注:OrthographicCamera:正交相机,即镜头下所有东西均不会产生近大远小的透视效果,尺寸保持一致。

可通过我之前编写的笔记 《2D Quadratic Curves on the GPU》 了解更多该技术相关的知识点。通过该方法能生成每棵植物所需的曲线和线段。最终效果如下:

2D Quadratic Curves on the GPU

在顶点着色器中,我添加了参数函数,以实现沿 t 弧长变化的线宽。例如:thickness = sin(t * PI) 会压缩曲线的开始和结束部分。有了这些函数,平面几何的轮廓开始变得更像锥形叶子。

锥形叶子

最后,添加颜色和外观细节——每片叶子在亮度、色相、饱和度、叶脉密度和旋转角度等方面均有了细微变化。所有这些计算均在片段着色器(fragment shader)完成。例如:每片叶子的叶脉和中线是基于纹理坐标计算得到的,并使用 fwidth() 计算出抗齿锯 2~3 像素的平滑曲线。

片段早色起

在开发期间,我使用 dat.gui 作为可视化滑块,并使用 surge.sh 与团队的其他成员共享迭代。这些工具使得我们能够尝试许多不同的想法和方向。而这种开发迭代的方式也让我们能想出一些有趣的特性:直到项目后期我们才引入了在黑色“手绘”状态下添加动画的想法(译者注:此句原文为:it wasn’t until later in the project that we introduced the idea of animating plants in from a black “hand-drawn” state)。

dat.gui

小细节

为了让项目更加生意盎然,我在小细节上花费了许多时间。实际上,叶子的核心结构和弹簧般的交互效果是最简单的部分,而大部分时间则花在了提升视觉效果、制作动画和修复各种跨浏览器问题上。

部分细节如下:

  • 随机性在案例的几乎所有部分(即视觉上的细微变化、动效和音效)均有应用。例如,随机长度、曲率、密度、时间、风速、色调、线宽、亮度、音量等。在最终效果中,我使用固定的随机系数,以保证所有用户体验到一致的效果。
  • 声音被节流(通过时间和最大同时播放数),从而避免破音和杂音。
  • 声音的音量是基于鼠标的划动速率进行动态修改。鼠标的快速移动能产生更加戏剧性的声音效果。
  • 根据鼠标的交互位置,声音会在往左/右声道靠拢,以呈现空间立体感。
  • 多处性能优化:整个场景仅有一个着色器(译者注:顶点着色器和片段着色器组成一个着色器程序)和 3 种不同的几何形状;花费大量时间查看分析器(Profilers)和优化函数,直至能在所有浏览器和设备上流畅运行。屏幕像素密度、叶子密度、植物组织和其他变量均基于用户浏览器和分辨率进行适配。

最后的障碍

和一般交互式 Web 项目一样,项目的最后阶段通常需要小调整,以确保能在各类浏览器和设备上顺利运行。

对此,我使用了几个过去用于处理常见跨浏览器问题的模块,如用于统一鼠标和触摸事件的 touches 和兼容 iOS WebAudio 的 web-audio-player

还有其他一些浏览器问题,以下是我处理的方案:

  • 在 FireFox 和 MS Edge 中,当 JavaScript 有大量 CPU 运算时,setTimeout 不能及时触发回调函数。因此,我认为它是不精确的,而且我也不可能为此等待 2~3 秒之久。于是选择 timeout-raf 修复它。
  • 为不支持 WebAudio 的浏览器(如 Safari)进行 polyfill 兼容处理——stereo panner node。而对于声道有偏移问题的浏览器(如移动端 iOS safari)则采取在移动端禁用该效果的处理。
  • Safari 同时还存在其他一些问题:我不得不控制最大同时播放数,以避免破音/咔嚓声;避免因浏览器偶尔中断 audio 上下文,而需在播放前调用 audioContext.resume()
  • 与其他浏览器相比,JIT/JavaScript 引擎在 MS Edge 上表现太差。除了降低植物细节外,我无法修复该问题。
  • iOS Safari 中嵌入 iFrame,有时会获取到不正确的 window.innerWidth 值。为了修复该问题,我最终为 canvas 设置 position:fixed 且宽高 100% 的样式。

作者

感谢 Tendril 团队,名单如下:

本文和交互案例的源码均可在以下链接找到:

https://github.com/mattdesl/tendril-webtoy-blog-post

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant