>

画线函数Glib_Line算法的研商,小程序swiper下拉判

- 编辑:www.bifa688.com -

画线函数Glib_Line算法的研商,小程序swiper下拉判

时间: 2020-01-01阅读: 64标签: 下拉前言

 

原文发布于vcmo博客 by Jie

swiper没有这个功能:小程序竖向swiper滚动到底部,再往下拉判断到底的功能。 本妹子为了完成“下拉到底就弹框”的业务,想了下面的办法,给小伙伴们分享下。

 

打开ImageView的源码,从源码入手ScaleType的各个属性值:

实现

   在这里首先先简单把我对函数的功能的理解阐述一下,方便后面的分析:Glib_Line函数实现的功能是通过参数给定(x1,y1,x2,y2,color),来确定起点(x1,y1)和终点(x2,y2)两点之间的一条直线,并通过color参数来确定这条直线的颜色。这里这条语句的算法重点在于如何给像素点填充对应的颜色来画出任意直线,至于颜色具体值的确定会在后续的配色原理中阐述。首先先来看两幅图片:图片 1

    public enum ScaleType {
        MATRIX      (0),

        FIT_XY      (1),

        FIT_START   (2),

        FIT_CENTER  (3),

        FIT_END     (4),

        CENTER      (5),

        CENTER_CROP (6),

        CENTER_INSIDE (7);

        ScaleType(int ni) {
            nativeInt = ni;
        }
        final int nativeInt;
    }

给swiper添加transition,swiper-item的位置发生改变时会触发 transition 事件,并获得x、y轴改变的指,event.detail = {dx: dx, dy: dy}。然后将改变值存入数组中,当改变值为0,而且数组的最后一位数有值时,则判断到底了。

他们是相同的一个图片,图片2是图片1放大后的情况。

可以发现ScaleType是ImageView中定义的一个枚举类型。有上面的那些属性可以设置。当然源码中是包含英文注释的,对于这里每个枚举对象的作用和效果,网络上已经有很多前辈解释过,在此就不在赘述。我们从源码来看看这些属性是如何作用于ImageView的src定义的Drawable的。
上一篇博文源码分析Android 中ImageView的设置src与background绘制流程简单的介绍了ImageView的绘制流程,我们知道通过src设置的资源最终都会被解析为Drawable对象赋值与全局变量mDrawable。并且设置mDrawable的属性调用到configureBounds()这个方法,回到源代码:

wxml:

    我们平时在LCD上画直线还无所谓,但是如果画斜线,由于LCD上没有半个像素点,所以,当x或y增加一个像素点之后,对应的y或x要么增加一个像素点,要么保持原来的像素点不动,举个例子:假设:x1=1,x2=320,y1=1,y2=2,那么画线后的情况是x轴的前面160个像素点,y轴的是1,在x轴的后面160个像素点,y轴是2。
   我们根据图3来仔细分析画斜线时可能出现的问题,以及处理的算法。这里我们取众多情况中的一种来分析,即dx>=0 ,dy >= 0,dx>=dy的情况。

    private void configureBounds() {
        ···
        if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
            mDrawable.setBounds(0, 0, vwidth, vheight);
            mDrawMatrix = null;
        } else {
            mDrawable.setBounds(0, 0, dwidth, dheight);

            if (ScaleType.MATRIX == mScaleType) {
                if (mMatrix.isIdentity()) {
                    mDrawMatrix = null;
                } else {
                    mDrawMatrix = mMatrix;
                }
            } else if (fits) {
                mDrawMatrix = null;
            } else if (ScaleType.CENTER == mScaleType) {
                mDrawMatrix = mMatrix;
                mDrawMatrix.setTranslate(Math.round((vwidth - dwidth) * 0.5f),
                                         Math.round((vheight - dheight) * 0.5f));
            } else if (ScaleType.CENTER_CROP == mScaleType) {
                mDrawMatrix = mMatrix;

                float scale;
                float dx = 0, dy = 0;

                if (dwidth * vheight > vwidth * dheight) {
                    scale = (float) vheight / (float) dheight; 
                    dx = (vwidth - dwidth * scale) * 0.5f;
                } else {
                    scale = (float) vwidth / (float) dwidth;
                    dy = (vheight - dheight * scale) * 0.5f;
                }

                mDrawMatrix.setScale(scale, scale);
                mDrawMatrix.postTranslate(Math.round(dx), Math.round(dy));
            } else if (ScaleType.CENTER_INSIDE == mScaleType) {
                mDrawMatrix = mMatrix;
                float scale;
                float dx;
                float dy;

                if (dwidth <= vwidth && dheight <= vheight) {
                    scale = 1.0f;
                } else {
                    scale = Math.min((float) vwidth / (float) dwidth,
                            (float) vheight / (float) dheight);
                }

                dx = Math.round((vwidth - dwidth * scale) * 0.5f);
                dy = Math.round((vheight - dheight * scale) * 0.5f);

                mDrawMatrix.setScale(scale, scale);
                mDrawMatrix.postTranslate(dx, dy);
            } else {
                mTempSrc.set(0, 0, dwidth, dheight);
                mTempDst.set(0, 0, vwidth, vheight);

                mDrawMatrix = mMatrix;
                mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
            }
        }
    }
