分类
前端 技术

使用UI的矢量切图做可控的路径填充

大家都见过某些网站在获取验证码时,需要手指拖动滑块到指定位置,进行一个真人验证才能获取到验证码,那么本篇文章就想要探究一下,类似这样手指拖动滑块进行路径填充时的一些方案。

当然了,上述例子中,手指拖动滑块所填充的路径是一条直线,这种特殊的路径填充会相对的比较容易实现,类似的功能,比如多媒体播放器的进度条,这类的实现已经很多也很成熟了。可以通过计算手指在某一区域内滑动距离的百分比,以此来更新滑块的位置,需要注意的是,滑块的位置更新并非跟随手指移动的触发点,而是被限制在一条k率为0的直线上,其实就是一条横线上,无论x如何改变,y始终都保持不变。而滑动区域的颜色填充实现方案也不唯一,比如可以通过使用一张纯色图进行横向百分比拉伸,但是如果遇到需要渐变色时,不太美观,那么渐变色或者带花纹就可以考虑用一整张渐变图片再配合蒙板的收缩来实现。

上述所说的渐变色、花纹图案其实是需求的延伸拓展,那么我们可以再拓展一下,如果现在要填充的路径是一个圆饼,又或者是个圆环呢,其实也好办,从直线路径填充我们可以联想到,其实关键就在于:1、如何计算手指在某个大致的运动轨迹下移动距离的百分比,之所以说是大致的运动轨迹,是因为人手无法画出一个完美圆或者完美直线。2、如何根据这个百分比来更新一个滑块的位置,并且进行路径的填充,而且需要注意的是,无论是滑块还是路径填充都与手绘相反,都是被约束在一个既定形状的路径上所进行的。对于一个圆饼来说,我们需要知道其圆周所在的圆方程,对于圆环来说我们需要知道内外两个圆的圆方程。

不论是直线还是圆,用手滑过其大致路径并进行滑块跟随和路径填充,都还局限于一笔画,而如果是任意不规则形状的多笔画呢?这个才是今天真正要讨论的终极解决方案–将问题扩展到如题目所说,根据UI所切的图形,进行任意形状的描摹。它可以是个汉字、可以是个字母、可以是个动物、甚至也可以是个音乐符号。听起来是不是已经变得有意思得多了。

方案一:由动效师逐帧绘制动画,完整动画的效果就是一个滑块走过一个既定路径,并在已走过的区域进行颜色填充,以下我们将其成为描摹过程,然后由程序控制动画的播放进度,那么关键还在于程序如何计算手指在某个大致的运动轨迹下移动距离的百分比。因为此时整个描摹事件被做成了一个动画,程序是无法感知到这个动画具体所呈现出来的图形是什么,这只能靠人眼就行观察到,而并不是动画本身所能给到的信息。那么该方案只能适用于针对特定已知图形的描摹,同时程序需要定义一个映射关系表:某动画->其对应的程序能感知到的形状描述信息。而怎么去描述一个程序能感知到的形状信息就成为了一个新的问题,而不这样做的话,程序就无法判断手指所滑动过的区域是否是基于该形状的一个大致图形。不是没有解决办法,SVG就是这么一个描述形状的语言,恰好对于设计师来说,导出一个SVG跟吃饭一样简单。但是由动效师进行逐帧绘制的话,终究效率太低且工作量巨大,而且不够灵活,动效师需要对每一个设计师所设计出来的图形进行动画化,有没有一种一劳永逸的办法?去掉动效师这个环节,去掉映射表,但凡设计师画出一个新图形,就可以对该图形进行描摹行为。

