Chromium源码阅读:从页面加载到元素展示(1)

从<p>hello world</p>.html到界面上的hello world

今天,我们一起来看看一个html元素,是如何绘制到界面上。我们选择了最简单的场景,便于快速掌握总体的流程,加深之前阅读知识的印象。

准备环境

首先,我们保存这段html:

<html><body><p>Hello world</p></body>
</html>

接下来,debug浏览器content_shell跑起来,加载这个文件:
在这里插入图片描述

开始分析

接下来,我们开始下断点分析流程。

根据我们这些天对chromium的了解,可以盲猜一下,总体的流程应该是:

加载命令->读取文件 -> 解析html -> 创建p元素 -> 创建渲染Node ->
压入绘制序列 ==> 到渲染进程 执行绘制序列 -> 执行合成(cc) -> 上屏幕(viz)
复习一下这个神图:
在这里插入图片描述

好的,那么我们一个一个断点验证我们的猜想,把关键堆栈截图出来,看到相关的模块和关键代码吧!

读取文件

很容易找打相关类FileURLLoader,打上断点,确认是我们的文件,这个类放在content/browser/loader下面:
在这里插入图片描述
堆栈只能看到FileURLLoader这一层,说明代码存在异步,只能通过搜索,找到异步的地方,再下个断点:
在这里插入图片描述
这次断点看到了FileURLLoaderFactory这个类,但是由于跨了进程(mojo)于是跟随线索,找到URLLoaderFactoryStubDispatch这个类,打上断点,找到了调用方:
在这里插入图片描述
这个堆栈在Browser进程,虽然堆栈有点深,但可以简而言之(顺序从1到3):

  1. content.dll!content::NavigationRequest::BeginNavigation() [content\browser\renderer_host\navigation_request.cc]
  2. content.dll!content::NavigationURLLoaderImpl::MaybeStartLoader()
    [content\browser\loader\navigation_url_loader_impl.cc]
  3. network_cpp.dll!network::mojom::URLLoaderFactoryProxy::CreateLoaderAndStart()
    [out\x64_debug\gen\services\network\public\mojom\url_loader_factory.mojom.cc]

目前,我们主要在Browser进程和NetWork进程转悠,还没到Render进程。

解析html

读取到文件之后,需要Parser解析,我们也很容易找到Praser,根据目录,找最大的源文件,并打上断点:
在这里插入图片描述
在这里插入图片描述
这段堆栈简而言之(顺序从1到6执行):

  1. content.dll!content::RenderFrameImpl::CommitNavigation [content\renderer\render_frame_impl.cc]
  2. blink_core.dll!blink::WebLocalFrameImpl::CommitNavigation [third_party\blink\renderer\core\frame\web_local_frame_impl.cc]
  3. blink_core.dll!blink::FrameLoader::CommitDocumentLoader[third_party\blink\renderer\core\loader\frame_loader.cc]
  4. blink_core.dll!blink::DocumentLoader::CommitNavigation() [third_party\blink\renderer\core\loader\document_loader.cc]
  5. blink_core.dll!blink::Document::CreateParser() [third_party\blink\renderer\core\dom\document.cc]
  6. blink_core.dll!blink::HTMLTreeBuilder::HTMLTreeBuilder() [third_party\blink\renderer\core\html\parser\html_tree_builder.cc]

构造HTMLTree

既然到了HTMLTreeBuilder,那我们可以去Element元素类打个断点,看看构造HTMLItem:
第一次进来,是个开始的伪元素:
在这里插入图片描述
跳过几个元素,到了我们的P元素:
在这里插入图片描述
构造元素的过程也不复杂,可以简化为:

  1. blink_core.dll!blink::HTMLDocumentParser::ConstructTreeFromToken(blink::AtomicHTMLToken &) [G:\chromium\src\third_party\blink\renderer\core\html\parser\html_document_parser.cc]
  2. blink_core.dll!blink::HTMLTreeBuilder::ConstructTree(blink::AtomicHTMLToken *) [G:\chromium\src\third_party\blink\renderer\core\html\parser\html_tree_builder.cc]
  3. blink_core.dll!blink::CustomElement::CreateUncustomizedOrUndefinedElement(blink::Document &) [G:\chromium\src\third_party\blink\renderer\core\html\custom\custom_element.cc]
  4. blink_core.dll!blink::HTMLElementFactory::Create(const WTF::AtomicString &) [G:\chromium\src\out\x64_debug\gen\third_party\blink\renderer\core\html_element_factory.cc]
  5. blink_core.dll!blink::HTMLParagraphElement::HTMLParagraphElement(blink::Document &) [G:\chromium\src\third_party\blink\renderer\core\html\html_paragraph_element.cc]