swiper bind:transition="_transition" block wx:for="" wx:for-item="item" wx:for-index="idx" wx:key="activityId" swiper-item" viewtest/view /swiper-item /block /swiper

   我们把像素点放大抽象为图3,其中黑点定为起始点,灰色线所指的点为第二个像素点的理论位置,绿色点和黄色点为实际像素点可能赋值的点,并且我们这里规定,绿色点到灰色点的距离为a,黄色点到灰色点的距离为b。灰色点坐标为(x,y),绿色点坐标为(x1 1,y1 1),黄色点坐标为(x1 1,y1)。并且规定所画直线在x轴上的投影为dx=x2-x1,在y轴上的投影为dy=y2-y1。

在initImageView()方法中将mScaleType的默认值设为了ScaleType.FIT_CENTER。
我们接着看这个方法:

js:

 理论上,灰色点才是理论点,但是由于像素点不能有小数,所以只可能是上面的绿点或者下面的黄点选一个,具体选哪个要由谁更靠近灰点(误差小)决定图片 2

  1. 判断dwidth与dheight(从mDrawable的获得的宽高)有没有意义,或者mScaleType的值是否为ScaleType.FIT_XY,OK 其中一个,其调用的代码为:

            mDrawable.setBounds(0, 0, vwidth, vheight);
            mDrawMatrix = null;
    

    即按照ImageView内部空间大小设置mDrawable的大小,所以ScaleType.FIT_XY的效果 是伸缩至恰好填充控件

  2. 进入另一个大分支,首先将mDrawable的边界范围设定为它自己的宽高,接着往后看:ScaleType.MATRIX == mScaleType

            mDrawable.setBounds(0, 0, dwidth, dheight);
            if (ScaleType.MATRIX == mScaleType) {
                if (mMatrix.isIdentity()) {
                    mDrawMatrix = null;
                } else {
                    mDrawMatrix = mMatrix;
                }
            }
    

    判断mMatrix是否为单位矩阵(对角线全一)来设置mDrawMatrix(将来在绘制的时候根据此矩阵来改变drawable)为null或者为mMatrix;而mMatrix默认是没有做任何变换的,只有在setImageMatrix()方法中改变其属性,由此得知mMatrix是用来给开发者通过矩阵变换drawable用的。所以可知ScaleType.MATRIX按照用户设置的Matrix变换,否则不会变换,即按照资源自身大小绘制。

  3. 下一个的是**ScaleType.CENTER **:

        else if (ScaleType.CENTER == mScaleType) {
            mDrawMatrix = mMatrix;
            mDrawMatrix.setTranslate(Math.round((vwidth - dwidth) *     0.5f),Math.round((vheight - dheight) * 0.5f));
        }
    

    将mMatrix赋值与mDrawMatrix,此时还没有做任何变换。下一行代码mDrawMatrix发生了转换,setTranslate(float dx,float dy)方法作用是位移,传入的参数也很简单,就是ImageView的内部宽高与drawable宽高差的一半,即移动到ImageView的中间。

  4. 接下来是 ScaleType.CENTER_CROP,直接看源码:

            else if (ScaleType.CENTER_CROP == mScaleType) {
                mDrawMatrix = mMatrix;
    
                float scale;
                float dx = 0, dy = 0;
    
                if (dwidth * vheight > vwidth * dheight) {
                    scale = (float) vheight / (float) dheight; 
                    dx = (vwidth - dwidth * scale) * 0.5f;
                } else {
                    scale = (float) vwidth / (float) dwidth;
                    dy = (vheight - dheight * scale) * 0.5f;
                }
    
                mDrawMatrix.setScale(scale, scale);
                mDrawMatrix.postTranslate(Math.round(dx), Math.round(dy));
            }
    

    可以看到,这个方法定义了一个scale缩放比例,dx,dy分别是x和y轴的偏移量,但下面的代码中dx和dy只会计算一个值。重点在中间的if (dwidth * vheight > vwidth * dheight)语句,其实我们可以将这个表达式变换一下,if(dwidth/vwidth > dheight/vheight)。一目了然,是比较x轴和y轴方向哪个比值更大,我们先假设这个条件成立,看下里会干什么,计算y轴的比例,然后求出dx,另一个分支则是相反计算出x轴的比例,求出dy,最后两句,先缩放图片,再设置偏移。我们回头看一下if语句,当进入第一个分支,scale 是根据y轴的比例计算的,所以此种情况图片的y轴就会缩放到与控件大小一致,y轴就不需要移动,所以只需要计算dx的值。同理另一种情况只需要计算dy的值,就可以让缩放后的图片居中了。现在来分析一下if的条件,图片与控件的大小,无非只有四类情况(临界状态随意上下归并):

    • dwidth < vwidth && dheight < vheight
    • dwidth > vwidth && dheight < vheight
    • dwidth > vwidth && dheight > vheight
    • dwidth < vwidth && dheight > vheight

    具体的分析也很简单,我们最后可以得出结论ScaleType.CENTER_CROP会将图片尽量放大或者避免缩小。最终的结果都是宽高一个属性与控件的相等,另一属性值大于等于控件的值,会完全填充ImageView。且居中。

  5. 下一个ScaleType.CENTER_INSIDE:

             else if (ScaleType.CENTER_INSIDE == mScaleType) {
                mDrawMatrix = mMatrix;
                float scale;
                float dx;
                float dy;
    
                if (dwidth <= vwidth && dheight <= vheight) {
                    scale = 1.0f;
                } else {
                    scale = Math.min((float) vwidth / (float) dwidth,
                            (float) vheight / (float) dheight);
                }
    
                dx = Math.round((vwidth - dwidth * scale) * 0.5f);
                dy = Math.round((vheight - dheight * scale) * 0.5f);
    
                mDrawMatrix.setScale(scale, scale);
                mDrawMatrix.postTranslate(dx, dy);
            }
    

    分析完了上一个这个就相对容易了。首先判断如果图片的宽高都小与控件的宽高,就不缩放。否则就分别判断x轴,y轴的比值,取较小值。其实大家可以想一下,既然第一种情况已经判断了图片宽高都小于控件的宽高了,那么现在一定会有一个方向图片是超出控件的,所控件/图片,一定至少有一个值小于1,而又是取两个的较小值,所以一定会是缩小,而且是尽量缩小(因为取得是最小的比例且小于1)。后面就是计算偏移量让图片居中了。所以ScaleType.CENTER_INSIDE特性我们可以总结:

    • 图片宽高都小于控件,不缩放,居中
    • 图片宽高只要有一方大于控件的。就尽量缩小,图片会完全显示在控件内,居中。
  6. 终于到了最后一个分支:

        else {
                mTempSrc.set(0, 0, dwidth, dheight);
                mTempDst.set(0, 0, vwidth, vheight);
                mDrawMatrix = mMatrix;
                mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
            }
    

    到这里就分支完了,我们看ScaleType中还有三个没有提到:

    • FIT_START
    • FIT_CENTER
    • FIT_END

    在最后一个分支中可以看到调用的mDrawMatrix.setRectToRect()方法第三个参数是ScaleToFit类型,顿时明白了,FIT_*都在此了。去看下这个方法:

