Android及Harmonyos实现图片进度显示效果

鸿蒙Harmonyos实现,使用ImageKnife自定义transform来实现图片进度效果

import { Context } from '@ohos.abilityAccessCtrl';
import { image } from '@kit.ImageKit';
import { drawing } from '@kit.ArkGraphics2D';
import { GrayScaleTransformation, PixelMapTransformation } from '@ohos/imageknife';/*** 垂直进度灰度变换:顶部灰度,底部彩色,带波浪边界* @param grayRatio 顶部灰度区域占比 [0-1]* @param enableWave 是否启用波浪边界效果,false为直线分割(性能更佳)*/
@Sendable
export class VerticalProgressGrayscaleTransformation extends PixelMapTransformation {private readonly grayRatio: number;private readonly enableWave: boolean;private static readonly WAVE_AMPLITUDE_RATIO: number = 0.08; // 波浪振幅比例private static readonly WAVE_FREQUENCY: number = 2.5; // 波浪频率private static readonly WAVE_STEPS: number = 40; // 波浪平滑度(降低提升性能)constructor(grayRatio: number, enableWave: boolean = true) {super();this.grayRatio = Math.max(0, Math.min(1, grayRatio));this.enableWave = enableWave;}override async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise<PixelMap> {try {// 边界情况快速处理if (this.grayRatio <= 0.001) {return toTransform;}if (this.grayRatio >= 0.999) {return new GrayScaleTransformation().transform(context, toTransform, width, height);}// 获取实际图片尺寸const imageInfo = await toTransform.getImageInfo();if (!imageInfo.size) {return toTransform;}const actualWidth = imageInfo.size.width;const actualHeight = imageInfo.size.height;const grayHeight = Math.floor(actualHeight * this.grayRatio);// 如果灰度区域太小,直接返回原图if (grayHeight < 5) {return toTransform;}return this.applyVerticalGrayEffect(context, toTransform, actualWidth, actualHeight, grayHeight);} catch (err) {console.error('[VerticalProgressGrayscaleTransformation] Error:', err);return toTransform;}}/*** 应用垂直灰度效果(性能优化版)*/private async applyVerticalGrayEffect(context: Context,original: PixelMap,width: number,height: number,grayHeight: number): Promise<PixelMap> {try {// 创建结果PixelMap并复制原图const result = await this.createClonedPixelMap(original, width, height);// 创建灰度图const grayPixelMap = await new GrayScaleTransformation().transform(context, original, width, height);// 一次性完成Canvas操作const canvas = new drawing.Canvas(result);canvas.save();// 设置裁剪路径并绘制灰度区域canvas.clipPath(this.createOptimizedClipPath(width, grayHeight));canvas.drawImage(grayPixelMap, 0, 0, new drawing.SamplingOptions(drawing.FilterMode.FILTER_MODE_LINEAR));canvas.restore();return result;} catch (error) {console.error('[VerticalProgressGrayscaleTransformation] Apply effect error:', error);return original;}}/*** 创建克隆PixelMap(优化版)*/private async createClonedPixelMap(original: PixelMap, width: number, height: number): Promise<PixelMap> {const opts: image.InitializationOptions = {size: { width, height },pixelFormat: image.PixelMapFormat.RGBA_8888,editable: true,alphaType: image.AlphaType.PREMUL};const cloned = await image.createPixelMap(new ArrayBuffer(width * height * 4), opts);new drawing.Canvas(cloned).drawImage(original, 0, 0);return cloned;}/*** 创建优化的分割路径(波浪或直线)*/private createOptimizedClipPath(width: number, grayHeight: number): drawing.Path {const path = new drawing.Path();// 直线分割模式(高性能)if (!this.enableWave) {path.addRect({left: 0,top: 0,right: width,bottom: grayHeight});return path;}// 波浪分割模式const amplitude = Math.min(25, grayHeight * VerticalProgressGrayscaleTransformation.WAVE_AMPLITUDE_RATIO);// 波浪太小时使用直线if (amplitude < 2) {path.addRect({left: 0,top: 0,right: width,bottom: grayHeight});return path;}// 构建波浪路径path.moveTo(0, 0);path.lineTo(width, 0);path.lineTo(width, grayHeight);// 优化的波浪计算const steps = VerticalProgressGrayscaleTransformation.WAVE_STEPS;const stepWidth = width / steps;const waveFreq = VerticalProgressGrayscaleTransformation.WAVE_FREQUENCY;let prevX = width;let prevY = grayHeight;for (let i = 1; i <= steps; i++) {const x = width - i * stepWidth;const wavePhase = (i / steps) * Math.PI * 2 * waveFreq;const y = grayHeight + amplitude * Math.sin(wavePhase);// 使用二次贝塞尔曲线优化连接const controlX = (prevX + x) * 0.5;const controlY = (prevY + y) * 0.5;path.quadTo(controlX, controlY, x, y);prevX = x;prevY = y;}path.lineTo(0, 0);path.close();return path;}getKey(): string {return `VerticalProgressGray_${this.grayRatio}_${this.enableWave ? 'wave' : 'line'}`;}
}

