如何使用 CSS 媒体查询检测 Android 设备的默认字体大小?

Posted

技术标签:

【中文标题】如何使用 CSS 媒体查询检测 Android 设备的默认字体大小?【英文标题】:How to detect Android device's default font size with CSS media queries? 【发布时间】:2020-09-20 21:20:44 【问题描述】:

android 中,设置 > 辅助功能 > 字体大小,用户可以在“小”、“默认”、“大”、“最大”之间设置字体大小。除其他外,此设置会影响 WebView 中 html 内容的默认字体大小。

我已经开发了我的布局,使其与默认字体大小相得益彰。将字体大小设置为“最大”会导致文本在某些地方被截断,水平滚动条出现在其他地方等。在这些情况下,我可以使用替代布局(例如,垂直堆叠而不是水平堆叠),但我不确定如何检测要使用的布局。

理想情况下,我会使用 CSS 媒体查询。比如:

#foo 
    display: flex;


@media (min-width: 360px) 
    #foo 
        /* If at least 360px available, use a horizontal layout */
        flex-direction: row;
    

问题是,360px 断点不受设备上字体大小设置的影响(这是有道理的)。我也尝试过其他测量单位:rem、ch、cm——但它们似乎都没有考虑到设备的字体大小。

我考虑过在页面加载时做这样的事情:

在屏幕上显示一行常量文本(例如“0000000000”) 在 JS 中测量 如果测量的宽度/设备宽度比高于某个设置的常数,则切换到替代布局

但这种方法会增加复杂性、延迟、重绘和页面加载闪烁。

有没有办法在 CSS(媒体查询或其他)中考虑设备字体大小?

【问题讨论】:

可以使用orientation: "portrait"orientation控制站点,看这里manifest.json您所要做的就是将信息发送到页面以垂直或水平显示。并根据这个参数提供相应的 manifest.json @GrzegorzT。我不确定我是否遵循 - 在清单中指定方向如何帮助我检测可用宽度(就适合的文本字符而言)? 在手机端检查是否设置了缩放,您知道在此手机分辨率和文本缩放下页面会中断。您正在下载一个带有一些参数的页面,该参数将成为另一个具有不同 orientation:" portrait " 的 manifest.json 的基础。例如。 myWebView.loadUrl("https://www.example.com?orientation=landscape");基于此参数,您可以在页面上提供各种 manifest.json 当然,这不仅仅是manifest.json的替换,你可以改变字体大小或者隐藏页面上的元素。您在非电话方面进行更改。手机只会通知网站该文本不适合视图,仅此而已。 @GrzegorzT。啊,我想我现在明白了——决定在本机代码中使用哪种布局(通过检查用户的首选字体大小、屏幕大小等),然后根据它加载不同的 HTML 文档? 【参考方案1】:

简答

不,您不能仅使用 CSS 来做到这一点。但是,您可以使用类似于您在问题中提到的方法(测量字体大小并相应地调整布局)来最小化影响。

长答案

您不能仅使用 CSS 来做到这一点,但是可以拥有一个无需重绘的高性能网站,并且可以回退到 no JS 的默认样式。

这种方法有一个缺点,您最终会在页面中注入样式表,这会影响第一次内容绘制时间。但是请记住,这与匹配媒体查询本质上是相同的(因此实际上,这与媒体查询之间没有区别,只是它依赖于 javascript)。

您可以通过内联相关样式来缓解这种情况,但显然这会带来页面权重成本。你将不得不决定哪个是更大的罪!

解决方法很简单。

(1) 使用与您描述的方法类似的方法计算出用户的字体大小。

(2) 根据需要加载覆盖关键布局选项的条件 CSS。

(2a) 或者向正文添加一个类,并根据现有样式表中的样式更改布局,或者如果在折叠上方,则在文档中内联。

1。计算出用户的字体大小

您可以在原版 JS 中作为内联脚本直接在页面标题中执行此操作,这样它就不会延迟任何事情(除了解析脚本)并且仍然可以执行。

尝试下面的示例,首先将字体大小设置为“中”,然后将字体大小设置为“特大”并再次运行脚本。您的字体大小应分别显示为 16px 和 24px。

var el = document.getElementById('foo');
var style = window.getComputedStyle(el, null).getPropertyValue('font-size');
var fontSize = style;
console.log(style)
<div id="foo">a</div>

我们现在有了一个与用户缩放相关的字体大小。

我们可以进一步改进这一点,只需将结果字体大小除以 16(默认大小)即可获得 %age scale。

var el = document.getElementById('foo');
var style = window.getComputedStyle(el, null).getPropertyValue('font-size');
var fontSize = style;
var fontSizePercentage = parseFloat(style) / 16 * 100;


console.log(style, fontSizePercentage + "%");
<div id="foo">a</div>

