问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

【Flutter绘制技巧】Path路径变换

发布网友 发布时间:2024-10-03 16:33

我来回答

1个回答

热心网友 时间:2024-10-03 23:23

本文来探讨一下路径的变换,我们知道Canvas本身也支持变换,那Path的变换有什么必要性吗?和Canvas变换又有什么区别呢?如何在一次变换中叠加多种变换效果,如何修改变换中心?这些都是绘制的基本技能。本文将作为《Flutter绘制指南-妙笔生花》的补充内容,被同步到小册中。本文源码见【idraw/extra_03_path】

1.绘制路径测试

如下,通过PathPainter作为画板,绘制如下图案:左上角是一个三角形路径。坐标系以画布中心为原点,右和下方为正方向,只起到辅助查看作用。通过之前封装的Coordinate类进行绘制,详见coordinate_pro.dart。

voidmain(){runApp(CustomPaint(painter:PathPainter(),));}

可以看出默认情况下,以画布的左上角为原点。

classPathPainterextendsCustomPainter{@overridevoidpaint(Canvascanvas,Sizesize){Paintpaint=Paint()..style=PaintingStyle.stroke;Pathpath=Path()..lineTo(40,40)..relativeLineTo(0,-40)..close();canvas.drawPath(path,paint);}@overrideboolshouldRepaint(covariantPathPainteroldDelegate)=>true;}2.画板的变换和路径的变换

现在,如果想让这个三角形绘制时以画布中心为原点,实现这个需求的方式有很多。总得来说有两个方向,一者是对Canvas进行处理,一者是对Path进行处理。

如下是对Canvas进行变换,将画板的左上角平移到中心,如下浅蓝色区域:

---->[extra_03_path/01]----canvas.translate(size.width/2,size.height/2,);

如下,不改变画布,通过对Path处理,也可以完成同样的显示。此时画布的原点仍在屏幕左上角,如下浅蓝色区域:

---->[extra_03_path/02]----Pathpath=Path()..moveTo(size.width/2,size.height/2,)..relativeLineTo(40,40)..relativeLineTo(0,-40)..close();

优劣党开始发问,那这两种方式有什么区别,用哪种更好呢。还是那句话,抛开场景谈优劣,就是纸上谈兵,两者各有各的好处,各有各的不足。如果对canvas进行变换,那么接下来的所有绘制都会在该变换的基础上;如果是对Path进行处理,不会影响canvas。

另外有个非常重要的注意点,如果是对Path进行处理,它的真实位置是发生变化的,对canvas进行变换,Path的真实位置不变。Path中有个contains方法,用于校验点是否在路径内。比如下面的红点是30,10,通过canvas平移实现的。

此时通过输出可以看出30,10点仍在path路径下,这就说明path只是在绘制时进行了视觉上的偏移,它本身还在红色虚线所示的区域。这样的话,如果路径需要校验触点,就需要额外的运算处理。可能有人会说,不就是加加减减,简单计算一下吗,也不麻烦。但这里只是平移,如果是缩放、旋转、斜切等变换,你还算得过来吗?

---->[extra_03_path/03]----print(path.contains(Offset(30,10)));//true3.路径变换

其实前面只是通过对路径进行移动处理而已,并没有真正用到变换。但有些场景通过计算会非常麻烦,这时路径的变换就会非常实用。比如需要旋转10°?,如下通过Matrix4进行变换,rotationZ表示沿Z轴旋转,也就是说在XOY水平面上旋转。可以看出,默认情况下,是以屏幕左上角为变换中心的。

---->[extra_03_path/05]----Matrix4m4=Matrix4.rotationZ(10*pi/180);path=path.transform(m4.storage);

?那如何指定某点为变换中心呢?在一次变换中,通过平移,可以改变变换中心。比如下面左上角的红色虚线路径,通过平移变换,形成如下黑线路径。

---->[extra_03_path/06]----Pathpath=Path()..moveTo(0,0)..relativeLineTo(40,40)..relativeLineTo(0,-40)..close();Matrix4m4=Matrix4.translationValues(size.width/2,size.height/2,0);path=path.transform(m4.storage);

这时只要在m4的基础上叠加旋转变换,这样对于旋转变换来说,变换中心就是上面红点所示,如下图所示。变换效果的叠加,本质上就是两个4*4矩阵的乘法,通过multiply方法实现。注意这个方法无返回值,会改变?m4的值。

---->[extra_03_path/07]----Matrix4m4=Matrix4.translationValues(size.width/2,size.height/2,0);Matrix4rotateM4=Matrix4.rotationZ(10*pi/180);m4.multiply(rotateM4);path=path.transform(m4.storage);

在一次变换中,我们可以叠加多个变换,比如下面在旋转的基础上,再叠加缩放变换。这个变换中心依然是红点,也就是说,在一次变换中,通过平移变换可以用来修改变中心。

---->[extra_03_path/08]----Matrix4m4=Matrix4.translationValues(size.width/2,size.height/2,0);Matrix4rotateM4=Matrix4.rotationZ(10*pi/180);Matrix4scaleM4=Matrix4.diagonal3Values(2,2,1);m4.multiply(rotateM4);m4.multiply(scaleM4);path=path.transform(m4.storage);

那接下来思考一个问题,如何以任意点为变换中心呢,比如以20,20点为变换中心,进行旋转和缩放操作。实现思路也非常简单,定义两个偏移的矩阵,在旋转和缩放前,先叠加center,让变换中心变为20,20。在最后为了不影响结果,在通过back矩阵,平移会取即可。