//存储y轴改变值 this.dyAry=[];//eventhandle swiper-item 的位置发生改变时会触发 _transition(e) { if (this.lastData) { let dy = e.detail.dy; //如果到底部还继续往下拉,这弹出提示框 if (dy == 0  this.dyAry[this.dyAry.length - 1]  0) { this.dyAry = []; wx.toast('swpier都看完啦') } this.dyAry.push(dy) } }

这里我们根据数学知识可以得到:

图片 3

原文

a=(y 1)-(x 1)×(dy/dx)

03-08-48.jpg

b=(x 1)×(dy/dx)- y

代码特别简单,就是根据mScaleType枚举类型的值去数组sS2FArray里面拿到对应的类型的值。sS2FArray[st.nativeInt

1],ScaleType.FIT_START.nativeInt的值是2,2-1正好是Matrix.ScaleToFit.START在数组中的位置,其它也同样。所以这三个FIT_都是通过mDrawMatrix.setRectToRect()方法作用于矩阵来改变视图的。这三种效果都特别简单,与FIT_XY不同的是,它们不会在两个方向上对图片拉伸,会*保持图片比例,高度与控件内部一致,三个属性分别对应开始、中间、末尾(一般是左、中、右,是按照文字阅读方向)三个位置。*


我们已经获得了变换后的矩阵mDrawMatrix,最终在onDraw()方法中使用:

