背景
今日看到HarmonyOS论坛,有研发求助关于官方提供的一段缩放算法的问题.这个求助问题其实深究的话还是比较有意思的,比如,如何在HarmonyOS 系统上出设计稿?
先解决今天的求助问题,问题描述如下
求助问题
developer.huawei.com/consumer/cn...
问题描述: adaptDimension这个方法里面,为什么不使用display的height,而是自己通过scale算出一个虚拟高度呢?还有后面的designDim跟virtualDim为什么要这么计算?
问题1: 为什么要计算虚拟高度,即 virtualHeight
ini
static adaptDimension(value: number): number {
let deviceDisplay: display.Display = GlobalContext.getContext().getObject('display') as display.Display;
let widthScale = deviceDisplay.width / DESIGN_WIDTH;
let virtualHeight = widthScale * DESIGN_HEIGHT;
let designDim = Math.sqrt(DESIGN_WIDTH * DESIGN_WIDTH + DESIGN_HEIGHT * DESIGN_HEIGHT);
let virtualDim = Math.sqrt(deviceDisplay.width * deviceDisplay.width + virtualHeight * virtualHeight);
return virtualDim * value / designDim;
}
问题2: 输出值为什么采用 virtualDim * value / designDim
这个公式
答案核心
这是一个以宽为基准的等比例缩放算法
因此,才会出现adaptDimension中的所有代码逻辑
解析
假设设计稿的屏幕是60 x 120, 设计稿中的蓝色图标是 30 x 60, 那么我们想象一下,如果要等比例放大到真实屏幕(240 x 340),那么设计稿的60x120,应该变为多少?
有两种基准:1. 以屏幕宽度为基准 2. 以屏幕高度为基准
我们可以尝试推演一下两种基准
以屏幕宽度为基准
真实屏幕的分辨率为 240 x 340
那么设计稿以同等宽高比放大之后的高度应该为: (120 * 240) / 60 = 480
注意,这个高度比屏幕实际高度大
那么图标以宽占比放大之后的宽度应该为: (30 * 240) / 60 = 120
以屏幕高度为基准
真实屏幕的分辨率为 240 x 340
那么设计稿以同等宽高比放大之后的宽度应该为: (60 * 340) / 120 = 170
注意,这个高度比实际屏幕宽度小
那么图标以高占比放大之后的高度应该为: (60 * 340) / 120 = 170
两种基准的效果图
图中第一行示意图是采用"以屏幕宽为基准"的算法,第二行示意图是采用"以屏幕高度为基准"
基准算法实现
回看"问题1"中的算法实现,其采用的是"以屏幕宽为基准"的算法
ini
const DESIGN_WIDTH = 360;
const DESIGN_HEIGHT = 780;
...
static adaptDimension(value: number): number {
//获取分辨率,具体的获取代码是在下段代码中
let deviceDisplay: display.Display = GlobalContext.getContext().getObject('display') as display.Display;
//计算设计稿与设备的宽度缩放比例
let widthScale = deviceDisplay.width / DESIGN_WIDTH;
//通过宽度的缩放比例计算出,deviceDisplay.width宽度应该对应的高度
let virtualHeight = widthScale * DESIGN_HEIGHT;
//计算设计稿对角线长度
let designDim = Math.sqrt(DESIGN_WIDTH * DESIGN_WIDTH + DESIGN_HEIGHT * DESIGN_HEIGHT);
//计算设计稿对应的理想屏幕对角线长度
let virtualDim = Math.sqrt(deviceDisplay.width * deviceDisplay.width + virtualHeight * virtualHeight);
// 由于是采用宽高比,以宽为基准的计算方法,所以对于输入值来讲,输出值的计算公式为
// virtualDim / 理想值 = designDim / value
// 即:virtualDim*value / designDim
return virtualDim * value / designDim;
}
...
这个文件是Ability
async onWindowStageCreate(windowStage: window.WindowStage) {
//获取分辨率, 将获取到的分辨率存放在GlobalContext这个文件中的Map变量中
GlobalContext.getContext().setObject('display', await display.getDefaultDisplaySync());
......
}
接下来,再用"以屏幕高度为基准"来实现一遍
ini
adaptDimension2(value: number): number {
let deviceDisplay: display.Display = GlobalContext.getContext().getObject('display') as display.Display;
let deviceHeight = deviceDisplay.height
console.log('display:' + deviceDisplay.width + 'x' + deviceDisplay.height)
let heightScale = deviceHeight / DESIGN_HEIGHT;
let virtualWidth = heightScale * DESIGN_WIDTH;
let designDim = Math.sqrt(DESIGN_WIDTH * DESIGN_WIDTH + DESIGN_HEIGHT * DESIGN_HEIGHT);
let virtualDim = Math.sqrt(deviceHeight * deviceHeight + virtualWidth * virtualWidth);
return virtualDim * value / designDim;
}
质疑
"以屏幕宽度为基准" 这个通过对角线方式实现的等比例缩放算法是不是有点复杂了?
图标.宽 / 设计稿.宽 = 目标图标.宽 / 真实屏幕.宽
目标图标.宽 = 图标.宽 * 真实屏幕.宽 / 设计稿.宽
假设:设计稿宽 360,高780, 图标.宽 30
那么,目标图标.宽 = 30 * 真实屏幕.宽 / 360
真实屏幕.宽 可以通过 display.getDefaultDisplaySync()
API获取到
codelabs样例库
关于HarmonyOS实践教程,有一个Demo库, 在这个库中,可以找到和这个研发人员求助的所有函数使用样例代码。
- codelabs/AlarmClock【API 9】
- codelabs/AnimateRefresh【API 9】
- codelabs/TransitionAnimation【API 9】
- codelabs/HarmonyOS_NEXT/TransitionAnimation/【API 10】
- codelabs/HarmonyOS_NEXT/AnimateRefresh/【API 10】
结尾
关于适配,终极目标是要得到UED的还原认可,所以,应该采用哪种算法,最好了解清楚UED还原的过程。要不然容易陷入无休止的返工,甚至永远无法达到适配要求。