---->[extra_03_path/09]----Matrix4m4=Matrix4.translationValues(size.width/2,size.height/2,0);Matrix4center=Matrix4.translationValues(20,20,0);Matrix4back=Matrix4.translationValues(-20,-20,0);Matrix4rotateM4=Matrix4.rotationZ(10*pi/180);Matrix4scaleM4=Matrix4.diagonal3Values(2,2,1);m4.multiply(center);//tag1m4.multiply(rotateM4);m4.multiply(scaleM4);m4.multiply(back);//tag2path=path.transform(m4.storage);canvas.drawPath(path,paint);4.路径变换与命中

路径的变换操作是对路径本身的真实操作,通过contains方法,判断点是否在路径之内。这个点是相对于组件左上角的,也就是说通过手势事件,可以很方便地校验触点是否在路径之内。比如下面的效果,当在区域内时,路径加粗且为橙色,实现代码详见:【extra_03_path/10】

classPathPainterextendsCustomPainter{@overridevoidpaint(Canvascanvas,Sizesize){Paintpaint=Paint()..style=PaintingStyle.stroke;Pathpath=Path()..lineTo(40,40)..relativeLineTo(0,-40)..close();canvas.drawPath(path,paint);}@overrideboolshouldRepaint(covariantPathPainteroldDelegate)=>true;}0

通过不对路径变换,而是通过canvas变换,那么在校验时,就需要进行复杂的点位计算。这就是两者之间最大的区别,另外canvas变换本质上也是通过Matrix4实现的,上面所说的叠加特性对canvas也使用。

最后简单说一下Matrix4#multiply和Matrix4#multiplied两个方法的区别。从源码中可以看出multiplied本质上是通过multiply实现功能的,只不过它会克隆对象,对新对象进行multiply操作。也就是说这个方法会返回一个新的Matrix4对象,不会影响调用者的内部数据。

而multiply方法,如下所示:是根据矩阵的乘法,来修改自身的数据。

所以如果调用者需要在后续被使用,可以通过?Matrix4#multiplied?返回个新的。如果不需要被使用,通过?Matrix4#multiply方法直接修改自身数据即可。了解其原理之后,就不会用起来稀里糊涂的。那本文就到这里,谢谢观看~

原文:https://juejin.cn/post/7097789306302890020
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
我网贷一万,手续费300,分24期,每期506.67,请问怎么计算月息,和年利率... 多普达手机铃音多普达手机铃声 【车载软件推荐】有车一族必备软件有哪些?好用的车主必备app 我画画时常被说我画画不动脑筋,弱弱的问一句,画画怎样动脑筋? 三星手机如何设置时间在桌面显示? 精确到十分位,要看小数点右边第一位 对吗 要把一个小数精确到十分位,就要看这个小数的( )A.十分位B.百分位C... 黑米算不算粗粮 存定期协议利率选是还是否? 同城上那些卖二手ndsi的是真的吗? 乳腺增生BI-RADS一级什么意思 一、Canvas基础-09、图形的平移与旋转 乳腺BI-RADS1级能治愈吗 Canvas 从进阶到退学 canvas 2D API1.2 转换(Transformations) 物理问题:如果一个同学相当于车厢外竖直向上跳起,他是否会落�... 是一个物理题:一个人在一个匀速直线运动的火车的车顶上,垂直向上跳,请 ... 是一个物理题:一个人在一个匀速直线运动的火车上,垂直向上跳,请问他... 在电梯上跳起来 落地还是不是在原来的台阶上? 聊城怎么办中级工程职称证 ...朝上仍一个乒乓球,这个乒乓球会落在扔出去原点的哪 一个人再匀速直线运动的轮船尾部向上跳,会掉到海里吗 ...便产生了一个想法,地球既然转的这么快,那么 在匀速运动的轮船长向上抛任何高度的小球,会落在原地吗? ...的车上你向上垂直跳动,车会在你脚下移动吗?人在空中停留的时间比较... cd机读不了盘 建伍320CD没声音 乌云盖顶形态预示着什么 黄壁庄水库设计 祸生肘腋正确是什么生肖 为什么焊不锈钢焊缝发黑? 家放什么花最旺财 买什么花最旺财最旺人 广州三天两夜游,求方便的路线 去广州的路线,请高手指教,对广州熟的 我家有两只猫,大猫有猫癣,喷了药剂之后小猫开始出现了呕吐的现象,吃啥... 本人在广州,想去云南游玩,大约7-10天,请高手指教安排路线 请教从广州去凤凰古城,自助游的路线,什么时候去可以看到雪景。_百度知 ... 港澳通行证是“L”签,想去港澳自由行,寻求指教 厦门医学院学费多少钱一年 厦门医学院在哪 厦门医学院在哪个校区 厦门大学医学院现任领导 苏教版八年级上册语文书中,有哪些古诗?(2013年的) 苏教版八年级语文课文中的所有古诗(急需) 我姓陈,老婆姓王,儿子是2011年1月31日11点55分出生,是公历,请帮儿子取... 父亲姓强,母亲姓石,宝宝出生于农历2011年1月1日,是男孩,帮忙起个名字... 男孩子爸爸姓丁妈妈姓石,生日2011年11月15日出生取什么名字好 开学季个性外观MP3有哪些值得推荐? 汶川大地震中外援助