HTMLParagraphElement 就是我们的

元素。
HTMLParagraphElement 类不可谓不简单:
在这里插入图片描述
HTMLParagraphElement 这个类直接出现的地方也很少:
在这里插入图片描述

另外,有趣的是,HTMLElementFactory构造HTMLElement的时候,简单粗暴,就是查表:
在这里插入图片描述
另外,在看HtmlElement源码时发现了一个FastGetAttribute的函数,以为有什么黑魔法可以Fast,跟进去发现竟然是个遍历,看来此Fast非彼Fast。
在这里插入图片描述

接下来,该布局了Layout

背景知识:

1.块级元素
1.1、特征
霸占一行,不能与其他任何元素并列。
宽高设置有效,那么宽度将默认变为父级的百分百。
1.2、常用元素中那些属于块级元素
<h1>~<h6>,<p>,<div>,<ul>,<ol>,<li>等,所有的容器级标签,都是块级元素
2.行内元素
2.1、特征
设置宽高无效,能与其他行内元素并排,宽高由内容撑开。
水平方向上的padding和margin可以设置,垂直方向上的无效。
不会自动进行换行
2.2、常用元素中那些属于行内元素
如:p , span , a , b , i , u , em 等,所有的文本级标签,都是行内元素 ps: p元素比较特殊,在P元素里面只能放文字和图片和表单元素,p里面不能放h和ul,也不能放p。所有P元素属于行内元素,也属于行内块元素
3.行内块元素
3.1、特征
不自动换行,能够识别宽高
默认排列方式为从左到右
3.2、常用元素中那些属于行内块元素
img,input
4.三种元素互相转换
使用display属性可以转换标签的元素级别,其属性有三种设置方式如下:
block 可以把行内元素转换成块级元素
inline 把块级元素准换成行内元素
inline-block 转换成行内块元素

我们这里的p元素,在 HTML 中,

元素(段落)是一个块级元素(Block-level Element)。
于是,我们去找对应的LayoutNode:
在这里插入图片描述

在BlockNode下断点:
在这里插入图片描述
布局部分的主要流程如下:

  1. cc.dll!cc::ProxyMain::BeginMainFrame(s)[cc\trees\proxy_main.cc]
  2. cc.dll!cc::LayerTreeHost::RequestMainFrameUpdate(bool) [cc\trees\layer_tree_host.cc]
  3. blink_platform.dll!blink::LayerTreeView::UpdateLayerTreeHost()
    [third_party\blink\renderer\platform\widget\compositing\layer_tree_view.cc]
  4. blink_core.dll!blink::LocalFrameView::UpdateLayout() [third_party\blink\renderer\core\frame\local_frame_view.cc]
  5. blink_core.dll!blink::LayoutView::LayoutRoot() [third_party\blink\renderer\core\layout\layout_view.cc]
  6. blink_core.dll!blink::BlockNode::Layout(const blink::ConstraintSpace &)
    [third_party\blink\renderer\core\layout\block_node.cc]
  7. blink_core.dll!blink::BlockLayoutAlgorithm::Layout() [third_party\blink\renderer\core\layout\block_layout_algorithm.cc]

布局后,会拿到一个LayoutObject组成的树,其根是LayoutView:

LayoutView 类在类似 Blink(Chromium 项目的一部分)这样的 Web渲染引擎中是一个基础概念,:

  1. 布局树的根LayoutView 是布局树的根对象。在 Web 文档中,布局树表示所有视觉元素的空间关系和尺寸。文档中需要布局的每个元素都会有一个关联的 LayoutObject,而
    LayoutView 是最顶层的 LayoutObject

  2. 初始包含块(ICB):在 CSS 中,初始包含块是包含整个文档布局的矩形。它是布局中所有盒子的最终祖先,其尺寸等同于视口大小。LayoutView
    表示这个概念,并作为相对视口定位的元素(例如,固定或绝对定位的元素)的包含块。

  3. 与布局视口尺寸匹配LayoutView 的尺寸与布局视口的尺寸相匹配,布局视口是浏览器用来计算页面上元素大小的区域。布局视口对于响应式设计非常重要,用于确定不同屏幕尺寸上内容的布局方式。

  4. 相对于文档的位置为 (0,0)LayoutView 相对于文档的位置是坐标 (0,0)。这意味着它总是位于文档的左上角,但如果页面滚动了,它不一定在视口中可见。

  5. 树的共享成员:由于每个根布局树(或浏览器中的每个 Frame)有一个 LayoutView,这个类包括跨布局树共享的成员。例如,m_layoutState 可能是一个保存布局进程当前状态的成员,而
    m_layoutQuoteHead 可能与布局中引用的管理相关。

  6. 从 LayoutNGBlockFlow 继承LayoutView 类继承自 LayoutNGBlockFlow,这表明它是 Blink 的 LayoutNG(下一代布局)架构的一部分。LayoutNG
    旨在提高布局性能和精度,与传统的布局系统相比有所改进。作为一个块流(block flow),它还表明 LayoutView
    参与块格式化上下文布局操作(如定位块级元素和处理内容流)。

