好好的web页面,为什么嵌入到CEF3中就错位了?

15 篇文章 8 订阅

一、问题引入

        这些天在做一个windows客户端软件的二维码登陆需求,使用的是C++的框架+CEF3展示内容(二维码)的架构,其中CEF3仅仅用来展示web写的二维码。开发完成后,在测试和产品老师的电脑上居然二维码是错位的。实际效果如下:

(其中二维码所在的白色区域是一个CEF3嵌套web页面,周围的蓝色区域是C++框架)

图1是我做好的效果,给产品和测试老师运行的时候,居然成了图2和图3???同一个web页面,运行效果却不一样?看效果应该是图像进行了缩放?究竟是什么导致的呢?作为开发,我们怎么能够兼容不同的设备,统一显示效果呢?解决的思路又是什么样的呢? 我们一步一步的来解决这些问题。

二、找原因

上面有了结论,在某些计算机上是正常的,在有些计算机上是不正常的,并且缩放比例也不同。由于涉及到缩放,首先考虑的是屏幕分辨率的问题,但是设置完屏幕分辨率发现并没有什么变化,该正常的还是正常的,改有问题的还是有问题。这时候想到还有另外一个是与显示缩放有关的就是DPI。结果就发现了显示正常的计算机的缩放都是100%,也就是DPI都是96。但是显示非正常的计算机的缩放都不是100%,也就是DPI不为96。知道了由于这个变量的影响导致显示效果不同我们也就有了研究方向-DPI。

我们先来了解一下DPI,DPI, 全称是dots per inch, 也就是每英寸的点数,在显示器上就是每英寸的像素个数,Window上一般默认是96 dpi 作为100% 的缩放比率, 但是要注意的是该值未必是真正的显示器物理值, 只是Windows里我们的一个参考标准。

 下面我们一起探索一下为什么DPI设置高了之后, 我们看到的字体、图片等元素会变大(反之变小)? 以字体为例,因为系统字体是是以固定大小(宋体10号字,物理尺寸为(10/72)英寸)设计的, 当我们DPI设置高了之后 ,说明该字体要占有更多的像素, 在屏幕分辨率不变的前提下, 看起来也就大了。所以如果我们设置高DPI,通常也意味着我们的显示器是高分辨率, 里面的字体看起来太小了, 我们需要提高DPI来把内容放大。这也就解释了,为什么我们的web里面的内容(二维码)会随着DPI的不同,而放大或者缩小了。

我们既然已经找到web页面在CEF3中缩放的原因是由于DPI不同了,我们就针对DPI进行寻找解决方案。

三、解决方案

3.1 方案一:硬件设置

在当前电脑的桌面,右键-显示设置,把显示比例调整为100%(windows7及其以下系统需要重启计算机方可生效),这时再看,显示就正常了。设置步骤如下(以windows7 为例):

在桌面空白处,鼠标右键->屏幕分辨率->放大或缩小文本和其他项目->选择“较小,100%(默认)”

但是,我们不能强制要求每台计算机为了适应我们的软件都调整缩放为100%,即DPI=96。这样很不合理,也很不现实的!既然我们不能要求用户来做,那么就来找一下我们使用代码能够实现的方式。

3.2 方案二:软件支持高DPI

使软件C++部分支持高DPI,同时CEF也要支持高DPI。使用原生开发,界面库不改变,针对不同的分辨率设计对应的UI布局、字体、资源,程序创建窗口时根据不同分辨率选择对应的布局文件,这种方案缺陷是界面多套开发工作量、维护工作量加倍,适合界面不多的程序。

我们先来看一下CEF3怎么来支持高DPI的。首先我们会知道CEF3中有一个与高分辨率有关的方法CefEnableHighDPISupport。

但是CefEnableHighDPISupport()这个接口函数在使用时一般不为人所注意,但是如果稍有不慎,会造成打开的网页不能填满窗口的问题。如果是需要flash插件才能运行的游戏。则会出现打开游戏后,调整窗口大小时,发现游戏界面的大小并未随之改变,而且游戏里的点击位置却与界面显示的位置也不对应。我们 看一下接口它的定义:

// Call during process startup to enable High-DPI support on Windows 7 or newer.
// Older versions of Windows should be left DPI-unaware because they do not
// support DirectWrite and GDI fonts are kerned very badly.
///
/*--cef(capi_name=cef_enable_highdpi_support)--*/
void CefEnableHighDPISupport();

 翻译:windows7或者更高版本系统中,在进程启动时设置High-DPI为启用状态。而低版本的windows系统对DirectWrite和DPI支持不好(设置这个不会有什么作用)。除此之外还需要在命令行中进行设置:

command_line->AppendSwitchWithValue("--force-device-scale-factor","1");

如果设置了对高DPI的支持,但是打开网页时,没有设置相应的缩放比例,那么在win7以上版本里,很容易出现开头所说的问题。(如果要支持win7以下的版本,也不能使用这种方法。)解决这个问题也可以很简单,要么不要使用CefEnableHighDPISupport()这个接口函数,要么去设置网页的缩放比例。也就是我们接下来要说的第三种方式,设置网页缩放比例,达到显示正常的效果。

3.3 方案三:网页进行缩放