图片 4

10-27-21.jpg

至此,我们的源码分析已经结束了。

最后一个小疑惑
在sS2FArray数组中有Matrix.ScaleToFit.FILL值,想必就是与FIT_XY对应的(按照ScaleType.FIT_XY.nativeInt值=1,1=1是Matrix.ScaleToFit.FILL在数组中的位置),但是在上面已经对mScaleType=ScaleType.FIT_XY做过处理了,不会到最后一个分支,也就是说数组中Matrix.ScaleToFit.FILL永远不会被用上了?如有理解的朋友还望告知。


欢迎分享交流
邮箱:liuj4563@163.com
原创博文 转载请注明出处。

下面我们只需要判断a和b谁大谁小便可以决定到底选黄点还是绿点。

这里我们选用差值法来判断a,b的大小:

为了去掉小数点,因为dx>0,所以有d=dx*(b-a)= 2*(xdy-ydx) 2dy-dx;

接下来我们只需要判断dx*(b-a)>0还是dx*(b-a)<0;

    这个时候我们用相对坐标来考虑,把起始点(x1,y1)平移到(0,0)点,整条直线虽然也随之平移,这个时候黄点和绿点的坐标也随之改变,但是不改变第二个点到底是在绿点还是黄点的位置。同理也不改变后面其他像素点的选择。所以采用相对坐标后x1=0,y1=0, d1=dx*(b-a)=2dy-dx,通过判断这个d1与0的大小关系就可以知道是图3中的绿点还是黄点了。这个地方我们用到的d1/2就是程序中的变量e.即e=d1/2=dy-dx/2.

   下面我们就对照着函数代码来分析程序中算法的实现。同样我们取众多情况中的一种来分析,即dx>=0 ,dy >= 0,dx>=dy的情况,和上面算法对应起来.

 1 e=dy-dx/2;
 2 
 3 while(x1<=x2)
 4 
 5 {
 6 
 7 PutPixel(x1,y1,color);
 8 
 9    if(e>0){y1 =1;e-=dx;}
10 
11        x1 =1;
12 
13 e =dy;
14 
15 }

 

