实现望远镜效果的两种方式

510 阅读2分钟

先看下效果

录屏_选择区域_20211009092409.gif

实现这种效果,我所知道的有两种办法,而且都很简单,一是使用着色器Shade,第二是使用PorterDuffXfermode。

着色器是一个非常好玩的东西,Shade是个基类,子类有五个,最常用的就是BitmapShader,相当于给画笔设置了一个图片,在调用drawXXX绘图的时候,会呈现出这个图片所对应的位置。

Shade实现

下面是使用着色器实现的步骤,首先给paint设置setShader,当调用drawCircle绘制圆时,圆中心点的坐标会被绘制成图片中所对应位置,比如中心点坐标是10,10,那么就会从图片10,10的位置开始绘制。


@RequiresApi(Build.VERSION_CODES.P)
class MiniLoading @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

 var srcBitmap: Bitmap;
 var currentX: Float = 0f;
 var currentY: Float = 0f;
 init {
     srcBitmap = BitmapFactory.decodeResource(resources, R.drawable.back)
     post {
         var ma = Matrix()
         ma.setScale(width / srcBitmap.width.toFloat(), width / srcBitmap.width.toFloat())
         srcBitmap = Bitmap.createBitmap(srcBitmap, 0, 0, srcBitmap.width, srcBitmap.height, ma, false)
         invalidate()
     }
}


override fun onDraw(canvas: Canvas) {
        var paint = Paint()
        var bitmapShader = BitmapShader(srcBitmap, Shader.TileMode.MIRROR, Shader.TileMode.CLAMP)
        paint.setShader(bitmapShader)
        canvas.drawCircle(currentX,currentY,100f,paint)
}


override fun onTouchEvent(event: MotionEvent): Boolean {
        if (event.action==MotionEvent.ACTION_MOVE){
            this.currentX = event.x!!
            this.currentY = event.y!!
            invalidate()
        }
     return true;
 }
}

那么当图片大小是200*200时候,当绘制到300的时候,该怎么办,这就是BitmapShader构造器中TileMode的作用,取值有以下三个

  1. TileMode.CLAMP:拉伸最后一个像素去铺满剩下的地方;

  2. TileMode.MIRROR:通过镜像翻转铺满剩下的地方;

  3. TileMode.REPEAT:重复图片平铺整个画面。

对应的我们还可以绘制炫酷的字体。

  var paint = Paint()
  var bitmapShader = BitmapShader(srcBitmap, Shader.TileMode.MIRROR, Shader.TileMode.CLAMP)
  paint.setShader(bitmapShader)
  paint.textSize=100f
  canvas.drawText("听风逝夜",width/2.toFloat(),height/2.toFloat(),paint)

image.png

PorterDuffXfermode实现

上一张看烂了也不会的图,PorterDuffXfermode就相当于PS中选区相交,相减,实现本效果用到的是SRC_IN

image.png

首先在canvas绘制一个圆圈,在设置xfermode为SRC_IN,在绘制一个全屏的图片,那么,和圆对应位置的图片就会被保留下,其他位置会被删除,这样不停移动currentX、currentY,就形成了望远镜效果。

override fun onDraw(canvas: Canvas) {

  val paint = Paint()
  canvas.drawCircle(
      currentX,
      currentY,
      100f,
      paint
  )
  val rect = Rect(0, 0, this.width, this.height)
  paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
  canvas.drawBitmap(srcBitmap, rect, rect, paint)
}     

利用这个特性还可以实现刮奖的效果。