使用CEF3提供的方法setZoomLevel函数对web页面进行缩放,抵消因为高DPI产生的缩放。使web页面保持100%的原样,效果与缩放100%(DPI=96)是一样的。

综合上述三种方案,

第一种需要用户手动调整显示的DPI,肯定是不合适的;

第二种软件支持高DPI,需要界面多套开发工作量、维护工作量加倍,在界面较多的程序中也是不适用的。

第三种方案是目前对于客户端软件最具有实际意义的措施。我们就使用第三种方案来对我们的问题进行修复。

下面我们来一起探索一下,使用第三种方案的具体实施方式。

四、实施 

我们上面说了,我们决定使用CEF3提供的方法setZoomLevel函数对web页面进行缩放,抵消因为高DPI产生的缩放。我们先来看一下setZoomLevel函数是怎么定义的:

///
// Change the zoom level to the specified value. Specify 0.0 to reset the
// zoom level. If called on the UI thread the change will be applied
// immediately. Otherwise, the change will be applied asynchronously on the
// UI thread.
///
/*--cef()--*/
virtual void SetZoomLevel(double zoomLevel) = 0;

翻译:改变zoomLevel的值可以对页面执行缩放。当zoomLvel = 0,就是恢复页面原始大小(100%)。 执行渲染之前,在UI线程中,使用这个函数重置缩放比例,可以立即生效。

我们的思路是这样的,我们最终是要算出来屏幕缩放percent与CEF3对应的zoomLvel的关系。根据percent计算出来zoomLevel的值,然后通过CEF3提供的方式进行反向缩放(反缩放:当真实缩放值为scale,反向缩放就在真实缩放的基础上缩放-scale,对缩放值进行抵消)。具体实施的时候,我们可以先计算出来当前DPI系统的缩放比例percent;然后再将web页面进行缩放-percent。抵消因为DPI的缩放。这里就是cef提供的void setZoomLevel(double level) 函数,其中-8<= zoomLevel <=9进行设置。但是zoomlevel与缩放比percent不是同一个数值,而是有对应关系。zoomLevel是以每次20%的步长进行增长。也就是我们会从100%每次增长20%,就是会得到120%、144%...。100%对应的zoomLeve为0。

我们可以得到zoomLeve与屏幕真正的缩放比percent的转化公式,也就是:

1.2^zoomLevel = percent;

例如:

1.2^(-2.0) = 1.0/1.44 = 69.4%; //意思就是当屏幕实际缩放比例为原来的69.4%的时候,这时候的zoomLeve其实是-2.0。

1.2^2.0=1.44=144%;

1.2^1.0=1.2=120%

1.2^0=1.0 = 100%;//这是屏幕不进行缩放的时候,zoomLevel为0,不是为1。

这样我们就可以先使用其他方式计算出来屏幕的实际缩放比例percent。然后根据percent与zoomLvel的关系,通过高中学习的对数转化方式计算出来zoomLvel:

zoomLvel = log1.2(percent) = ln(percent) / ln(1.2)

再将setZoomLevel(-zoomLvel)进行界面的还原,达到修正二维码界面的偏移。

4.1 真实缩放比 percent

对于系统真实的缩放比percent的计算方法应该是这样的:

double Application::scalePercent()
{
#ifdef WIN32
    HDC hdc = GetDC(NULL);

    int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
    ReleaseDC(NULL, hdc);

    return dpiX / 96.0;
#else
    return 1.0;
#endif
}

其中:

1.GetDeviceCaps()为系统函数,可以获取一些关于设备的一些属性,如屏幕逻辑宽度(HORZSIZE)、水平的像素总数(HORZRES,就是平时所说的屏幕分辨率)、逻辑像素(LOGPIXELSX)等。这里我们用来获取屏幕横向(X轴)逻辑像素,这里说的像素其实是每英寸的像素数,非分辨率。也就是计算的DPI。

2. 96 为缩放100%时候的DPI。所以 当前的DPI / 96.0即为真正的缩放系数。也就是我们上面提到的的percent。

4.2 CEF3缩放比例 zoomLevel

根据我们上面推导的公式 zoomLvel = log1.2(percent) = ln(percent) / ln(1.2) 

    double zoomLevel = 1.0 * qLn(factor) / qLn(1.2);

所以,这是候我们应该使用-zoomLevel进行对CEF3进行反缩放,完整函数是:

void WebView::applyZoom()
{
    if (nullPtr == m_Browser) {
        return;
    }

    double percent = Application::scalePercent();
    double zoomLevel = 1.0 * qLn(percent) / qLn(1.2);

    CefRefPtr<CefBrowserHost> host = m_Browser->GetHost();
    host->SetZoomLevel(-zoomLevel);
}

至此,我们就已经将缩放的web页面进行了还原。我们:计算机的DPI,分别是低于96,还是 高于96,都得到了正确的显示效果:

五、总结

此次我们经历了从问题发现,原因查找,方案对比,方案实施,结果验证等一个完整的解决问题的周期。作为程序员我们绝对不能让用户手动操作来解决我们的问题,对于有多种方案的问题,我们一定要选取最优的,而不是最简单的。我们在解决CEF3中网页嵌套会出现错位的问题,进行了修复,不仅仅对于win7以上的系统适用,也要对win7以下的系统进行了兼容。最大限制的保证了用户的体验。

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值