JavaFX实现ImageView局部模糊:按方向动态模糊人物图像
实现ImageView可指定区域的渐进式模糊效果
嘿,我刚好之前啃过类似的需求,要实现这种能控制模糊区域(从上到下/从右到左覆盖人物)的效果,其实核心就是把模糊后的图像和原图分层,然后通过进度控制模糊层的显示范围就行。下面给你分平台的具体实现方案,亲测好用:
一、Android 端实现方案(自定义ImageView)
我们可以通过自定义ImageView,结合RenderScript生成模糊Bitmap,再根据进度裁剪模糊区域叠在原图上。
1. 先写一个模糊工具类
object BlurUtils { fun blurBitmap(context: Context, bitmap: Bitmap, radius: Float): Bitmap { val rs = RenderScript.create(context) val input = Allocation.createFromBitmap(rs, bitmap) val output = Allocation.createTyped(rs, input.type) val script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)) script.setRadius(radius) script.setInput(input) script.forEach(output) output.copyTo(bitmap) rs.destroy() return bitmap } }
2. 自定义ProgressBlurImageView
class ProgressBlurImageView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : AppCompatImageView(context, attrs, defStyleAttr) { private var blurProgress = 0f // 取值0-1,0=无模糊,1=完全覆盖 private var blurredBitmap: Bitmap? = null private val paint = Paint() // 外部调用这个方法更新模糊进度 fun setBlurProgress(progress: Float) { blurProgress = progress.coerceIn(0f, 1f) invalidate() } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) blurredBitmap ?: run { // 首次绘制时生成模糊后的Bitmap val originalBitmap = (drawable as BitmapDrawable).bitmap.copy(Bitmap.Config.ARGB_8888, true) blurredBitmap = BlurUtils.blurBitmap(context, originalBitmap, 25f) } blurredBitmap?.let { blurBmp -> // --- 从上到下覆盖的逻辑 --- val blurHeight = height * blurProgress val srcRect = Rect(0, 0, width, blurHeight.toInt()) val destRect = Rect(0, 0, width, blurHeight.toInt()) // --- 如果要改成从右到左,替换成下面这段 --- // val blurWidth = width * (1 - blurProgress) // val srcRect = Rect((width - blurWidth.toInt()), 0, width, height) // val destRect = Rect((width - blurWidth.toInt()), 0, width, height) canvas.drawBitmap(blurBmp, srcRect, destRect, paint) } } // 页面销毁时回收Bitmap,避免内存泄漏 override fun onDetachedFromWindow() { super.onDetachedFromWindow() blurredBitmap?.recycle() blurredBitmap = null } }
使用方式
在布局里引用这个自定义View,然后通过setBlurProgress()方法更新进度,比如配合ValueAnimator做平滑动画:
val blurView = findViewById<ProgressBlurImageView>(R.id.blur_image) ValueAnimator.ofFloat(0f, 1f).apply { duration = 2000 addUpdateListener { animation -> blurView.setBlurProgress(animation.animatedValue as Float) } }.start()
二、iOS 端实现方案(自定义UIImageView)
思路和Android一致:先生成模糊图,再根据进度裁剪模糊区域绘制。
自定义ProgressBlurImageView
class ProgressBlurImageView: UIImageView { // 0-1的进度值,设置后自动重绘 var blurProgress: CGFloat = 0.0 { didSet { setNeedsDisplay() } } // 懒加载生成模糊后的图片 private lazy var blurredImage: UIImage? = { guard let originalImage = image else { return nil } let context = CIContext(options: [.useSoftwareRenderer: false]) let filter = CIFilter(name: "CIGaussianBlur")! filter.setValue(CIImage(image: originalImage), forKey: kCIInputImageKey) filter.setValue(25, forKey: kCIInputRadiusKey) guard let outputImage = filter.outputImage else { return nil } let cgImage = context.createCGImage(outputImage, from: outputImage.extent)! return UIImage(cgImage: cgImage, scale: originalImage.scale, orientation: originalImage.imageOrientation) }() override func draw(_ rect: CGRect) { super.draw(rect) guard let blurImage = blurredImage else { return } // --- 从上到下覆盖的逻辑 --- let blurHeight = rect.height * blurProgress let blurRect = CGRect(x: 0, y: 0, width: rect.width, height: blurHeight) // --- 从右到左的话替换成这段 --- // let blurWidth = rect.width * (1 - blurProgress) // let blurRect = CGRect(x: rect.width - blurWidth, y: 0, width: blurWidth, height: rect.height) // 裁剪模糊图并绘制 UIGraphicsBeginImageContextWithOptions(rect.size, false, UIScreen.main.scale) blurImage.draw(in: rect) let clippedImage = UIGraphicsGetImageFromCurrentImageContext()?.cropped(to: blurRect) UIGraphicsEndImageContext() clippedImage?.draw(in: blurRect) } } // 给UIImage加个裁剪扩展 extension UIImage { func cropped(to rect: CGRect) -> UIImage { guard let cgImage = cgImage?.cropping(to: rect) else { return self } return UIImage(cgImage: cgImage, scale: scale, orientation: imageOrientation) } }
使用方式
在代码中初始化或从Storyboard引用,然后配合动画更新进度:
let blurImageView = ProgressBlurImageView(image: UIImage(named: "person")) UIView.animate(withDuration: 2.0) { blurImageView.blurProgress = 1.0 }
几个关键注意事项
- 模糊半径:建议设置在15-25之间,太大的话会严重影响性能,尤其是在低端设备上。
- 内存管理:生成模糊Bitmap后要注意及时回收(Android的
recycle(),iOS避免强引用),防止内存泄漏。 - 动画平滑度:用系统自带的动画API(ValueAnimator/UIView.animate)来更新进度,比手动postDelayed要流畅得多。
内容的提问来源于stack exchange,提问作者Barba
相关产品推荐
相关产品推荐

