OpenGL-贴纸方案
OpenGL-贴纸方案
普通贴纸(缩放、Z轴旋转、平移)
OpenGL环境说明
OpenGL渲染区域使用正交投影换算,正常OpenGL坐标是vertexData
,这样的 Matrix.orthoM
进行换算
//顶点坐标(原点为显示区域中心店)private final float[] vertexData = {-1.0f, -1.0f, //左下角1.0f, -1.0f, //右下角-1.0f, 1.0f, //左上角1.0f, 1.0f, //右上角};
m_width=720;
m_height=1280Matrix.orthoM(matrix, 0, 0, m_width, m_height,0 , -1, 1);//坐标原点对应屏幕左上角private final float[] m_position = {0f, 0f,//左上角坐标720f,0f,///右上角坐标0f, 1280f, //左下角坐标720f, 1280f,//右下角坐标};
以上面的基础的渲染区域设置FBO的绘制区,进行贴纸绘制,对应的缩放、旋转、平移时候对矩阵的处理和顶点点位处理的方法说明m_position
对应的是贴纸的坐标,m_materialRect
对应的xy轴的坐标和宽高。
float[] m_materialRect={0f,0f,500f,500f};//x,y,w,h//坐标原点对应屏幕左上角private final float[] m_position = {0f, 0f,//左上角坐标500f, 0f,//右上角坐标0f, 500f, //左下角坐标500f, 500f,//右下角坐标};void processCommonRect() {if (m_locateType == Locate_Common_Rect && m_position) {float x = m_materialRect.origin.x * m_width;float y = m_materialRect.origin.y * m_height;Rect rect(x, y, m_materialRect.size.width, m_materialRect.size.height);m_matrix->identity();if (m_scale != 1.0f || (m_angle != 0.f && m_angle != 360.f)) {m_matrix->scale(m_scale);m_matrix->rotateZ(m_angle);// 纹理在屏幕上旋转,x 和 y 需要计算屏幕的比例,防止拉伸float screenRadio = m_height / m_width;float screenRadioFlip = 1.0f / screenRadio;float *m = (float *)m_matrix->get();m[0] *= screenRadio;m[4] *= screenRadio;m_matrix->scale(screenRadioFlip, 1, 1);if (m_offsetRect.size.width > 0 && m_offsetRect.size.height > 0) {float offsetX = m_offsetRect.origin.x - (0.5f - m_offsetRect.size.width / m_width * 0.5f);float offsetY = m_offsetRect.origin.y - (0.5f - m_offsetRect.size.height / m_height * 0.5f);rect.origin.x -= offsetX * m_width;rect.origin.y -= offsetY * m_height;m[12] = offsetX * 2.0f;m[13] = offsetY * 2.0f;} else {float centerX = (m_width - rect.size.width) * 0.5f;float centerY = (m_height - rect.size.height) * 0.5f;m[12] = (rect.origin.x - centerX) / m_width * 2.0f;m[13] = (rect.origin.y - centerY) / m_height * 2.0f;rect.origin.x = centerX;rect.origin.y = centerY;}}if(m_isCut){if(m_originalRect.size.width != 0 && m_originalRect.size.height != 0){m_texcoord[0].set(0, 0);if(1.0f*m_materialRect.size.width/m_originalRect.size.width < 1.0){m_texcoord[1].set(1.0f*m_materialRect.size.width/m_originalRect.size.width, 0);}else{m_texcoord[1].set(1.0,0);}m_texcoord[2].set(0, 1);if(1.0f*m_materialRect.size.width/m_originalRect.size.width < 1.0){m_texcoord[3].set(1.0f*m_materialRect.size.width/m_originalRect.size.width, 1);}else{m_texcoord[3].set(1.0, 1);}}else{m_texcoord[0].set(0, 0);m_texcoord[1].set(1, 0);m_texcoord[2].set(0, 1);m_texcoord[3].set(1, 1);}}m_position[0].set(rect.origin.x, rect.origin.y);m_position[1].set(rect.right(), rect.origin.y);m_position[2].set(rect.origin.x, rect.bottom());m_position[3].set(rect.right(), rect.bottom());}
}
代码解说:
float x = m_materialRect.origin.x * m_width;float y = m_materialRect.origin.y * m_height;Rect rect(x, y, m_materialRect.size.width, m_materialRect.size.height);
m_materialRect.origin.x
和m_materialRect.origin.y
分别是占m_width和m_height的比例,相乘就获取真实的x,y坐标值,然后保存到Rect。
m_matrix->identity();
是矩阵的初始化, m_matrix->scale(m_scale);
进行缩放, m_matrix->rotateZ(m_angle);
进行旋转.
// 纹理在屏幕上旋转,x 和 y 需要计算屏幕的比例,防止拉伸float screenRadio = m_height / m_width;float screenRadioFlip = 1.0f / screenRadio;float *m = (float *)m_matrix->get();m[0] *= screenRadio;m[4] *= screenRadio;m_matrix->scale(screenRadioFlip, 1, 1);
上面的代码是对进行缩放后的再按屏幕(720,1280)比例进行再次矫正,防止拉伸,实际上的操作 m[0] *= screenRadio*screenRadioFlip; m[4] *= screenRadio*screenRadioFlip;
如果不理解可以查看缩放矩阵和Z轴旋转矩阵相乘
float centerX = (m_width - rect.size.width) * 0.5f;float centerY = (m_height - rect.size.height) * 0.5f;m[12] = (rect.origin.x - centerX) / m_width * 2.0f;m[13] = (rect.origin.y - centerY) / m_height * 2.0f;rect.origin.x = centerX;rect.origin.y = centerY;
-
float centerX = (m_width - rect.size.width) * 0.5f;
:计算屏幕宽度减去矩形宽度后的一半,以此确定矩形在 x 轴上居中的位置。 -
float centerY = (m_height - rect.size.height) * 0.5f;
:计算屏幕高度减去矩形高度后的一半,以此确定矩形在 y 轴上居中的位置。 -
m[12] = (rect.origin.x - centerX) / m_width * 2.0f;
:这行代码的目的是根据矩形左上角的 x 坐标相对于屏幕中心点的偏移量来计算 x 轴的位移量。以下是具体步骤:(rect.origin.x - centerX)
:计算矩形左上角 x 坐标和屏幕中心 x 坐标之间的偏移量。/ m_width
:将得到的偏移量除以屏幕宽度,将其转换为比例,结果范围在 [-0.5, 0.5] 之间。* 2.0f
:最后乘以 2.0,将比例倍增,确保偏移量适合矩阵的变换范围。而归一化后的[-0.5, 0.5]范围不太适合用于这些变换。通过乘以2,可以使得矩阵的位移调整更加明显和精确[-0.1, 0.1]
-
这样计算出的结果将会被存储在矩阵
m_matrix
的第 12 个元素中,通常表示 x 轴的位移信息。
总的来说,这段代码用于根据矩形左上角与屏幕中心的偏移量,计算并设置矩阵的 x 方向位移,以便将矩形移动到屏幕的中心位置,从而实现在屏幕中心进行正确显示和定位。
Matrix4说明:
Matrix4& Matrix4::identity()
{m[0] = m[5] = m[10] = m[15] = 1.0f;m[1] = m[2] = m[3] = m[4] = m[6] = m[7] = m[8] = m[9] = m[11] = m[12] = m[13] = m[14] = 0.0f;return *this;
}Matrix4& Matrix4::rotateZ(float angle)
{angle *= DEG2RAD;float c = cosf(angle);float s = sinf(angle);float m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3],m4 = m[4], m5 = m[5], m6 = m[6], m7 = m[7];m[0] = m0 * c + m4 *-s;m[1] = m1 * c + m5 *-s;m[2] = m2 * c + m6 *-s;m[3] = m3 * c + m7 *-s;m[4] = m0 * s + m4 * c;m[5] = m1 * s + m5 * c;m[6] = m2 * s + m6 * c;m[7] = m3 * s + m7 * c;return *this;
}Matrix4& Matrix4::scale(float s)
{return scale(s, s, s);
}Matrix4& Matrix4::scale(float x, float y, float z)
{m[0] = m[0]*x; m[1] = m[1]*x; m[2] = m[2]*x; m[3] = m[3]*x;m[4] = m[4]*y; m[5] = m[5]*y; m[6] = m[6]*y; m[7] = m[7]*y;m[8] = m[8]*z; m[9] = m[9]*z; m[10]= m[10]*z; m[11]= m[11]*z;return *this;
}
缩放转的矩阵等于:
沿Z轴旋转的矩阵等于:
计算重心坐标原理
已知三角形3顶点坐标A(x1,y1),B(x2,y2),C(x3,y3),求三角形ABC的面积的公式
写成一般形式如下:
设A(x1,y1),B(x2,y2),C(x3,y3)在坐标系中中顺序为三点按逆时针排列,对应的权重 ( weight1, weight2, weight3 )
weight1 + weight2 + weight3 = 1S=1/2[(x1y2-x2y1)+(x2y3-x3y2)+(x3y1-x1y3)]//分别计算三个点对总面积的贡献 这一步表示每个点在总面积中所占的比例。
S1 = S * weight1;
S2 = S * weight2;
S3 = S * weight3;
//计算加权平均的重心坐标 (xw, yw)xw = (S1 * x1 + S2 * x2 + S3 * x3) / S;yw = (S1 * y1 + S2 * y2 + S3 * y3) / S;
在重心坐标系中,三角形平面的任何一个点(x,y)都可以表示成三角形三个顶点的线性组合(系数分别是α β γ \alpha \beta \gammaαβγ,且满足这三个系数相加和为1)
获得三角形任意一点的重心坐标
人脸贴纸说明
1=weight1+weight2+weight3
p_index_1=44
weight1=-2.2564
p_index_2=38
weight2=1.6250
p_index_3=37
weight3=1.6314void processFace2DLocate()
{if (m_position == nullptr) {Rect rect = m_materialRect;rect.origin.x = (m_width - rect.size.width) * 0.5f;rect.origin.y = (m_height - rect.size.height) * 0.5f;// 更新数据int count = 4;m_position = new Vector2[count];m_position[0].set(rect.origin.x, rect.origin.y);m_position[1].set(rect.right(), rect.origin.y);m_position[2].set(rect.origin.x, rect.bottom());m_position[3].set(rect.right(), rect.bottom());m_texcoord = new Vector2[count];m_texcoord[0].set(0, 0);m_texcoord[1].set(1, 0);m_texcoord[2].set(0, 1);m_texcoord[3].set(1, 1);m_indexCount = 6;m_pointIndex = new unsigned short[m_indexCount]{0, 1, 2, 2, 1, 3};m_matrix = new Matrix4;}m_matrix->identity();Vector2* point106 = m_face->getPoint106();float screenRadio = m_height / m_width;float screenRadioFlip = 1.0f / screenRadio;float x_dis = std::fabs(point106[32].x - point106[0].x) * screenRadioFlip;float y_dis = std::fabs(point106[32].y - point106[0].y);// 标准人脸0\32之间的距离float standard_face_x = 475.0f / m_width;float standard_face_y = 0.0f;float scale = std::sqrt(x_dis * x_dis + y_dis * y_dis) / std::sqrt(standard_face_x * standard_face_x + standard_face_y * standard_face_y) * screenRadio;int index1 = m_2DLocateParam.p_index_1;float weight1 = m_2DLocateParam.p_weight_1;int index2 = m_2DLocateParam.p_index_2;float weight2 = m_2DLocateParam.p_weight_2;int index3 = m_2DLocateParam.p_index_3;float weight3 = m_2DLocateParam.p_weight_3;float _x1 = point106[index1].x;float _y1 = point106[index1].y;float _x2 = point106[index2].x;float _y2 = point106[index2].y;float _x3 = point106[index3].x;float _y3 = point106[index3].y;// 总面积float S = 0.5 * fabs((_x1 * _y2 - _x2 * _y1) + (_x2 * _y3 - _x3 * _y2) + (_x3 * _y1 - _x1 * _y3));// s_index1float S1 = S * weight1;// s_index2float S2 = S * weight2;// s_index3float S3 = S * weight3;float xw = (S1 * _x1 + S2 * _x2 + S3 * _x3) / S;float yw = (S1 * _y1 + S2 * _y2 + S3 * _y3) / S;m_matrix->scale(scale);m_matrix->rotate(m_face->getYaw() * 1.2, 0.0, 1.0, 0.0);m_matrix->rotate(m_face->getPitch() * 1.2, 1.0, 0.0, 0.0);m_matrix->rotate(m_face->getRoll(), 0.0, 0.0, 1.0);
// printf("m_face->getRoll() = %f, %f, %f\n", 360 + m_face->getRoll(), m_face->getPitch(), m_face->getYaw());// 纹理在屏幕上旋转,x 和 y 需要计算屏幕的比例,防止拉伸float *m = (float *)m_matrix->get();m[0] *= screenRadio;m[4] *= screenRadio;m_matrix->scale(screenRadioFlip, 1, 1);m[12] = xw * 2.f - 1.f;m[13] = yw * 2.f - 1.f;
}
m_matrix
进行旋转变换,根据人脸的偏航角(yaw)、俯仰角(pitch)和横摆角(roll)来调整姿态。让我给您解释一下:
-
m_matrix->rotate(m_face->getYaw() * 1.2, 0.0, 1.0, 0.0);
- 这行代码根据人脸的偏航角(yaw)来进行绕 Y 轴旋转。
m_face->getYaw()
是获取人脸的偏航角度,乘以1.2用于增加旋转幅度。- 参数
(0.0, 1.0, 0.0)
表示围绕 Y 轴旋转。
-
m_matrix->rotate(m_face->getPitch() * 1.2, 1.0, 0.0, 0.0);
- 这行代码根据人脸的俯仰角(pitch)来进行绕 X 轴旋转。
m_face->getPitch()
是获取人脸的俯仰角度,乘以1.2用于增加旋转幅度。- 参数
(1.0, 0.0, 0.0)
表示围绕 X 轴旋转。
-
m_matrix->rotate(m_face->getRoll(), 0.0, 0.0, 1.0);
- 这行代码根据人脸的横摆角(roll)来进行绕 Z 轴旋转。
m_face->getRoll()
是获取人脸的横摆角度。- 参数
(0.0, 0.0, 1.0)
表示围绕 Z 轴旋转。
point106[32]和point106[0]分别是左右两边脸部两个点,下面代码为了计算脸部对屏幕上的缩放比例,std::sqrt(x_dis * x_dis + y_dis * y_dis) 计算了特征点索引为 0 和 32 之间的欧几里德距离。同时,std::sqrt(standard_face_x * standard_face_x + standard_face_y * standard_face_y) 则计算了标准人脸上 x 和 y 方向的长度与屏幕尺寸的比例之间的欧几里德距离。
float x_dis = std::fabs(point106[32].x - point106[0].x) * screenRadioFlip;float y_dis = std::fabs(point106[32].y - point106[0].y);// 标准人脸0\32之间的距离float standard_face_x = 475.0f / m_width;float standard_face_y = 0.0f;float scale = std::sqrt(x_dis * x_dis + y_dis * y_dis) / std::sqrt(standard_face_x * standard_face_x + standard_face_y * standard_face_y) * screenRadio;
重心坐标值(xw,yw)在[0, 1]范围转化为[-1, 1] 范围,这个顶点坐标标准
m[12] = xw * 2.f - 1.f;m[13] = yw * 2.f - 1.f;
相关文章:

OpenGL-贴纸方案
OpenGL-贴纸方案 普通贴纸(缩放、Z轴旋转、平移) OpenGL环境说明 OpenGL渲染区域使用正交投影换算,正常OpenGL坐标是vertexData,这样的 Matrix.orthoM 进行换算 //顶点坐标(原点为显示区域中心店)private final float[] vertex…...

【性能测试】移动测试md知识总结第1篇:移动端测试课程介绍【附代码文档】
移动测试完整教程(附代码资料)主要内容讲述:移动端测试课程介绍,移动端测试知识概览,移动端测试环境搭建,ADB常用命令学习主要内容,学习目标,学习目标,1. window安装andorid模拟器,学习目标。主流移动端自动…...
Vue2和vue3的区别(前端面试常见问题)
1.Api的变化:vue3使用组合式Api(compostion Api)和Vue2是选项式Api(options Api)。选项式Api具有data ,watch,methods,computed,一个个的模块。如果代码过多可读性会很差…...

openGauss学习笔记-241 openGauss性能调优-SQL调优-审视和修改表定义
文章目录 openGauss学习笔记-241 openGauss性能调优-SQL调优-审视和修改表定义241.1 审视和修改表定义概述241.2 选择存储模型241.3 使用局部聚簇241.4 使用分区表241.5 选择数据类型 openGauss学习笔记-241 openGauss性能调优-SQL调优-审视和修改表定义 241.1 审视和修改表定…...
PDFPlumber解析PDF文本报错:AssertionError: (‘Unhandled’, 6)
文章目录 1、问题描述2、问题原因3、问题解决 1、问题描述 今天在使用PDFPlumber模块提取PDF文本时extract_text()方法报错,报错内容如下: Traceback (most recent call last):......File "F:\Python\...\site-packages\pdfminer\pdffont.py"…...

51WORLD正式落地中东,助力沙特伙伴与客户数字化升级!
近日,在被誉为中东“数字达沃斯”的LEAP科技展上,51WORLD首次震撼亮相Digital Twin Riyadh2924k㎡ 全要素城市底座、数字地球平台51Earth,向中东及全球科技从业者展现中国企业技术实力与创新能力。此外,以LEAP为起点,5…...

嵌入式学习38-数据库
数据库软件: 关系型数据库: Mysql (开源) Oracle SqlServer Sqlite (小型数据) 非关系型数据库:(快速查找数据) Redis NoSQ…...

去除PDF论文行号的完美解决方案
去除PDF论文行号的完美解决方案 1. 遇到的问题 我想去除论文的行号,但是使用网上的Adobe Acrobat裁剪保存后 如何去掉pdf的行编号? - 知乎 (zhihu.com) 翻译时依然会出现行号,或者是转成word,这样就大大损失了格式,…...

《ElementPlus 与 ElementUI 差异集合》icon 图标使用(包含:el-button,el-input和el-dropdown 差异对比)
安装 注意 ElementPlus 的 Icon 图标 要额外安装插件 element-plus/icons-vue. npm install element-plus/icons-vue注册 全局注册 定义一个文件 element-icon.js ,注意代码第 6 行。加上了前缀 ElIcon ,避免组件命名重复,且易于理解为 e…...
力扣题库第8题:去重后的最长子串
题目: 给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串的长度。 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。 示例 2: 输入: s "bbbbb" …...
CSS样式中长度单位含义解析:rpx、px、vw、vh、em、rem、pt
在 CSS 样式中,有几种常见的长度单位,包括 rpx 、 px 、 vw 和 vh 等,含义解析如下: 1 . rpx (响应像素): 是微信小程序中的一种相对长度单位,可以根据屏幕宽度进行自适应缩放。 1rp…...

全国车辆识别代码信息API查询接口-VIN深度解析
我们先来介绍下什么是vin码,以及vin码的构成结构解析,汽车VIN码,也叫车辆识别号码,通俗可以理解为汽车的身份证号码。 VIN码一共分四大部分: 1~3位,是世界制造厂识别代号(WMI)&…...
python django 模型中字段设置blank, null属性值用法说明
问题1: ShareUser models.CharField(max_length128, blankTrue) blank设置True和false分别代表什么含义, 有什么区别?chatgpt回答的答案如下: 在 Django 模型字段中,blank 参数用于指定在创建对象时该字段是否可以为空值。它的含义如下: blankTrue:…...
暴雨信息:可持续转型更需要“以人为本”
数字化正在开启新的商业模式和价值流,为企业与组织带来巨大收益。其中,“人 (People)”这一因素至关重要。 提供更好的工作与生活体验,应对人口老龄化、劳动力短缺等挑战。对于企业而言,解决这些问题既是社会责任,也是…...

1.2_3 TCP/IP参考模型
文章目录 1.2_3 TCP/IP参考模型(一)OSI参考模型与TCP/IP参考模型(二)5层参考模型(三)5层参考模型的数据封装与解封装 1.2_3 TCP/IP参考模型 (一)OSI参考模型与TCP/IP参考模型 TCP/I…...

真空泵系统数据采集远程监控解决方案
行业背景 半导体制造业可以说是现代电子工业的核心产业,广泛应用于计算机、通信、汽车、医疗等领域。而在半导体生产加工过程中,如刻蚀、 镀膜、 扩散、沉积、退火等环节,真空泵都是必不可少的关键设备,它可以构建稳定受控的真空…...

Python语言在编程业界的地位——《跟老吕学Python编程》附录资料
Python语言在编程业界的地位——《跟老吕学Python编程》附录资料 ⭐️Python语言在编程业界的地位2024年3月编程语言排行榜(TIOBE前十) ⭐️Python开发语言开发环境介绍1.**IDLE**2.⭐️PyCharm3.**Anaconda**4.**Jupyter Notebook**5.**Sublime Text** …...

基于Redis自增实现全局ID生成器(详解)
本博客为个人学习笔记,学习网站与详细见:黑马程序员Redis入门到实战 P48 - P49 目录 全局ID生成器介绍 基于Redis自增实现全局ID 实现代码 全局ID生成器介绍 背景介绍 当用户在抢购商品时,就会生成订单并保存到数据库的某一张表中&#…...
hadoop 总结
1.hadoop 配置文件 core-site hdfs-site yarn-site.xml worker hdfs-site.xml <?xml version"1.0" encoding"UTF-8"?> <?xml-stylesheet type"text/xsl" href"configuration.xsl"?> <configuration><pr…...

luatos框架中LVGL如何使用中文字体〈二〉编写脚本设置中文字体
本节内容,将和大家一同学习,在luatos环境中,使用lvgl库,一步步的编译固件、编写脚本,最终实现中文字体的显示。 芯片:AIR101 LCD屏:ST7789 上一节,我们一同学习了,硬件引…...

智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...