随着代码的阅读,笔者发现LocalFrameView、Document等实例都是LocalFrameView的类成员。可见,LocalFrameView代表了一个页面可视化的顶层逻辑。为此,笔者进一步了解了其他页面的顶层逻辑类的情况:

在 Blink 项目中,WebFrameWidgetLocalFrameLocalFrameView 是 Web渲染流程中的关键组件:

  1. LocalFrame:

    • LocalFrame 是 Blink 中的一个类,代表了一个框架(Frame),在 HTML 中通常对应一个 <iframe> 元素或顶级窗口。每个框架都有自己的文档和 DOM 树。
    • LocalFrame 负责处理输入事件、执行 JavaScript、处理导航请求、以及管理布局和绘制流程等。
    • 它是 Web 渲染的核心部分,负责处理和维护 Web 页面的内容和状态。
  2. LocalFrameView:

    • LocalFrameView 是与 LocalFrame 相关联的视图,代表了框架内容的可视化表现。它是一个布局树的根,负责计算和存储框架中所有元素的布局信息。
    • LocalFrameView 处理布局更新、绘制操作,以及视口的滚动和缩放等。
    • 它也是处理重绘和重流(reflow)的关键组件,即在 DOM 更新或样式变化时更新元素的布局和视觉表现。
  3. WebFrameWidget:

    • WebFrameWidget 是 Blink 对外提供的接口,它是一个与特定 LocalFrame 相关联的小部件(widget),通常是顶级框架或者有视觉表现的子框架(如有自己的层叠上下文的 <iframe>)。
    • 它负责框架的 UI 表现,如绘制、处理输入事件、以及与视图大小和缩放有关的操作。
    • WebFrameWidget 主要用于顶层框架和非顶层但需要独立输入处理的框架,是连接渲染引擎和浏览器 UI 之间的桥梁。

简而言之,LocalFrame 管理框架的逻辑和状态,LocalFrameView 管理框架的布局和视觉表现,而WebFrameWidget 是框架与外部交互的界面,负责处理输入和绘制。这三者共同工作,以确保 Web页面能够正确渲染并与用户交互。在实际的页面渲染过程中,它们各自承担不同的职责,但又相互依赖,共同构成了 Blink 渲染引擎的核心部分。

更进一步,笔者梳理出了Blink顶层逻辑的类,并放到一起综合列出:

在 Blink 渲染引擎中,各个类的功能和作用可以按照它们在渲染流程中的职责进行分类。以下是对这些类的分类和详细说明:

页面结构和框架管理

  1. Page

    • 代表整个浏览器窗口中的一个完整页面。
    • 协调属于同一页面的所有框架(LocalFrameRemoteFrame)。
    • 管理与页面生命周期相关的功能,视觉更新,全局事件处理等。
  2. FrameTree

    • 描述框架层级结构,管理页面中所有框架的树状结构。
    • 允许遍历和管理框架之间的父子关系。

框架表示

  1. LocalFrame

    • 代表一个框架,可以是顶级窗口或嵌套的 <iframe>
    • 负责处理输入事件、执行 JavaScript、处理导航请求、管理布局和绘制流程。
  2. RemoteFrame

    • 代表一个远程框架,内容在另一个进程中渲染。当使用跨域 iframe 或者分布式多进程架构(如 Chrome 的多进程架构)时,RemoteFrame 代表了一个远程框架。与 LocalFrame 相比,RemoteFrame 中的内容实际上是在另一个进程中渲染的。RemoteFrame 与 LocalFrame 并行,提供了一种机制来同步跨进程的框架状态和事件。
    • 主要用于同步跨进程框架LocalFrame的状态和事件。