2。加载条件 CSS

现在我们知道字体大小是否已被用户缩放,我们只需有条件地加载 CSS。

为此,我们需要一个简单的 JavaScript 检查

//set to whatever criteria you need, if your site still works on "large" font size and only needs adjustment at "extra large" then use 124 etc.
if(fontSizePercentage > 100)
    //add the CSS

在下面的示例中,我有 3 列变成 3 行,以演示字体大小如何决定要应用的样式。

请注意,为了模拟一个动态添加的 CSS 文件,我添加了一些写入样式表的内联 CSS 代码,您显然只需添加一个样式表(我已经包含了一个函数来执行此操作,并且只是注释掉了该函数的位置被调用)。

var el = document.getElementById('foo');
var style = window.getComputedStyle(el, null).getPropertyValue('font-size');
var fontSize = style;
var fontSizePercentage = parseFloat(style) / 16 * 100;
el.remove();

console.log(fontSizePercentage);



//this is just to simulate adding CSS, you would obviously import a style sheet properly here. I have put the proper function further down.
var normal = `
    .col3
        float: left;
        width: 32%;
        margin-right: 1%;
    
`
var bigger = `
    .col3
        float: left;
        width: 100%;
    
`

var styleSheet = document.createElement("style");
styleSheet.type = "text/css";
if(fontSizePercentage > 100)
    styleSheet.innerText = bigger;
else
    styleSheet.innerText = normal;

document.head.appendChild(styleSheet);

////actual style sheet code/////
function addCss(fileName) 

  var head = document.head;
  var link = document.createElement("link");

  link.type = "text/css";
  link.rel = "stylesheet";
  link.href = fileName;

  head.appendChild(link);

if(fontSizePercentage > 100)
    //addCss('url-to-large-font-size-layout');
else
    //addCss('url-to-normal-font-size-layout');
<div id="foo">a</div>

<div class="col3">column</div>
<div class="col3">column</div>
<div class="col3">column</div>

您将从示例中看到我们动态添加样式表,在示例中的两个样式表之间进行选择。实际上,您可能只需要检查使用大字体样式表的要求,因为您的标准字体大小将被您的主 CSS 覆盖。

优点 这种方法实际上与“字体大小媒体查询”相同,并带有很小的 JavaScript 开销。

缺点 如果您对重绘感到困扰,那么性能显然对您很重要,此方法会添加一个额外的请求,并且可能会延迟“首屏内容”的首次内容绘制/初始页面渲染。

因此我提出第二个建议:

2a。向正文添加一个类。

使用与上述完全相同的方法,但不插入样式表,只需使用检查字体大小在页面加载时将类添加到正文。

只需将样式包含在当前样式表中,但将附加的正文类作为限定符。

var el = document.getElementById('foo');
var style = window.getComputedStyle(el, null).getPropertyValue('font-size');
var fontSize = style;
var fontSizePercentage = parseFloat(style) / 16 * 100;
el.remove();

var bod = document.getElementById('simulated-body');

if(fontSizePercentage > 100)
   bod.classList.add('large-font-layout');





console.log(fontSizePercentage);
.col3
        float: left;
        width: 32%;
        margin-right: 1%;
    
    
.large-font-layout .col3
        float: left;
        width: 100%;
    
<div id="simulated-body">
<div id="foo">a</div>
<div class="col3">column</div>
<div class="col3">column</div>
<div class="col3">column</div>
</div>

专业人士这不会添加任何额外的请求,不会影响您的页面绘制。通常,此选项比第一个选项更可取,因为您应该只需要覆盖少数 CSS 类,因此增加的权重可以忽略不计。

缺点 - 给你的 CSS 增加额外的权重。

结论

有人应该添加“用户字体大小”媒体查询:-P

说真的,尽管我会选择我给你的 (2a) 选项和inline your critical CSS。如果您在 CSS 中更改了 100 多个类(因此 CSS 权重成为问题),那么您的设计有问题,因此速度差异可以忽略不计。再加上 JS 小于 1kb,它不会影响您的绘画,这是解决您问题的简单而有效的方法。

选项 2 的奖励信息

作为一个额外的想法,您可以将选项 2 与检查屏幕宽度结合起来,以真正最大限度地减少通过网络发送的数据量。然而,这开始增加相当大的复杂性,这是你说你想要避免的。为了完整起见,我将其包含在此处。

function getFontSizePercentage()    
    
    var el = document.getElementById('foo');
    var style = window.getComputedStyle(el, null).getPropertyValue('font-size');
    var fontSize = style;
    var fontSizePercentage = parseFloat(style) / 16 * 100;
    el.remove();
    return fontSizePercentage;