这段程序就是在满足dx>=0 ,dy >= 0,dx>=dy的情况下,需要执行的内容。

首先是把和a,b差值有直接联系的e求出来,接下来便进入while循环, while循环的作用是对从x1到x2之间所有的像素点赋值。

因为dx>dy,结合图3我们知道在x1和x2之间对应要赋值的有x2-x1个像素点,所以这就是while语句括号中判断的作用,就是对x1和x2之间的x2-x1个像素点赋值,因此每次x1 =1语句的作用就是对x2-x1这个数进行计数,每次加1直到等于x2为止,等于x2了就算把所有该赋值的像素点都赋值了。

while语句里,先是对(x1,y1)这个位置的像素点赋值,然后如果横坐标没到终点的横坐标x2,就一直赋值,期间要对下一个点的位置做判断,即if(e>0)语句:

1.第一次判断如果b>a,即b-a>0,也就e>0,这个时候我们肯定是选择绿点,所以第二点的坐标为(x1 1,y1 1),x1 1在整个while中的x1 =1中实现,y1 1在if语句中的y1 =1中实现,然后便是改变判断第三点的位置时要用到的e。

因为d2=2*(x2dy-y2dx) 2dy-dx,因为第一点相对坐标为(0,0),则第二点的相对坐标为(1,1),所以d2=4dy-3dx,此时的e2=d2/2=2dy-3/2dx=e dy-dx;e2中对原来的e dy是在整个while中的e =dy中实现的,对原来的e-dx是在在if语句中的e-=dx中实现的。

2.第一次判断如果b<a,即b-a<0,也就是e<0,这个时候我们肯定选择黄点,所以第二点的坐标为(x1 1,y1),x1 1在整个while中的x1 =1中实现,此时不进入if语句,然后便是改变判断第三点的位置时要用到的e。

因为d2=2*(x2dy-y2dx) 2dy-dx,因为第一点相对坐标为(0,0),则第二点的相对坐标为(1,0),所以d2=4dy-dx,此时的e2=d2/2=2dy-dx/2=e dy;e2中对原来的e dy是在整个while中的e =dy中实现的,此时不进入if语句,dx不增加。

同样的道理,到了判断e的时候,重复上面的过程,d=2*(xdy-ydx) 2dy-dx;x,y分别为当时判断点的相对坐标。

例如,第二点选择的是黄点,第三点选择的是绿点,通过算法我们得到e2=e1 dy;e3=e2 dy-dx=e1 2dy-dx=e 2dy-dx=3dy-3/2dx;

下面通过对e的定义来验证一下:

第二点的相对坐标为(1,0),则第三点的坐标为(2,1)

e3=d3/2=(x3dy-y3dx) dy-dx/2=2dy-dx dy-dx/2=3dy-3/2dx;

两种方法的e3值完全一样,以此类推证明了程序与算法的吻合,程序完全可以实现算法所需要的功能。

以上只是分析了其中dx>=0 ,dy >= 0,dx>=dy一种情况,我们仔细分析发现,其他的七种情况的数学含义以及分析方法和第一种完全一样,只是斜线的斜率,以及dx,dy长短等不同而以,这里不做过多的赘述。

所有的情况分别为8种:

1.dx>=0,dy>0 ,dx>=dy

2.dx>=0,dy>0 ,dx<dy

3.dx>=0,dy<0 ,dx>=dy

4.dx>=0,dy<0 ,dx<dy

5.dx<0, dy>0 ,dx>=dy