文档和 DOM

  1. Document
    • 代表 LocalFrame 中的文档对象模型(DOM)。
    • 包含页面内容和结构,提供访问和修改 DOM 树的 API。

视图和渲染

  1. LocalFrameView

    • LocalFrame 相关联的视图,代表框架内容的可视化表现。
    • 处理布局更新、绘制操作,以及视口的滚动和缩放。
  2. RenderView

    • 渲染进程中的组件,协调一个 Page 的渲染过程。
    • WebView 对象相关联,负责显示整个页面的内容。

用户界面和交互

  1. WebFrameWidget

    • 与特定 LocalFrame 相关联的小部件(widget),通常用于顶级框架或有视觉表现的子框架。
    • 负责框架的 UI 表现,包括绘制、处理输入事件、以及与视图大小和缩放有关的操作。
  2. WebView

    • 浏览器 UI 层使用的接口,用于在浏览器中显示和操作整个页面的内容。
    • RenderView 协作,提供页面级别的渲染和交互。

以上类别组件通过协作,共同完成了从文档加载、DOM树构建、布局计算到最终的页面渲染和交互的整个过程。每个类都在渲染和显示 Web 页面的过程中扮演着特定的角色。

构造RenderTree

根据我们之前的阅读笔记,ElementTree构建出来之后,如果想要渲染出来,那么需要构造RenderTree。刚刚提到RenderView,那相关逻辑也肯定在那里。我们先看看RenderView是怎么创建出来的:

创建RenderView

在这里插入图片描述
跨进程了,再到另一个进程跟踪流程:
在这里插入图片描述

这两张截图的流程粗略为:

在Browser进程:

  1. content_shell.exe!content::Shell::LoadURL(const GURL &) [v8\samples\shell.cc]
  2. content.dll!content::FrameTreeNode::TakeNavigationRequest() [content\browser\renderer_host\frame_tree_node.cc]
  3. content.dll!content::RenderFrameHostManager::CreateSpeculativeRenderFrameHost()
    [content\browser\renderer_host\render_frame_host_manager.cc]
  4. content.dll!content::RenderFrameHostManager::CreateRenderFrameProxy()
    [content\browser\renderer_host\render_frame_host_manager.cc]
  5. content.dll!content::RenderViewHostImpl::CreateRenderView() [content\browser\renderer_host\render_view_host_impl.cc]
  6. content.dll!content::AgentSchedulingGroupHost::CreateView() [content\browser\renderer_host\agent_scheduling_group_host.cc]

在Render进程:
(通过mojo::AssociatedRemotemojom::AgentSchedulingGroup mojo_remote_到Render进程:)

  1. content.dll!content::AgentSchedulingGroup::CreateWebView() [content\renderer\agent_scheduling_group.cc]
  2. blink_core.dll!blink::WebView::Create(bool) [third_party\blink\renderer\core\exported\web_view_impl.cc]

在阅读RenderView的相关代码时,会遇到很多相似的类名的类,这里也一并梳理一下:

  1. RenderView

    • 如前所述,RenderView 是负责协调页面渲染的组件。它存在于渲染进程中,并与 WebView 相关联,负责管理一个 Page 的布局和绘制。
  2. RenderViewImpl

    • RenderViewImplRenderView 的具体实现类。它具体实现了渲染视图的功能,如处理页面的布局、绘制和事件等。
  3. RenderViewHost

    • RenderViewHost 存在于浏览器进程中,是渲染进程中 RenderView 的代理(或对应物)。它负责与渲染进程中的 RenderView 进行通信,发送命令和接收事件。
  4. RenderViewHostImpl

    • RenderViewHostImplRenderViewHost 的具体实现。它实现了与渲染视图交互所需的逻辑,并管理着渲染进程和浏览器进程之间的通信。
  5. RenderViewHostFactory

    • RenderViewHostFactory 是一个工厂类,用于创建 RenderViewHost 的实例。它允许在测试或其他特殊场景中替换或自定义 RenderViewHost 的创建过程。
  6. RenderViewDelegate

    • RenderViewDelegate 是一个接口,定义了浏览器进程期望从 RenderView 接收的事件和命令。RenderView 的实现类(如 RenderViewImpl)将通过这个接口与浏览器进程进行交互。
  7. RenderViewImplScaleFactor

    • 这个类是特定于缩放功能的实现,负责处理与 RenderViewImpl 相关的缩放因子调整。

