丰富的线上&线下活动,深入探索云世界
做任务,得社区积分和周边
最真实的开发者用云体验
让每位学生受益于普惠算力
让创作激发创新
资深技术专家手把手带教
遇见技术追梦人
技术交流,直击现场
海量开发者使用工具、手册,免费下载
极速、全面、稳定、安全的开源镜像
开发手册、白皮书、案例集等实战精华
为开发者定制的Chrome浏览器插件
23年兔年,五福项目将传统的写福字升级成了年画,用户通过绘制兔子轮廓可以得到活动的兔子,同时由AI生成对应的兔子年画,整个过程给用户带来很强的惊喜感,同时将具有传统氛围的年画与科技感拉满的AI作图有机结合,为大家带来全新的年俗体验。
AI年画作为23兔年五福的创新项目,在玩法和技术方案上都采用全新的实现,前后端技术、AI算法深度,以及美术互动等深度协同,实现了玩法了技术的双创新,最终呈现的效果得到了广泛的认可。
一句话介绍:用户根据引导画出兔子轮廓,根据轮廓生成AI福兔,福兔结合装饰套组生成福兔年画,并可以对应的虚拟和实体物品。
用户在创作页完成年画兔子的创作,同时请求服务端生成对应的AI年画,最后通过转场动画过渡到装饰页。
创作页-绘制兔子
创作页-合成动画
用户在装饰页使用各种素材贴纸DIY装扮自己的年画,为自己的年画署名,或是选择重新创作。
主页作品集
年画周边
兔年五福年画项目作为一个2D互动应用,选择TinyJS做为渲染引擎,同时选择一系列Tiny的插件库。TinyJS在支付宝业务中广泛应用,经历多年互动业务落地沉淀,是目前支付宝2D互动的首选。
TinyJS拥有丰富的生态,今年的年画项目中使用了一系列插件来完成各种功能模块的开发。
mars动画是实现各种炫酷的特效的首选,同时也是今年的五福规范,在TinyJS作为渲染引擎的背景下我们使用tinyjs-plugin-mars播放mars动画。
MarsStudio工具制作动画
为了让兔子在画布中动起来,我们选择使用spine实现兔子的骨骼动画,在TinyJS中使用tinyjs-plugin-spine骨骼动画插件实现。
为了减少内存和预推资源的消耗,我们在一些部分动画上选择使用shader的自定义动画,同时保证性能和效果。
年画的玩法有一条明确的动线,那就是绘制兔子,生成年画,装扮年画,年画作品集,因此在开发我们可可以整个互动分成四阶段:
paint(绘制阶段)
transform(合成年画)
decoration(装饰阶段)
home(年画主页)
创作页主要的难点有两个:
最开始的产品方案其实就是让按照步骤描轮廓,大致的交互如下:
为了增加趣味性,参考了其他的绘画App,需要在绘画的过程中和绘画完之后兔子的部件(耳朵、手、脚)要动起来,这无疑增加很大的难度(因为这是第一次需求评审完之后才变更的,这给设计师和前端开发都会带来巨大的工作量)。
骨骼动画确实是可以动,但是怎么将绘画的轮廓跟骨骼动画结合起来了,这个在蚂蚁,甚至业界目前看来没有现成的方案的。
具体的方案探索
1)骨骼动画支持换肤,可以将用户的画的笔迹提取出来作为骨骼去进行换肤。
所以设计师在设计的时候就需要把哪些要动并且进行可以替换的地方,做成插槽。
2)如何将用户画的部件精确的提取出来
一开始业务期望是一个可以任意画的画板,用户随意画,但是我们无法识别出来用户画的笔迹哪里是耳朵、哪里是手、哪里是脚,识别不出来,就更别想提取笔迹进行替换了。
为了可以识别出来在画板里哪部分是耳朵、手、脚,我们需要对用户的可绘制区域和绘制顺序进行限制,限制出哪部分是耳朵、手、脚,用户在我们圈定的地方绘制的,我们就认为是该部件。
比如左上角圈定一块区域,我们就认为是左耳朵,这部分提取出来的纹理,在进行换肤的时候就换肤到spine动画的左耳朵处。
为此我们实现和设计师制定了统一的坐标系,实现笔迹提取范围和美术资产的对应,保证绘制内容到spine骨骼的无感切换。
所以我们跟设计师约定了大致这么一个范围,来确定插槽的部分。
3)如何让动画动起来好看
虽然我们约定了笔迹提取范围,可以识别出来身体部件,但是用户如果在这个框里画出来不像我们期望的样子,这样就会导致两个问题:
所以为了解决不好看的问题,我们还得限制,尽量保证我们引导用户画耳朵,就画出来像耳朵。
两个解决方案来保证这个问题:
限制用户绘制区域:
设计师在给引导的兔子部件底纹的时候,还需要给一份部件的轮廓坐标。
这样我们就可以利用轮廓点位,通过Tiny.Graphics绘制一个Mask区域,盖在画布上,相当于把轮廓外面的都设置了mask,超出轮廓的笔迹都看不到;并且保证了用户绘制内容的可控。
看标记的地方,明细能看出超出的部分别截掉了。
在这个区域绘制出来的笔迹,尽量像引导的底纹:
如果按照我们限制绘制区域画出来,基本上画出来的兔子已经很像了,但是有可能用户在这个区域里没有画完,可能只随便画了一笔,这样可能不是很像,所以我们还加了一个粗糙的相似度检测。尽量的去保证用户画出来的笔画是我们预期的效果。
具体的方案就是判断笔迹的路径是否通过我们预设点的大多数(目前是80%的点位)。所以设计师还需要给一份兔子部件的点位,见下图。
在笔触移动的时候通过碰撞检测,检测是否经过预设的点位,超过80%的点位,就认为完成当前部件的绘制。
到此,基本上就能保障用户画出来的兔子默认好看,并且动起来的时候也好看。
绘制我们用的tiny-plugin-calligraphy插件,该插件本身并不支持自动绘制能力,在分析插件源码后通过事件代理的方式最小成本的实现了自动绘制功能。
要实现自动绘制很关键的两个点:
绘制点位的顺序:
这个在前面已经给过了,就是这个,只是给的时候需要注意一下顺序即可,通过数组的方式保证顺序。
当前用户绘制进度在哪里:
因为在给点位的时候是每个部件一份数据,而不是一份完整的兔子轮廓数据,还需要每个部件合起来定义一个顺序。同时,如果用户画到一半,然后再想用自动绘制的时候,需要知道当前自动绘制应该绘制哪个部件。
我们将兔子身体划分成六个部分,分别是左耳、右耳、左脸、右脸、左身体、右身体,通过标志符判断用户的绘制进度,同时使用标志符管理绘制阶段。
drawIndex=0
drawIndex=1
drawIndex=2
drawIndex=3
drawIndex=4
drawIndex=5
drawType=DrawTypes[drawIndex]='earL'
drawType=DrawTypes[drawIndex]='earR'
drawType=DrawTypes[drawIndex]='faceL'
drawType=DrawTypes[drawIndex]='faceR'
drawType=DrawTypes[drawIndex]='bodyL'
drawType=DrawTypes[drawIndex]='bodyR'
定义好之后,就按照drawIndex进行自动绘制即可。
在切换装饰页时,我们通过一整套完整的动画流程来保证用户体感和引导的连续性,兼具趣味性和科技感。为此我们在各个节点上设置了相互衔接的不同动画。
spine兔子首帧出现
兔子spine动画
动态合成年画图片
溶解出现
年画框上移滑动
切换装饰页
兔子首帧出现和年画溶解出现的动画效果,使用了Tiny.Filter提供的自定义着色器能力,通过自定义的shader代码实现高性能动画。
在具体实现溶解效果的时候,将连续的灰度分布图作为底图,在着色器中根据连续的采样结果+线性偏移的时候。
用户在装饰页完成年画的DIY装扮,生成最终的年画。
整个DIY过程主要用到了tinyjs-plugin-transformable插件实现贴纸的编辑(拖拽、旋转、删除、镜像、缩放)。
再装饰完成之后使用tinyjs-plugin-extract插件导出年画和年画前景。
年画项目还为用户提供了一系列的年画周边,如挂历,马克杯,地毯等印有年画的商品,为了保证周边商品上年画的清晰度,我们需要再实现一套额外的高清年画合成方案,为周边商品提供大尺寸的高清年画。
我们在装饰页DIY年画的过程中,会记录兔子,背景,贴纸等素材的位置大小和角度,根据位置和大小信息使用原本素材的四倍图在合成年画的高清版本。
受限于现有打印链路的能力,我们使用多媒体团队提供的SVG合图方案,而在年画创作DIY和时候使用的是TinyJS以及tinyjs-plugin-transformable插件的混合坐标体系,与SVG坐标体系不兼容,为此我们需要写一套额外的坐标转换逻辑,同时后端还需要将前端提供的坐标转换逻辑再转成Java实现,最终在后端实现合图。
为了覆盖100%的用户,尽量保证效果,我们就用户不同的终端情况实现不同的降级逻辑。我们通过jsapi接口getDowngradeResult获取降级信息。
年画的使用场景很多,不同的使用场景,比如壁纸、付款码皮肤、红包封面、NFT,不同的场景需要的图片尺寸还不一样。
提示:尺寸为了统一,都以750宽度为基准,虽然最终生成的年画是600*780,这个跟750*975比例是一样的,为了好理解,文中750*975和600*780可以互相替代,不影响理解。
所以为了看起来是一个年画,但是要适应不同的场景,整个方案如下:
以学生兔这样的一个年画为例:
算法会给三张图片:长背景、短背景、兔子,前端负责剪短背景、合并兔子和吉祥话到画板中。
长背景(750*1624)
短背景(750*975)
兔子
画板。
用户在完成DIY装饰后导出最终年画和前景图。
年画(750*975)
前景图(750*975)
现在就有了适用于各种场景图片的原始图了,对于年画的业务方就根据需要使用即可。