有的,我们可以与设计师约定一套协议,设计师按照一个规则进行图形设计,这套规则是与图形无关的,不会影响设计师发挥其想象力,也不会对图形本身造成变形,这个规则约束的只是SVG本身。在SVG描述语言中,有两大基本形状–规则形状、路径形状,规则形状中有直线、矩形、圆、椭圆;路径形状则是一系列点、直线、曲线所组合出来的特殊填充或描边。而有趣的地方在于,即使是规则形状,也可以将其转化为路径形状,我们举个例子,对于一个矩形的规则形状描述,是这样的:rect:(x=0, y=0), width=1280,height=768,它描述了矩形的左上角点坐标以及矩形的长和宽,由此可以唯一确定一个唯一矩形,那么将其转化为路径形状后会怎么描述呢:path:move(0,0),line(0,1280),line(768,1280),line(768,0),line(0,0),它描述了以顺时针顺序分别绘制了4条直线,由此组成了一个矩形。基于此,我们可以跟设计师约定,将所有形状都以路径形状来设计,虽然增加了一点点工作量,但因为规则形状本身并不多,其实很容易解决,接着由程序实现一个简易的SVG解析工具,当解析出SVG之后,程序不必知道这个图形叫做“小猫”还是“小狗”,程序只需要知道这个图形,由这么一串指令控制绘制就可以了。

具体实现:

首先是设计师设计图形,我们希望设计师在使用钢笔工具绘制形状的时候,钢笔起点就是这个形状描摹过程的起点,终点同理。因为是描摹,因此图形必然是一个闭合路径,同时还需要设计师为其设计手指的移动轨迹,那么一个形状至少包含两个图层,图层1是形状本身,是闭合路径,图层2是形状的手指轨迹,是开放路径。只要遵循这个约定,那么设计师所给到的SVG就可以称之为一个合格的SVG。

接着是程序方面的实现,程序首先需要解析SVG,将每一个路径形状分解为一个个指令,或move、或line、或curve、或close,有趣的是,除了move和close这两个纯控制指令,其它每一个指令最终都可以被转化成一个4阶贝塞尔曲线,形为:curve(x1y1,x2y2,x3y3,x4y4),其中点1、4为起止点,点2、3为贝塞尔曲线的控制点,之后可以通过资料查找,得知4阶贝塞尔曲线的通用方程,这下就回到最初的问题了,有了方程后我们就可以对其采样,采样数量可以自由控制,当然采样越多所绘制出来的图形越精细,但是最终还是要结合程序来看,显卡对于图形的渲染大多都是以像素为单位,抛开某些特殊硬件的次像素渲染不说,如果采样率太大,那么采样精度高于一个像素的时候,其实就是浪费计算性能了,我们甚至可以做成动态采样率,针对不同图形的大小使用不同的采样率,当然了这是后续的优化,按下不表。采样的过程,其实就是对一个连续的曲线方程进行离散化,从而得到一个个的点,这些点就是程序所需要的绘制点。

描摹过程的自然语言描述:手指在屏幕上移动,程序会收到一系列触摸事件,从中可以得知到手指所在屏幕上的坐标,我们需要计算这个坐标距离设计师所给的绘制路径曲线是否太远,如果距离在一个可接受范围内,我们认定手指是按照既定轨迹进行滑动的,那么就进行下一帧率的渲染,渲染过程包括两步:1、更新滑块位置。2、填充路径。更新滑块位置较容易,滑块的移动路径其实就是设计师所给的绘制路径,只需要将滑块放置到距离手指最近的且位于绘制路径上的一个点即可,距离计算可以使用向量公式不赘述。而路径的填充,基于我们知道上一次填充的终点,又能通过距离计算得到本次填充的终点,那么两个点就唯一确定了一段采样,我们将这段采样以每两个点之间作为矩形填色,最终得到一个形状的填充,其实很类似于积分的过程。

那么至此,整个描摹过程就算是完成了,余下的则属于正常程序流程的常规开发此不赘述,这个方案的本质,其实就是模仿各种图形库绘制贝塞尔曲线的过程,但是图形库所绘制的曲线是一次成型,是一个不可控的过程,我们无法控制其绘制进度,而可控是一个很重要的需求,可控意味着用户可控制绘制进度,也意味着程序也能控制绘制进度,这在制作引导动画时,是不可或缺的。

“使用UI的矢量切图做可控的路径填充”上的一条回复

作为不知道是小猫还是小狗但是咬牙一起画到深夜的我来说,表示很赞!