理解这些类的最佳方式是将它们分为两个主要类别:渲染进程中的实现类(如 RenderViewImpl)和浏览器进程中的管理类(如
RenderViewHostRenderViewHostImpl)。渲染进程中的类直接负责页面的渲染工作,而浏览器进程中的类则负责管理、协调和与渲染进程通信。

在实际的代码架构中,这些类之间的交互可能会涉及到复杂的多进程通信,但它们的基本职责还是围绕着页面的渲染和浏览器进程与渲染进程之间的通信。因此,当尝试理解这些类时,关注其所属的进程(渲染进程还是浏览器进程)以及它们在渲染和通信流程中的角色会有所帮助。

《未完待续 … 》

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

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

相关文章

yolo模型评估指标相关概念

mAP&#xff08;mean Average Precision 平均精度&#xff09;&#xff0c;是一个经常被用来衡量目标 检测模型是否具有较好性能的指标。 计算mAP需要先计算每一类物体的AP&#xff0c;然后取平均值&#xff0c;所以下面介绍的 TP,FP,FN,TN都是针对某一类物体&#xff08;目标&…

Java课程设计:基于Javaweb的超市商品管理系统

文章目录 一、项目介绍二、项目展示三、源码展示四、源码获取 一、项目介绍 管理员用户&#xff1a;需要能够添加商品类型以及商品&#xff0c;能够对商品进行管理&#xff0c;能够查询用户信息&#xff0c;能够查询出售记录&#xff1b;普通用户&#xff1a;需要能够搜索商品…

搜维尔科技:【应用】人形机器人将成为引领产业新浪潮的尖兵

特斯拉纷纷发表人形机器人计划&#xff0c;预示这项先进科技将成为下一个颠覆性的殖民地。人形机器人被视为继电脑、智能手机和电车之后,又一个将改变世界的创新产品。 全球人口结构正在快速老化&#xff0c;至2050年60岁以上人口将达22%,是现今的两倍。劳动人口短缺迫在眉睫&…

NewspaceAi之GPT使用新体验

GPT功能 使用地址&#xff1a;https://newspace.ai0.cn/ 上车 挂挡 踩油门&#xff0c;一脚到底&#xff0c;开始你的表演 问题1&#xff1a;你能做什么详细告诉我&#xff1f; 下面内容是GPT的回答 当然&#xff01;作为一个基于GPT-4架构的AI&#xff0c;我能够在许多方面为…

关于FPGA对 DDR4 (MT40A256M16)的读写控制 2

关于FPGA对 DDR4 &#xff08;MT40A256M16&#xff09;的读写控制 2 语言 &#xff1a;Verilg HDL EDA工具&#xff1a;ISE、Vivado、Quartus II 关于FPGA对 DDR4 &#xff08;MT40A256M16&#xff09;的读写控制 2一、引言二、DDR4的简介四、DDR4 SDRAM状态框图 关键词&#x…

【Hachker News】如果你不需要钱,你会干什么?

Hachker News上的一个问题&#xff0c;标题是“如果你不需要钱&#xff0c;你会做什么&#xff1f;” 回答摘要 问题链接&#xff1a;What would you spend your time working on if you didn’t need money? A1&#xff1a; 我会把时间投入到城市周围的农村地区&#xff0c…

Matlab使用Simulink仿真实现AM和BPSK信号的解调

前言 本篇实现了基于AM和BPSK调制的通信系统&#xff0c;采用Bernoulli Binary Generator生成随机二元序列&#xff0c;码元速率为0.5秒/个。AM调制使用Sine Wave模块生成载波&#xff0c;频率40Hz&#xff0c;相位π/2。BPSK调制通过Switch模块切换相位0和π的载波。信号传输…

【java计算机专业毕设】房屋租赁系统代码源码MySQL springboot vue html maven送文档ppt

1项目功能 【java计算机专业毕设】房屋租赁系统javaweb MySQL springboot vue html maven 送报告 2项目介绍 系统功能&#xff1a; 房屋租赁系统包括管理员和用户和房东三种角色。 该系统包含多个功能模块&#xff0c;分别为管理员、用户和房东提供服务。管理员功能包括个人中…

20.2 JSON-JSON解码、映射数据类型、处理JSON响应

1. JSON解码 JSON解码&#xff0c;即将JSON格式在字符串转换为Go语言数据类型的变量。 函数Unmarshal接受一个JSON字节切片和一个指定目标格式的接口。而这个借口即与JSON字符串中的结果相匹配的结构体类型的变量。 定义结构体类型 type Person struct { ... }创建结构体变量…

