QWebEngineView 实现网页触屏滑动

问题描述:

最近遇到一个很棘手的bug,最初做需求的时候没考虑到通用,认为前端不会经常改架构,就写死了只对某个标签设置scrollbar的值,导致后面前端html架构改了之后,找不到相应的标签,触屏滑动失效。
先说环境和需求:

SUSE linux, qt5.15.0(内置Chrome浏览器内核), Elo触屏显示器。
用QWebEngineView加载网页,希望做到像在手机上一样,通过手指上下滑动达到屏幕滚动效果

最开始做的时候,前端把滚动条放在IFRAME中,通过document.activeElement可以得到IFRAME,所以,只需要识别到IFRAME 标签,然后调用document.activeElement.contentWindow.scrollBy(0,yOffet)就能实现屏幕滚动。
现在前端修改了html架构, 把IFRAME标签去掉了,这就导致当初的逻辑完全失效,屏幕无法滚动。

问题分析

由于Chrome浏览器的特殊性,无论点击网页什么位置(除了输入框之类),document.activeElement.tagName始终返回BODY,无法得到BODY内部实际active的元素。Google各种资料,发现可以通过event获取当前事件的target。当前event事件的target可能是一个TD,DIV或者其他自定义标签,滚动条一般在其父节点上。尝试通过当前事件的target往上依次查找其父节点,直到得到有滚动条的元素。js脚本如下:

       QString load = QStringLiteral(
            " function f(evt) {" \
                "if(evt && evt.target) { " \
                    "var element = evt.target;" \
                    "while(element)" \
                    "{"
                         "element = element.parentElement;" \  //依次得到父节点
                         "if(element)" \
                         "{" \ //判断当前元素是否带滚动条
                               "element.scrollBy(0,2);"
                               "if(element.scrollTop > 0)"
                                "{"
                                      "element.scrollBy(0,-2);"
                                      "window.scrollElt = element;" //保存在一个全局变量里,以便在后面设置滚动条位置。
                                      "break; "
                                "}"
                                "else {"
                                 "element.scrollTo(0,0);}"
                         "}" \
                    "}" \

                "}" \
            "}" \

            " function f2(evt) {" \
                "window.scrollElt = null;" \
            "}" \

            "document.addEventListener('mousedown',f,false);" \
            "document.addEventListener('mouseup',f2,false);" \
        );

有了带滚动条的元素对象,就能在Qt代码中的mouseMoveEvent() 中通过j调用脚本执行window.scrollElt.scrollBy()来设置滚动条位置了。

尝试过程:

  1. 原代码调用window.scrollBy()不起作用,查资料发现通过window执行的是全局的滚动条,如果滚动条刚好设在全局,会起作用,反之,如果滚动条设在了body内部的某个或者某些标签上,就不会起作用。
  2. 最开始单纯地通过在html中找到当前这个带滚动条的tag,然后调用它的scrollBy方法document.querySelector(‘.table-scroll’).scrollBy(0,%1); 或者 document.getElementById(‘table-scrollTo’).scrollBy(0,%1),发现只对当前这个html页面起作用,其他页面因为没有指定的标签Id,所以无效。
  3. 通过暴力地遍历整个html页面的div标签,然后通过visibility overflow-y scrollHeight clientHeight这些参数过滤出带滚动条的div。先不说性能,对于个别网页上看上去起作用,但是会发现,无论在哪个位置拖动鼠标,屏幕都会滚动,很明显,是因为没有定位到鼠标mousedown位置的元素。另外,有些div元素(或者其他元素)不能通过这几个条件判断出是否scrollbar,原因未知。所以这种思路也不行。
                QString frameScrollCode = QStringLiteral("function frameScroll(){" \
                     "var divList = document.getElementsByTagName('div');" \
                     "var div;" \
                     "for(var index=0; index < divList.length; index++) {" \
                         "var bVisible = window.getComputedStyle(divList[index]).getPropertyValue('visibility');" \
                         "var isScroll =window.getComputedStyle(divList[index]).getPropertyValue('overflow-y');" \
                         "var height = window.getComputedStyle(divList[index]).getPropertyValue('height');" \
                         "var sHeight = divList[index].scrollHeight;" \
                         "var cHeight = divList[index].clientHeight;" \

                         "if((bVisible == 'visible') && (isScroll == 'auto' || isScroll=='scroll') && (sHeight>cHeight))" \
                          "{" \
                                "alert(divList[index]);" \
                                "divList[index].scrollBy(0,%1);" \
                                " break; " \
                          "}" \
                     "}" \

                "}" \
                "frameScroll();" \
                ).arg(iGapy);
  1. stackoverflow上有个方法,通过监听焦点事件,修改window.activeElement, 发现不起作用,focus和blur事件捕捉不到。但是发现‘mousedown’ 和‘mouseup’ 事件可以捕捉到,让问题有了新的转机。