Android实现,使用Glide自定义transform实现

public class VerticalProgressGrayscaleTransformation extends BitmapTransformation {private final float grayRatio; // 灰度区域高度占比,取值范围 [0, 1]public VerticalProgressGrayscaleTransformation(float grayRatio) {this.grayRatio = grayRatio;}@Overrideprotected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {int width = toTransform.getWidth();int height = toTransform.getHeight();Bitmap.Config config = toTransform.getConfig() != null ? toTransform.getConfig() : Bitmap.Config.ARGB_8888;Bitmap bitmap = pool.get(width, height, config);Canvas canvas = new Canvas(bitmap);// 1. 首先绘制完整的原始彩色图像作为底层canvas.drawBitmap(toTransform, 0, 0, null);// 对 grayRatio 进行边界处理,确保在 [0, 1] 范围内float clampedGrayRatio = Math.max(0f, Math.min(1f, this.grayRatio));// 只有当灰度占比大于一个极小值时才应用灰度效果if (clampedGrayRatio > 0.001f) {Paint grayPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 添加抗锯齿ColorMatrix matrix = new ColorMatrix(new float[]{0.299f, 0.587f, 0.114f, 0, 0,0.299f, 0.587f, 0.114f, 0, 0,0.299f, 0.587f, 0.114f, 0, 0,0, 0, 0, 1, 0,});grayPaint.setColorFilter(new ColorMatrixColorFilter(matrix));// 波浪的基准Y坐标,即灰色区域的平均下边界int waveBaseY = (int) (height * clampedGrayRatio);Path grayRegionPath = new Path();grayRegionPath.moveTo(0, 0);      // 移动到左上角grayRegionPath.lineTo(width, 0);  // 画到右上角// 定义波浪的振幅float amplitude;if (clampedGrayRatio <= 0.001f || clampedGrayRatio >= 0.999f) {amplitude = 0f; // 如果完全着色或完全灰色,则没有波浪} else {float baseAmplitude = height * 0.03f; // 基础振幅为图片高度的3%// 确保振幅不会使波浪超出图片顶部或底部amplitude = Math.min(baseAmplitude, waveBaseY);amplitude = Math.min(amplitude, height - waveBaseY);}// 从右向左绘制波浪线作为灰色区域的下边界grayRegionPath.lineTo(width, waveBaseY); // 连接到右侧波浪基准点int numCycles = 3; // 波浪周期数float waveLength = (float) width / numCycles; // 每个周期的长度float currentX = width;for (int i = 0; i < numCycles * 2; i++) { // 每个周期包含一个波峰和波谷,共 numCycles * 2段float nextX = Math.max(0, currentX - waveLength / 2); // 下一个X点float controlX = (currentX + nextX) / 2; // 控制点X坐标// 控制点Y坐标,交替形成波峰和波谷// 从右往左画,i为偶数时是波峰(相对基准线向上,Y值减小),奇数时是波谷(Y值增大)float controlY = waveBaseY + ((i % 2 == 0) ? -amplitude : amplitude);grayRegionPath.quadTo(controlX, controlY, nextX, waveBaseY); // 二阶贝塞尔曲线currentX = nextX;if (currentX == 0) {break; // 到达左边界}}grayRegionPath.lineTo(0, waveBaseY); // 确保连接到左侧波浪基准点grayRegionPath.close(); // 闭合路径,连接回 (0,0)// 保存画布状态canvas.save();// 将画布裁剪为波浪路径定义的区域canvas.clipPath(grayRegionPath);//在裁剪区域内,使用灰度画笔再次绘制原始图像canvas.drawBitmap(toTransform, 0, 0, grayPaint);// 恢复画布状态canvas.restore();}return bitmap;}@Overridepublic void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {messageDigest.update(("VerticalProgressGrayscaleTransformation" + grayRatio).getBytes());}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/85013.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/85013.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux 中的自动化之systemd

linux | 自动化之systemd linux 中的自动化之systemd 学习总是循序渐进的。 linux 中程序的自动化&#xff0c;包括早期手动启动&#xff0c;查看启动后的日志、分析启动情况&#xff0c;再到后面封装脚本(大致要求启动后检查是否挂了&#xff0c;挂了拉起&#xff0c;没挂跳过…

【编译工具】CodeRider 2.0:驭码 CodeRider 2.0 全流程智能研发协作平台深度技术测评报告

目录 前言&#xff1a;CodeRider 2.0 简介 &#xff08;1&#xff09;核心功能 &#xff08;2&#xff09;适用场景 &#xff08;3&#xff09;优势 一、产品概述与技术架构 &#xff08;1&#xff09;产品定位与核心价值 &#xff08;2&#xff09;技术架构解析 &…

抓包 TCP 四次挥手报文

文章目录 抓包 TCP 三次握手报文一、第一次挥手二、第二次挥手三、第三次挥手四、第四次挥手 抓包 TCP 三次握手报文 抓包 TCP 三次握手报文 一、第一次挥手 二、第二次挥手 三、第三次挥手 四、第四次挥手

KMP(Kotlin Multiplatform)发布Web版本乱码

一、背景 最近用KMP尝试运行在Android、iOS、desktop都成功了&#xff0c;网络数据访问也正常。 可是当运行wasmJs的时候遇到了2个较大的问题。 中文字体出现乱码。 出现了跨域问题。 首先贴一下每个平台的运行截图&#xff1a; Android iOS Desktop 二、问题 当web跑起…

一个应用程序或移动网站项目提供最佳UI解决方案

一个应用程序或移动网站项目提供最佳UI解决方案 此套件是用大量的爱和辛勤工作制作的&#xff0c;为您的下一个应用程序或移动网站项目提供最佳解决方案。120个完全可编辑的界面&#xff0c;分为10个类别和2种文件格式&#xff0c;Photoshop和AI。简单易用的结构将允许您以所…

Android studio打包生成jar包文件

Android studio打包生成jar包文件 一 项目配置1.修改 app/build.gradle2.修改 AndroidManifest.xml 二 打 Jar 包1.修改 app/build.gradle2.编译生成 Jar 包 一 项目配置 1.修改 app/build.gradle 将com.android.application改成com.android.library注释掉applicationId 2.…

JAVA类加载机制(jdk8)

三句话总结JDK8的类加载机制&#xff1a; 类缓存&#xff1a;每个类加载器对他加载过的类都有一个缓存。双亲委派&#xff1a;向上委托查找&#xff0c;向下委托加载。沙箱保护机制&#xff1a;不允许应用程序加载JDK内部的系统类。 JDK8的类加载体系 类加载器的核心方法 //…

更进一步深入的研究ObRegisterCallBack

引入 我们如果想hook对象的回调,在上篇文章里我们已经知道了对象回调函数存在一个列表里面&#xff0c;我们通过dt可以看见&#xff0c;这里他是一个LIST_ENTRY结构&#xff0c;但是实际调用的时候&#xff0c;这样是行不通的&#xff0c;说明它结构不对 0: kd> dt _OBJEC…

Nginx-3 Nginx 的负载均衡策略

Nginx-3 Nginx 的负载均衡策略 Nginx 的负载均衡其实就是指将请求按照一定的策略转发给服务集群中的一台&#xff0c;提高了服务集群的可用性&#xff0c;解决数据流量过大、网络负荷过重的问题。 AKF 扩展立方体 分为 3 个方向负载&#xff1a; x 轴&#xff1a;增加实例数…

Wiiu平台RetroArch全能模拟器美化整合包v1.18

这款WiiU平台RetroArch全能模拟器美化整合包v1.18的亮点包括&#xff1a; 1. 18款平台完美兼容&#xff1a;无论你是喜欢NES时代的经典游戏&#xff0c;还是钟爱SNES、GBA等平台的大作&#xff0c;这款整合包都能满足你的需求&#xff0c;让你尽情畅玩游戏。 2. 三款自制主题&a…

MyBatis原理

Mybatis执行过程为&#xff1a;接口代理->sqlSession会话->executor执行器->JDBC操作 一、接口代理 Mybatis根据Mapper接口&#xff0c;动态生成相应实现类 二、SqlSession介绍 MyBatis核心对象SqlSession介绍 - MyBatis中文官网 三、Executor执行器介绍 精通My…

升级内核4.19-脚本

#bash cd /root yum remove -y kernel-tools-3.10.0-1160.el7.x86_64 yum remove -y kernel-tools-libs-3.10.0-1160.el7.x86_64tar -xvf rhel-7-amd64-rpms.tar.gz cd /root/rhel-7-amd64-rpms #安装依赖、包括socat&conntrack yum localinstall -y *.rpm --skip-broken#升…

全面理解 JVM 垃圾回收(GC)机制:原理、流程与实践

JVM 的 GC&#xff08;Garbage Collection&#xff09;机制是 Java 程序性能的关键支柱。本文将从堆内存布局、回收原理、GC 算法、流程细节、并发收集器机制等维度&#xff0c;系统讲清楚 GC 的底层运作原理和优化思路。 一、JVM 堆内存结构 Java 堆是 GC 管理的主要区域&am…

runas命令让其他用户以管理员权限运行程序

RUNAS 用法: RUNAS使用示例&#xff1a; runas /noprofile /user:mymachine\administrator cmd #本机Administrator管理员身份执行CMD&#xff0c;/noprofile为不加载该用户的配置信息。runas /profile /env /user:mydomain\admin “mmc %windir%\system32\dsa.msc” #本机上…

实战指南:部署MinerU多模态文档解析API与Dify深度集成(实现解析PDF/JPG/PNG)

MinerU web api部署 MinerU 能够将包含图片、公式、表格等元素的多模态 PDF、PPT、DOCX 等文档转化为易于分析的 Markdown 格式。 克隆 MinerU 的仓库 git clone https://github.com/opendatalab/MinerU.gitcd 到 projects/web-api cd projects/web-api在可以科学上网的情况下…

向量外积与秩1矩阵的关系

向量外积与秩1矩阵的关系 flyfish 向量外积是构造秩1矩阵的基本工具&#xff0c;其本质是用两组向量的线性组合刻画矩阵的行和列相关性&#xff1b;任意秩1矩阵必可表示为外积&#xff0c;而低秩矩阵&#xff08;秩 k k k&#xff09;可分解为 k k k 个外积矩阵的和&#x…

设计模式-创建型模式(详解)

创建型模式 单例模式 一个类只允许创建一个对象&#xff0c;称为单例。 单例体现&#xff1a;配置类、连接池、全局计数器、id生成器、日志对象。 懒汉式 (线程不安全) 单例&#xff1a;【不可用】 用到该单例对象的时候再创建。但存在很大问题&#xff0c;单线程下这段代…

什么是BI?有哪些应用场景

BI&#xff08;Business Intelligence&#xff0c;商业智能&#xff09;是通过技术手段对海量业务数据进行采集、整合、分析和可视化的过程&#xff0c;旨在帮助企业从数据中获取洞察&#xff0c;支持决策。其核心是通过工具&#xff08;如Quick BI&#xff09;将原始数据转化为…

从零开始:使用Vite和Vue.js搭建一个空项目

进入node.js官网 https://nodejs.org/zh-cn 下载node.js 点击进行安装&#xff0c; 完成之后&#xff0c;按住shift鼠标右键&#xff0c;打开powershell窗口 输入node -v &#xff0c;出现版本号就是成功了 node -v 接下来&#xff0c;打开设置&#xff0c;搜索开发者设置&…