6.dx<0, dy>0 ,dx<dy

7.dx<0, dy<0 ,dx>=dy

8.dx<0, dy<0 ,dx<dy

具体代码如下:

  1 // glib库中的画线函数,可以画斜线,线两端分别是(x1, y1)和(x2, y2)
  2 void glib_line(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, unsigned int color)
  3 {
  4     int dx,dy,e;
  5     dx=x2-x1; 
  6     dy=y2-y1;
  7     
  8     if(dx>=0)
  9     {
 10         if(dy >= 0) // dy>=0
 11         {
 12             if(dx>=dy) // 1/8 octant
 13             {
 14                 e=dy-dx/2;  
 15                 while(x1<=x2)
 16                 {
 17                     lcd_draw_pixel(x1,y1,color);
 18                     if(e>0){y1 =1;e-=dx;}    
 19                     x1 =1;
 20                     e =dy;
 21                 }
 22             }
 23             else        // 2/8 octant
 24             {
 25                 e=dx-dy/2;
 26                 while(y1<=y2)
 27                 {
 28                     lcd_draw_pixel(x1,y1,color);
 29                     if(e>0){x1 =1;e-=dy;}    
 30                     y1 =1;
 31                     e =dx;
 32                 }
 33             }
 34         }
 35         else           // dy<0
 36         {
 37             dy=-dy;   // dy=abs(dy)
 38 
 39             if(dx>=dy) // 8/8 octant
 40             {
 41                 e=dy-dx/2;
 42                 while(x1<=x2)
 43                 {
 44                     lcd_draw_pixel(x1,y1,color);
 45                     if(e>0){y1-=1;e-=dx;}    
 46                     x1 =1;
 47                     e =dy;
 48                 }
 49             }
 50             else     // 7/8 octant
 51             {
 52                 e=dx-dy/2;
 53                 while(y1>=y2)
 54                 {
 55                     lcd_draw_pixel(x1,y1,color);
 56                     if(e>0){x1 =1;e-=dy;}    
 57                     y1-=1;
 58                     e =dx;
 59                 }
 60             }
 61         }    
 62     }
 63     else //dx<0
 64     {
 65         dx=-dx;        //dx=abs(dx)
 66         if(dy >= 0) // dy>=0
 67         {
 68             if(dx>=dy) // 4/8 octant
 69             {
 70                 e=dy-dx/2;
 71                 while(x1>=x2)
 72                 {
 73                     lcd_draw_pixel(x1,y1,color);
 74                     if(e>0){y1 =1;e-=dx;}    
 75                     x1-=1;
 76                     e =dy;
 77                 }
 78             }
 79             else        // 3/8 octant
 80             {
 81                 e=dx-dy/2;
 82                 while(y1<=y2)
 83                 {
 84                     lcd_draw_pixel(x1,y1,color);
 85                     if(e>0){x1-=1;e-=dy;}    
 86                     y1 =1;
 87                     e =dx;
 88                 }
 89             }
 90         }
 91         else           // dy<0
 92         {
 93             dy=-dy;   // dy=abs(dy)
 94 
 95             if(dx>=dy) // 5/8 octant
 96             {
 97                 e=dy-dx/2;
 98                 while(x1>=x2)
 99                 {
100                     lcd_draw_pixel(x1,y1,color);
101                     if(e>0){y1-=1;e-=dx;}    
102                     x1-=1;
103                     e =dy;
104                 }
105             }
106             else        // 6/8 octant
107             {
108                 e=dx-dy/2;
109                 while(y1>=y2)
110                 {
111                     lcd_draw_pixel(x1,y1,color);
112                     if(e>0){x1-=1;e-=dy;}    
113                     y1-=1;
114                     e =dx;
115                 }
116             }
117         }    
118     }
119 }

 

本文转自:

本文由必发88官网发布,转载请注明来源:画线函数Glib_Line算法的研商,小程序swiper下拉判