function getPageWidth()
  return Math.max(
    document.body.scrollWidth,
    document.documentElement.scrollWidth,
    document.body.offsetWidth,
    document.documentElement.offsetWidth,
    document.documentElement.clientWidth
  );


function addCSS(fileName) 

  var head = document.head;
  var link = document.createElement("link");

  link.type = "text/css";
  link.rel = "stylesheet";
  link.href = fileName;

  head.appendChild(link);


var fontSizePercentage = getFontSizePercentage();
var pageWidth = getPageWidth();
var cssSize = "1920";


switch(true) 
  case (pageWidth < 1366):
    cssSize = "1366";
    break;
  case (pageWidth < 728):
    var cssSize = "728";
    break;
  default:
    cssSize = "1920";



var cssPath = "styles/screen-width-" + cssSize + "-font-size-adjust";

if(fontSizePercentage > 100)
    console.log("adding CSS for width: " + cssSize, cssPath);
    addCSS(cssPath);
else
   console.log("not adding CSS", cssPath);
&lt;div id="foo"&gt;a&lt;/div&gt;

【讨论】:

感谢您提供令人印象深刻的详细回答。关于结论中的“用户字体大小”媒体查询备注:直观地说,没有阅读规范,我希望像@media (min-width: 123ch) 这样的东西会起作用,因为基本字体的字符宽度会根据用户的设置而改变。但是,唉,它不是那样工作的。 JS将不得不这样做。再次感谢您! 没问题,是的,我尝试了所有我能想到的媒体查询,我想这与其他可访问性设置被公开的原因相同,他们不希望人们成为目标,因为他们需要更大的字体尺寸等。烦人,但希望以上内容对你来说足够不显眼!【参考方案2】:

有没有办法在 CSS(媒体查询或其他)中考虑设备字体大小?

媒体查询用于根据设备宽度设置字体大小,而不是相反。

您可能使用的单位将始终基于设备宽度,而不是字体大小(因为此时无法定义)。

您可以做的是,您可以根据基本字体大小设置它们,而不是根据百分比或使用 px 单位来定义列的宽度。

例如,而不是

.flex-col 
  flex: 0 1 30%;

你可以使用

.flex-col 
  flex: 0 1 20rem;

真正的问题不是您的媒体查询,而是您的容器没有以响应方式定义(例如固定宽度和高度)导致截断和滚动条。

【讨论】:

在一个具体示例中,我没有网格,也没有设置任何宽度。我有一个布局,其中三个按钮水平相邻显示(在 flex 容器内,flex-direction: row)。如果按钮标签放不下,我想切换到垂直布局(flex-direction: column)。 @PēterisCaune 这是默认行为,只要您在弹性容器上设置flex-wrap: wrap IIUC,在三个按钮的示例中,flex-wrap: wrap 可能导致两个按钮出现在一行上,第三个按钮出现在第二行上。我想要实现的是两种不同的布局:水平的所有三个按钮(如果它们适合的话)或垂直的所有三个按钮。换行比截断内容要好 :-)【参考方案3】:

您可以使用 VH(视口高度)或 VW(视口宽度)字体大小度量,字体将根据设备视口的百分比调整大小,例如:

1vh = 视口高度的 1% 2vw = 视口宽度的 2% 等

如果你在做媒体选择器,你也可以使用 rem 或 px 作为字体大小。

您仍然需要为不同的设备添加@media 选择器,请记住视口是设备屏幕的实际大小,例如 10 英寸屏幕将有一个 @media 选择器,最大为 1280 像素或以下。

/* please use caniuse.com  for  browser compatibility table*/ 
h1 
  font-size: 2.5vw;


h2 
  font-size: 2vw;


p 
  font-size: 12px;


@media only screen and (max-width:768px) 
  h1 
    font-size: 5vw;
  
  h2 
    font-size: 4vw;
  
  p 
    font-size: 18px;
  
<h1>text</h1>

<h2>text</h2>

<p>some normal font controled by media query</p>

【讨论】:

谢谢,您建议设置相对于视口宽度的字体大小。这不是我所追求的。我想使用用户选择的任何字体大小,但如果字体大小与视口宽度的比率超过特定阈值,则使用替代布局。 大众单位似乎也受到 Android 系统字体设置的影响。即带有“大”的 1vw 实际上与带有“最大”的 1vw 不同。

以上是关于如何使用 CSS 媒体查询检测 Android 设备的默认字体大小?的主要内容,如果未能解决你的问题,请参考以下文章

使用 CSS 媒体查询检测打印页面大小

CSS媒体查询及其使用

我需要一种使用 jquery 检测 CSS3 媒体查询支持的简单方法

CSS媒体查询不足以检测超大型移动/平板设备与桌面设备[重复]

Twitter Bootstrap - 如何检测媒体查询何时开始

CSS3媒体查询总结