前言:
制作灵感来源于 http://species-in-pieces.com/ 这个网站,此网站作者是来自阿姆斯特丹的设计师 Bryan James,其借用纯CSS技术表现出30种濒危动物的碎片拼图形象,用转瞬即逝的动画来暗示这些动物目前的处境。
首先,所有动物形象都是借助 clip-path 这一 css3 属性通过各 三角拼接而成,这一属性不仅可以画 三角 还可以画出任意形状。
为了实现一个这样的动画编辑器,工具方面使用了浏览器的 Canvas2d 和 localStorage,代码风格上 大量使用了 fp 风格的代码。
代码风格:
一个比较典型的设计如下:
// 获取画布 div let drawboardGetter = () => $( ".polygon-wrap");这里 $ 是实现的一个简易原型,并未引入 jQuery,可以使代码精简。
使用这些自定义 getter 函数的目的是来代替所有 纯函数 不纯的部分,这样即便整个纯函数链有不纯的部分,也不影响各种功能链的正常运行,因为这些 getter 的引用变更不会引发任何错误。
实现步骤:
第一步新建一个 html 文件来完成编辑器的功能。
作为一个编辑器,首先要能手动画三角形,可用一个 canvas 来记录鼠标点击处的位置,起到一个提示三角形大概轮廓的效果:
数据方面分别区分两种单元,图案 和 三角形,三角形组成图案,图案之间可以切换,用 函数+组合 的方式生成一个实例,使其具备功能:
// 一个图案的点管理器 let pmgInstance = null; let pointArrMgGetter = () => pmgInstance || ( pmgInstance = mkPointArrMaker( basicArrMaker()));显然生成的 点数组 getter 都会具备相同的数据结构,因此可以像一个相同类型的变量一样穿梭于各种外部函数之间。
然后设计下编辑器的 ui 部分,是可拖动可隐藏的还有之前停留位置的记录功能,可拖动和可隐藏的设计随便拿一个编辑器比如 ps,unity-editor 这种对功能框都有这种设计,为了更方便使用,我还设计了一个右键点击编辑器空白处可以隐藏编辑器的功能,这样不用去点一般在右上角很小的关闭按钮,实际使用起来我个人感觉很流畅。
// 使一个 div 可以被鼠标拖动 const mkDragable = ( div, propTopHandler, eventHandler) => { /* ... */ }上面这个函数会让一个 div 变成可被拖动并在某些时机触发相应的钩子;
动画部分全部是借助 css 实现的,也就是浏览器默认的绘图功能,那作为一个编辑器就要有随时能准确更新 css 的功能,具体点就是更新 style 标签里的内容,使它变成 freeStyle:(ps:用来实现 数据 => css代码 功能函数也必不可少)
// 插入css规则 const { insertCssRule, alterCssByClassName, alterMainCss, inserKeyframeRule, alterCssByKeyFrameName, } = (() => { /* */ }
代码层 状态模式 这种设计也是必须的,因为用户在使用鼠标点击的时候 最基本的就可能处在三种情形: 三角形形状编辑,三角形位置编辑,绘制三角形。
// 状态切换器 function tabToState( mode, val = true) { /**/ }事件绑定方面有经验的开发会选择事件派发,简而言之就是绑在父元素,性能理论上也会好些。
回到 html 部分,为了用最简洁的方式实现三角形的动画过渡,这里我使用了 div 复用的设计,说白了一个舞台两场戏,都是原来那群龙套。
这里就还需要设计一个所有 div 如何按顺序开始变换到自己在下一副图案中位置的问题,这个对最终效果的展示至关重要,后面会展示对比效果。
// 某个 div 开始跳舞 const mkDivUiWork = ( singleConfig, arrSource) => ( i) => { // 三角形重置 singleConfig. showDomFrameConfig( arrSource[ i]. dom, 0); // 是否在舞台上 arrSource[ i]. show = true; };这里的封装是为了将这个群演 div 的戏份储存起来,这样后面总导演就能安排他们的出场时机。
万事具备,实现功能就顺理成章了,不过肯定有很多细节可以完善和优化的地方。
鉴于我的艺术素养有限,就从临摹开始来完成动画图形,这里开个 nodejs 服务用来将网站截图存储本地,去掉跨域限制后画到浏览器里面,然后就可以仿照着临摹了,临摹这里有两个要注意的地方,第一个是叠加的两个三角形后面的三角形要靠想象力先画出来,因为暂时没有新增调整三角间层级的功能,先绘制的三角形层级会比后绘制的高;第二是默认临摹出的三角形是无法自动获取被临摹三角的颜色的,需要点击 临摹取色 进入取色状态,填充点击在画板上此位置取到的像素颜色。
特点设计:
局部的编辑器也做了区分,比如双击一个三角就可以调出仅 针对 三角形形状编辑功能和三角形位置编辑功能的 编辑器,而其余的两个 图案编辑器 和 临摹图案编辑器是显示出来可以让用户点击的,流程设计大概如下:
临摹图案编辑器显示图案 => 单击连点临摹绘制三角形 => 双击三角形可编辑位置和形状 => 打开图案编辑器保存图案
(双击三角形调出编辑器即可以用按钮切换编辑位置和形状的功能,也可以直接右键鼠标实现功能的快速切换)
三角本身的动画也是在双击三角弹出编辑器上通过增减关键帧实现(具体就是变更 css 样式里 keyframe 的帧设置):
最后就是图案间动画的切换了,因为前面已经实现了 freeStyle,因而一旦一幅图的 freeStyle 被切到了另一幅图的 freeStyle,此时浏览器的绘图引擎在 transition 的影响下就开始自动工作了:
但是由于所有群演是同时开始动作的,就会让人眼花缭乱看不过来,之前提到封装每个群演的行为这时候就起作用了,我们可以安排他们一个一个按顺序执行,这里其实可以用各种缓动函数来 控制,比如开场缓慢,中场热烈,收尾迅速,就像原来网站里的动物间切换的律动效果一样。
这里用一个固定间隔 200 ms 来展示效果,当然这个固定间隔不仅可以提前配置也可以实时更改:
其实到这里除了出场间隔还有一个问题,就是三角形的绘制顺序问题。举个例子,群演 小黑 在剧本A(图案A)中是在西北角位置,在剧本B(图案B)中却被安排到了东南角位置,那么转场过程中,小黑 就不得不从西北角跑到东南角,这给观众带来的观影体验请参照(周星驰在喜剧之王中演死人龙套的场景),小黑 明显抢戏了。
因此在图案绘制过程中我都是遵循从右下到左上的绘图顺序,这样图案间的过渡就会流畅很多。但显然这是治标不治本的解决方案,小黑应该遵循就近原则自动补位。
这也是我还没实现的功能,不过思路也比较清晰,因为所有三角位置都清楚的情况下通过计算调整两场群演间的映射关系就可以了。还有的问题包括两个图案所用三角(群演)数量不匹配的问题,上图结尾可以发现新的群演是通过天外飞仙的方式登场的,可能需要调整下出场效果。
最后和原网站对比,可以发现编辑器还缺少的一些功能,比如应该可以配置背景图,可以配置图案的整体动画,动物身体下的阴影动画配置,这些都是可以列计划完成的。