聚鼎科技:现在的装饰画做起来难吗

在当代&#xff0c;装饰画作为一种体现个人品味和审美情趣的方式&#xff0c;已经广泛应用于各种室内空间。不少人会产生这样的疑问&#xff1a;在现代化技术和材料的支持下&#xff0c;制作一幅装饰画是变得容易了&#xff0c;还是依旧充满挑战? 现代科技的确为装饰画的制作带…

微信小程序查分易如何使用?

期末马上到了&#xff0c;老师们又开始为发放成绩而头疼了&#xff0c;堆积如山的试卷&#xff0c;密密麻麻的分数&#xff0c;还有那些不断响起的家长电话&#xff0c;真是让人心烦。别担心&#xff0c;今天就让我来介绍一个让老师“偷懒”神器——查分易微信小程序 第一步&am…

ROS中Twist消息类型

Twist消息类型在Robot Operating System (ROS)中是一个常见的数据结构&#xff0c;主要用于描述物体的线性速度和角速度。这种消息类型在ROS的geometry_msgs包中定义&#xff0c;常用于机器人运动控制&#xff0c;尤其是当需要向机器人发布速度指令时。 Twist消息由两个Vector…

6月14日 Qtday2

#include "widget.h" #include "ui_widget.h" #include <QTimer> using namespace std; Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget), lab1(new QLabel(this)) //初始化一个标签显示登录状态 {//设置华清远见的标签图…

再谈 dijkstra 算法和最短路径问题

前置文章&#xff1a; dijkstra 算法为什么高效 有向图的负权值边与建模 求单源最短路径的新方法 前天晚上实现了一个基于 dijkstra 算法的求单源最短路径的新算法&#xff0c;整理了一篇文章。我非常不愿意把一些直观的问题太过于技术化&#xff0c;但多年的职业经历偏偏让一…

C#——静态成员和非静态成员详情

静态成员和非静态成员 调用: 静态属性(static) : 类名.属性名调用 非静态属性(没static) : 1.先创建对象 2.对象.属性 特点: 静态方法里面只能访问静态成员 非经态方法中可以访问所有的属性 static数据成员在类的内部声明&#xff0c;但只能在类的外部定义&#xff0c;…

【软件安装9】OpenCV多版本安装Ubuntu18.04

文章目录 一、查看已安装的Opencv版本二、安装新版本三、多版本OpenCV切换 OpenCV 官网 在此 一、查看已安装的Opencv版本 查看已安装opencv的版本 pkg-config opencv --modversion官网下载对应的版本&#xff0c;并解压 opencv3.4.3 二、安装新版本 进入前置准备里下载…

24年法考报名照片千万别乱拍,否则卡审

法考报名照片每年都有很多被卡审&#x1f62d; 常见的问题是 ①照片比例不对&#xff0c;无法上传&#xff0c;人像比例要求非常严格 ②照片像素错误&#xff0c;不能直接拿大图压缩图片&#xff0c;需要做出413*626像素的法考证件照 ③照片文件偏大&#xff0c;照片要求40-100…

单触控单输出触摸开关芯片PT2052A

1.产品概述 PT2052封装和丝印 PT2052A 是一款单通道触摸检测芯片。该芯片内建稳压电路&#xff0c;提供稳定电压给触摸感应电路使用&#xff0c;同时内部集成高效完善的触摸检测算法&#xff0c;使得芯片具有稳定的触摸检测效果。该芯片专为取代传统按键而设计&#xff0c;具有…

【精品方案】离散型制造行业智能工厂标准解决方案(49页 PPT)

引言&#xff1a;随着科技的不断进步和制造业的转型升级&#xff0c;离散型制造行业正面临着从传统制造向智能制造转型的迫切需求。离散型制造行业涉及的产品种类繁多&#xff0c;生产流程复杂&#xff0c;对生产效率、产品质量和成本控制有着极高的要求。因此&#xff0c;开发…

SQL中的UPDATE语句:别让你的数据“离家出走”

sql的update操作正式环境用的很少&#xff0c;但是在测试环境还是用的挺多的。 想象一下&#xff0c;你正在管理一个学校的数据库&#xff0c;其中有一个students表&#xff0c;记录着每个学生的信息。有一天&#xff0c;你接到通知说某个学生的年龄或成绩需要更新。这时&…