;

白小萌】提问: 怎样做一个让用户自己上传图片,然后自动切分图片,生成拼图的案例

让用户自己上传图片,然后自动切分成几份,难度可以让用户选,比如6份,9份,16份。然后就可以生成一个拼图游戏,转发出去之后,就可以让别人来玩这个拼图了。


1 个回答

heymu

先贴案例二维码:

image.png

案例下载地址:http://www.ih5.cn/editor3/app/workCopy/5519133

先简单说一下这个案例的主要实现思路:

首先,在第一页做一个空图,然后在“上传图片”按钮那里,做上传图片的事件,让用户可以换图;然后,做两个计数器,分别用来记录需要横向切片的数量,和纵向切片的数量,用一个下拉菜单,来分别设置这两个计数器的值,难度越高,两个计数器的值就越大;然后,在生成拼图的时候,根据两个切片数量计数器,来克隆一些空的对象组,然后在每个对象组中,来复制用户上传的图片,这些对象组的剪切属性都为“是”,这样,就可以通过复制图片的不同x和y,来做切图的效果了。


除了利用对象组的剪切,还可以利用画布中对象的遮罩来做类似的效果。原理类似和本案例中的做法类似。


接下来讲一下这个案例的三个关键点:生成拼图格子、生成拼图碎片、制作拼图吸附效果。


一,生成拼图的格子

这些拼图的格子,要根据用户选择的难度来生成,比如,当用户选择中级难度的时候,要生成9个格子:

image.png

这些格子,是在用户点击“生成拼图按钮”的时候,在一个空的格子容器中,来复制一个格子模板来生成的。

格子模板,其实就是一个有背景颜色的对象组:

image.png

这个对象组的关键是加了两个自定义属性,“行数”和“列数”,用来标记之后克隆出来格子的序号。这个是必须的,否则在之后判断拼图重叠的时候,无法判断拼图和格子的对应关系。


在“生成拼图按钮”被点击的时候,就可以根据这个格子模板,来生成格子了:

image.png

这里,用了两层嵌套的循环,第一层循环的循环次数是纵向切片数,就是记录纵向切片数的计数器,第二层循环的循环次数是横向切片数的切片数。有关循环和循环的嵌套,请见另一篇问答:


在创建格子的时候,我们设了6个属性:

首先是格子的宽、高

由于每个格子的宽高是根据切片数量来调整的,而不是固定的,所以需要在创建格子的时候来设定。

我们希望无论切片的数量如何,最终拼图组合的大小都是固定的,在这里,我们把拼图组合的大小和原图的大小设为一致的。因此,把克隆出来的格子的宽、高,分别设为当前格子模板的宽和高:

image.png

之所以可以直接设为当前格子模板的宽和高,是因为在下拉菜单选择的时候,我们已经对格子模板的宽高进行了设置,将格子模板的宽设为原图的宽除以横向切片数,高设为原图的高除以纵向切片数,这样把这些格子模板拼接起来的时候,刚好可以拼成原图的大小。

image.png

然后是格子的X、Y,就是格子的位置

格子位置需要保证格子能够整齐的排列,比如,如果第一个格子的X、Y是0,0, 那么第二个格子,要排列在第一个的右边,所以X是格子的宽,Y还是0, 以此类推。

由于我们设的是双层循环,第一层是纵向的,第二层是横向的,所以我们是先创建第一行格子,再创建第二行,这样从上到下创建;每一行,我们都从左到右创建每个格子。所以,循环次数1,就代表当前格子所在的行数,循环次数2,代表当前格子所在的列数(注意循环次数是从0开始,所以行数和列数也是从0开始,比如,第一行第一列的格子,行数和列数分别为0,0, 第二行第一列,行数和列数为1,0);


image.png

以上,我们将格子的X,先设为格子模板的宽*循环次数2,就是当前格子的宽*当前格子所在列数,为了在格子之间设一些间隔,我们在格子模块的宽的基础是又加了1个像素,所以变成了(格子模板.宽+1)*循环次数2, 最后,考虑到整体拼图的位移,我们又在之前的基础上加了110的横向位移;

格子的Y也是类似的设置,Y轴上,我们加的整体位移是50;

最后是格子的自定义属性“行数”和“列数”

为了之后做重叠的判断,我们需要将格子的行数和列数进行标记,设置行数和列数这两个属性分别为循环次数1和循环次数2。

为了方便案例的调试,我们在格子里加了两个计数器,在格子创建完成之后,为这两个计数器分别赋值为当前格子所在的行数和列数,比如,我们创建3行2列的格子:

image.png

二、生成拼图碎片

生成拼图碎片,和生成格子的原理是一样的,我们也是做了一个拼图容器,并加了两个自定义属性"行数"和“列数”:

image.png


由于格子和拼图是一一对应的,所以,我们只需要在生成格子循环的后面,同时加上拼图生成的动作即可:

image.png

生成拼图的宽和高,也是拼图模板的当前宽和高,因为在之前选择难度的时候,我们也同时设了拼图模板的宽和高,其实这里可以直接用格子模板的宽和高,但是为了整齐起见,我们还是分别设了格子模板和拼图模板的宽高;

生成拼图的行数和列数,和格子一样,也是设为循环次数1和循环次数2;

最后,是拼图的X和Y,由于拼图在开始的时候,是随机分布的,所以我们用了random()这个公式来生成一个0-1之间的随机数。注意,我们还为拼图加了一个随机的旋转度。


这样,空的拼图容器就做好了。接下来,我们要把原图复制到这些拼图容器中,用来制作切图的效果。

这个原理其实很简单,就是在每个拼图容器里面复制一张图片,并为这些图片设置一些位移,由于我们把这些容器的剪切设为了是,就只会显示容器范围内的图片了。以下gif说明了这个原理:

切图.gif


我在每个拼图容器里加了一个自动播放的触发器,触发器的时长为默认时长,就是一帧,所以基本上是瞬间触发的,也就是在每个拼图容器刚刚被创建出来的时候,就触发,触发时,我们来复制图片:

image.png

以上,便是切图的事件了,这里要注意,复制图片的X和Y坐标是负的,因为这个X和Y坐标,是内部图片相对于当前拼图容器的位置,只有是负的,才能在容器剪切的时候,依旧看到图片;负的具体值是当前拼图模板的宽或高乘以当前模板的列数或行数,这样就能按切片的数量要求,来制作拼图碎片了。


三、制作拼图吸附效果

最后一个关键,是制作拼图的吸附效果,就是当用户移动拼图到相应的格子附近的时候,把拼图“粘”上去,同时,粘上去之后,禁用拼图的拖动行为,让用户不能再拖动拼图块了。

这个效果,只是用到了一个重叠事件:

image.png

当拼图模板和相应的格子模板重叠的时候,我让拼图模板设置属性,让它的允许拖动属性设为“不允许”,同时,让当前的拼图模板变换状态,变换状态和设置属性的用法一样,只是可以制作一个动画效果,这样拼图碎片会“移动”到格子模板上,而不是突然跳过去。最后,让当前分数加1,来计算成功的拼图数量。


注意,在这里的目标对象,我们要在下拉菜单中选择“当前对象”,而不能直接在对象树中去选“拼图模板”。

image.png

所谓“当前对象”,就是当前的触发对象,就是开始重叠的这个拼图模板。尽管这个触发对象,是拼图模板,但由于我们需要操作的是每个复制出来的拼图碎片,而不是原来那个用于充当模板的拼图模板,所以使用“当前对象”来指定那个触发重叠事件的拼图碎片。

同样道理,我们需要让触发重叠的拼图碎片,移动到和它对应的格子的位置上去,这个时候,我们有要使用一个特殊的对象选择器来指定这个和它重叠的格子,就是“重叠目标对象”,当触发条件是重叠或碰撞的时候,我们都可以在下面的绿色块中的下拉菜单选择“重叠目标对象”或“碰撞目标对象”,用来指定当前重叠或碰撞事件所碰触到的对象。以下的gif描述了这个设置过程:


最后,我们来讲一下这个重叠的判断条件。

image.png

我们在拼图模板和格子模板重叠的时候,加了判断条件,否则,任何由拼图模板和格子模板复制出来的拼图碎片和格子在重叠的时候,都会触发这个事件,这样就无法达到判断拼图碎片和相应格子配对的效果了。

这个判断条件,其实很简单,就是判断复制出来的拼图碎片的行数和列数,要和重叠目标格子的行数和列数一致,由于行数和列数是在拼图碎片和格子进行创建的时候设定的,每个碎片和格子都有一个独立的行数和列数,相当于是一个序号,因此,我们需要判断拼图碎片和格子的序号相同,就可以准确的判断对应关系了。因此,只有当某个拼图碎片和格子重叠,且它们的序号是一致时,才会触发这个重叠事件,来让拼图碎片“吸附”到对应的格子上去。


学习地图