function _dom_trackActiveElement(evt) {
    if (evt && evt.target) { 
        document.activeElement = evt.target == document ? null : evt.target;
    }
}

function _dom_trackActiveElementLost(evt) { 
    document.activeElement = null;
}

if (!document.activeElement) {
    document.addEventListener("focus",_dom_trackActiveElement,true);
    document.addEventListener("blur",_dom_trackActiveElementLost,true);
}
  1. 尝试了各种方法判断某个元素是否有滚动条均失败(不能cover到所有的case)。最后通过其他项目组做前端的同事建议,找到一种非常规的方法:https://www.feiniaomy.com/post/986.html, 先移动一下位置,再判断是否有偏移,如果偏移了,说明有滚动条,反之,没有。
 	 "element.scrollBy(0,2);"
       "if(element.scrollTop > 0)"
        "{"
              "element.scrollBy(0,-2);"
              "window.scrollElt = element;" //保存在一个全局变量里,以便在后面设置滚动条位置。
              "break; "
        "}"
        "else {"
         "element.scrollTo(0,0);}"

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在C语言中实现触摸屏滑动切换图片,你需要使用相应的图形库和触摸屏库。这里以SDL图形库为例,介绍一个简单的实现方法: 1. 安装SDL库:请确保已经安装了SDL库。你可以从SDL官方网站(https://www.libsdl.org/)下载并安装适合你操作系统的版本。 2. 初始化SDL库:在程序的开头,你需要初始化SDL库。 ```c #include <SDL2/SDL.h> int main() { // 初始化SDL库 SDL_Init(SDL_INIT_VIDEO); // 创建窗口和渲染器等操作 // 事件循环处理触摸事件和绘制 // 清理资源并退出SDL库 SDL_Quit(); return 0; } ``` 3. 创建窗口和渲染器:在初始化SDL库后,你需要创建一个窗口和渲染器。 ```c SDL_Window* window; SDL_Renderer* renderer; // 创建窗口 window = SDL_CreateWindow("Touch Screen Image Slider", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screenWidth, screenHeight, 0); // 创建渲染器 renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); ``` 4. 加载图片:你需要加载要显示的图片。 ```c SDL_Surface* imageSurface; SDL_Texture* imageTexture; // 加载图片到表面 imageSurface = SDL_LoadBMP("image.bmp"); // 将表面转换为纹理 imageTexture = SDL_CreateTextureFromSurface(renderer, imageSurface); // 释放表面资源 SDL_FreeSurface(imageSurface); ``` 5. 事件循环处理触摸事件和绘制:在程序的主循环中,你可以使用SDL的事件处理机制来检测触摸事件,并根据滑动的方向进行图片的切换。 ```c SDL_Event event; int currentImageIndex = 0; // 主循环 while (1) { // 处理事件 while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: // 处理退出事件 break; case SDL_FINGERDOWN: // 处理触摸开始事件 break; case SDL_FINGERMOTION: // 处理触摸移动事件 break; case SDL_FINGERUP: // 处理触摸结束事件 break; } } // 清空渲染器 SDL_RenderClear(renderer); // 绘制当前图片 SDL_RenderCopy(renderer, imageTexture, NULL, NULL); // 更新屏幕显示 SDL_RenderPresent(renderer); } ``` 在事件处理的代码中,你可以根据触摸事件的坐标和方向来切换图片。你可以使用一个图片列表来存储要显示的图片路径,并在切换时更新`currentImageIndex`变量。 这只是一个简单的示例,你可以根据实际需求进行扩展和修改。记得在程序结束时释放资源并退出SDL库。 希望这个简单的示例能帮到你!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值