javascript - 如何使用JavaScript在Chrome v78 +上检测#enable-force-dark标志?

标签 javascript google-chrome google-chrome-extension

我最近为我的网站设计并实现了一种暗模式,该模式使用自定义暗色来匹配浅色(默认)配色方案,最近我还意识到Chrome 78具有一个可选标记#enable-force-dark。启用后(用户必须这样做),Chrome会自动尝试将网站转换为深色主题。这样做与操作系统的首选项是分开的,这意味着用户可以在系统范围内使用灯​​光模式,但是启用此标志后,Chrome仍会转换。

我正在使用以下代码来检测用户设备是否更喜欢暗模式,如其他线程所建议的那样。请注意,我使用javascript进行检测是因为有一个按钮来回切换它,最终它比使用@media查询最终提供了更好的解决方案。

if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
    // my dark mode code goes here
}

if语句在jQuery $(window).load函数下,并且运行良好。

我需要的是能够检测Chrome的新#enable-force-dark标志,撤消Chrome所做的更改并启用我的更改,因为Chrome转换尚未完成,并且会破坏我的自定义样式。我知道此功能并未得到广泛使用,但我希望面向 future 。

这有可能吗?我不需要让用户禁用该标志,尽管如果需要的话,我会禁用它。谢谢!

最佳答案

这是无法解决的问题,但Chrome的#enable-force-dark(默认模式下,没有子选项)似乎执行了简单的颜色映射。单击下面的代码片段以动态生成此映射,以提高您的观看乐趣。 (请确保向下滚动嵌入式框架以获得有趣的部分。)

body {
  background-color:white;
}
activate about:flags #enable-force-dark before using this snippet<br/>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>

//document.domain = 'https://stacksnippets.net';

function gray(s) {
  return s/(S-1)*100 + '%';
}

var S = 32; // renders a table of size S*S cells
for(let BODY_BGCOLOR of ['white']) {//, 'black']) {
  document.body.innerHTML += `(body background-color = ${BODY_BGCOLOR})<br/>`;

  for(let CASE of ['bg','text','bg gray','text gray','bg gray + text gray']) {

/*
    var iframe = $(`<iframe src="${location.href.replace(/(:\/\/[^\/]*\/).*$/, '$1')}"></iframe>`).appendTo(document.body);
    var d = iframe[0].contentWindow.document;
    d.open(); d.close();
    $('body', d).append('test');
 */
    $('<table>').css({display:'inline-block', border:'5px solid blue', backgroundColor:'black'}).append(
      $(`<caption>${CASE}</caption>`)
    ).append(

      Array(S).fill().map((_,l)=> 
        $('<tr>').append(

          Array(S).fill().map((_,s)=> {


            gradient = `hsl(0,${s/(S-1)*100}%,${l/(S-1)*100}%)`;

            if (CASE=='bg') {
              var backgroundColor = gradient; 
              var color = gradient;
              var text = '_';

            } else if (CASE=='text') {
              var backgroundColor = '#444'; // you can change this to convince yourself color and background-color are 
                                            // independent and do not depend on each other
              var color = gradient;
              var text = 'X';
            } else if (CASE=='bg gray') {
              var backgroundColor = `hsl(0,0%,${gray(l)})`;
              var color = '';
              var text = '_';
            } else if (CASE=='text gray') {
              var backgroundColor = 'black';
              var color = `hsl(0,0%,${gray(s)})`;
              var text = 'X';
            } else if (CASE=='bg gray + text gray') {
              var backgroundColor = `hsl(0,0%,${gray(l)})`;
              var color = `hsl(0,0%,${gray(s)})`;
              var text = '▙';
            }


            return $('<td>').css({backgroundColor, color, fontSize:'0.5em'}).text(text);
          })
        )
      )
    ).appendTo(document.body)


  }
  $('<br/>').appendTo(document.body);
}
</script>

you can also right-click body tag and dynamically change its background-color, then highlight and unhighlight text, to convince yourself that color and background-color are usually independent


从Chrome 79开始,似乎以下各项独立发生:
  • 任何具有background-color> #ccc(大约为#cdcdcd或更高版本)的东西,其background-color亮度反转(在尊重色调的同时反转)
  • 任何带有color <#999的东西,大致都会将其color亮度反转(在尊重色调的同时反转)

  • 不幸的是,似乎“力量”确实意味着力量。您可以访问https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme并查看“结果”面板,然后单击鼠标右键以在DOM检查器中查看结果,然后随意摆弄和动态更改颜色(即使使用鼠标单击颜色也是如此);看来您将要发生的事情(Chrome注意到prefers-color-scheme: dark,如果这样,则不会反转执行强制颜色映射),不会发生;确实是力量。

    据我所知,您甚至无法轻松地检测到这一点(也许有人可以鸣叫),因为window.getComputedStyle(some_element)将(大概正确)返回CSS规范计算出的样式,而不是显示给用户的实际样式。

    从数学上讲,很明显,除非更改此选项(您可以提交功能请求),否则我们不能“撤消”该操作以允许我们使用自己的样式表,或者以其他方式彻底解决它。这是一个证明:如果您查看上面生成的调色板,请后退一步,您会发现它已经减少了,因此某些颜色将不再可用。具体来说,文本将始终显示为浅色(无深色文本),而背景将始终显示为浅色(无浅色背景)。尝试在此模式下提出一种使深色文本或浅色背景变暗的方法;乍看起来似乎是不可能的。因此,没有发生“奇怪的东西”的情况下使用传统的background-colorcolor是没有希望的。 (您甚至不能使用SVG文本; SVG文本也正确地应用了深灰色颜色映射!)

    但是,并非完全不可能!我可以想到三个想法:
  • 如果您真的需要它,我想您可以使用<canvas>元素,该元素似乎没有受到影响……但这确实很丑陋(无法选择文本等)。几乎可以肯定不要这样做。
  • 非强制背景色:
  • background-color:gradient(...)似乎不受影响!因此,只要您真的想要浅色背景(大约#ccc或更高的颜色),就可以使用渐变来强制使用任何背景色。这可能效率低下并导致额外的GPU工作(或者可能没有;您需要对其进行测试)。
  • 您可以尝试使用带有数据网址的background-image:url(data:...);还没尝试过
  • 非强制文本颜色:
  • 这是一个很多技巧,但是我只是想出了一个可怕的 kludge:找到一系列像常量函数一样工作的CSS filters ...或(由于剪切了颜色值),只是将将颜色值与夹轨相对应,然后将其用作常量,然后在功能空间中应用任何必要的转换。就像有人给您Photoshop / GIMP / etc。软件,并告诉您“这是一个漂亮的风景图像...仅使用滤镜,将整个图像变成纯#0000ff蓝色!(或某些选定的颜色)” ....所以...。让我们来做

  • 这里的总体思路如下:
    .blue-text {
    
      filter: 
       brightness(0%)      /* multiple all colors by 0 to get black; white may not work */
       contrast(50%)       /* get gray from black */
       sepia(100%)         /* get non-gray from gray */
       contrast(250%)      /* \   may not be necessary, but here we */
       brightness(500%)    /*  |   get fully satured from less-than- */
       saturate(500%)      /* /    -fully saturated                  */
       /*hue-rotate(???deg) we have pure red now, but if we didn't, we'd do this*/
       hue-rotate(240deg)  /* rotate from pure red to whatever you want */
       saturate(200%);     /* for some reason, hue-rotate is not keeping saturation? */
                           /* unsaturate your color here, adjust lightness, etc. */
    
    }
    

    因此,...我们不想每次都贴上那种恐怖的颜色,而是提供一些选择。意识到只有在您希望使用深色文本时才需要使用它(因此,对于浅色则不需要这样做)。我可以想到两种方法。第一种方法是,如果您只是非常快速地模拟某些东西,或者您是前端设计师或正在做主题;这是“纯CSS”方式。使用以下方法,可以在其他地方在CSS声明中甚至内联使用深色文本filter:...的地方使用color:...。定义了一些预设,但不需要使用:

    <style>
    
    /* horrible workaround to Chrome #force-dark-mode DEMONSTRATION; DO NOT USE THIS AS IT WILL DESTROY THE WEB, and cause HORRIBLE INEFFICIENCIES.
       NO REALLY.
       DO NOT USE IN PRODUCTION. DO NOT USE ON PERSONAL WEBSITES.
       DON'T USE THIS.
       DEMO ONLY. Use javascript polyfill instead below. */
    
    :root {
      --color: brightness(0%) contrast(50%) sepia(100%) contrast(250%) brightness(500%) saturate(500%);
      
      --light: contrast(50%) brightness(200%);
      --dark: contrast(50%) brightness(50%);
      
      --red:     hue-rotate(0deg);
      --orange:  hue-rotate(40deg);
      --yellow:  hue-rotate(60deg);
      --green:   hue-rotate(120deg);
      --cyan:    hue-rotate(200deg);
      --blue:    hue-rotate(240deg);
      --purple:  hue-rotate(300deg);
      --magenta: hue-rotate(315deg);
    }
    
    .dark-green {
      filter: var(--color) var(--dark) var(--green);
    }
    .custom-color-light-blue {
      filter: var(--color)  contrast(50%) brightness(200%)  hue-rotate(240deg);  /*can flip order to be (hue)(contrast+brightness), but you get a slightly different color*/
    }
    
    </style>
    </head>
    
    DO NOT I REPEAT DO NOT USE THIS METHOD. Just imagine what would happen if everyone did this.
    
    <p class="dark-green">example 1: dark green</p>
    <p class="custom-color-light-blue">example 2: custom color (light blue)</p>
    <p class="x" style="filter: var(--color) var(--light) var(--blue);">example 3: inline style="..." light blue</p>
    
    misc examples with var(--color) var(--light|dark) var(--red), either as a class or as an inline style:
    
    <p class="x" style="filter: var(--color) var(--red);">examples: red</p>
    <p class="x" style="filter: var(--color) var(--light) var(--red);">examples: light red</p>
    <p class="x" style="filter: var(--color) var(--dark) var(--red);">examples: dark red</p>
    
    <p class="x" style="filter: var(--color) var(--orange);">examples: orange</p>
    <p class="x" style="filter: var(--color) var(--light) var(--orange);">examples: light orange</p>
    <p class="x" style="filter: var(--color) var(--dark) var(--orange);">examples: dark orange</p>
    
    <p class="x" style="filter: var(--color) var(--yellow);">examples: yellow</p>
    <p class="x" style="filter: var(--color) var(--light) var(--yellow);">examples: light yellow</p>
    <p class="x" style="filter: var(--color) var(--dark) var(--yellow);">examples: dark yellow</p>
    
    <p class="x" style="filter: var(--color) var(--green);">examples: green</p>
    <p class="x" style="filter: var(--color) var(--light) var(--green);">examples: light green</p>
    <p class="x" style="filter: var(--color) var(--dark) var(--green);">examples: dark green</p>
    
    <p class="x" style="filter: var(--color) var(--cyan);">examples: cyan</p>
    <p class="x" style="filter: var(--color) var(--light) var(--cyan);">examples: light cyan</p>
    <p class="x" style="filter: var(--color) var(--dark) var(--cyan);">examples: dark cyan</p>
    
    <p class="x" style="filter: var(--color) var(--blue);">examples: blue</p>
    <p class="x" style="filter: var(--color) var(--light) var(--blue);">examples: light blue</p>
    <p class="x" style="filter: var(--color) var(--dark) var(--blue);">examples: dark blue</p>
    
    <p class="x" style="filter: var(--color) var(--purple);">examples: purple</p>
    <p class="x" style="filter: var(--color) var(--light) var(--purple);">examples: light purple</p>
    <p class="x" style="filter: var(--color) var(--dark) var(--purple);">examples: dark purple</p>
    
    <p class="x" style="filter: var(--color) var(--magenta);">examples: magenta</p>
    <p class="x" style="filter: var(--color) var(--light) var(--magenta);">examples: light magenta</p>
    <p class="x" style="filter: var(--color) var(--dark) var(--magenta);">examples: dark magenta</p>
    
    </body>


    第二种方法是编写一个JavaScript库,该库将扫描整个DOM,调用getComputedStyle(HTMLElement),并自动计算适当的过滤器,然后将其作为CSS样式注入(inject)。如果Chrome没有针对页面上的数百个元素进行一次单独的CSS过滤器优化,则此速度可能会很慢。

    另外,如果Chrome无法一次在页面上一次使用数百个CSS滤镜,则这两种方法中的任一种可能都很慢或无法工作(也许您每次使用滤镜时都需要重新编译GPU着色器!!)。

    所以...让我们写那个库...

    让我们编写一个#enable-force-dark库

    因此,上面的主要问题是我们要向前兼容。也就是说,我们不希望通过插入这些怪异的CSS样式(这些样式会破坏GPU并通常使事情变得可怕)来创建会破坏网络的错误代码。我们想要向前兼容。

    一种方法是,如果有人要向眨眼团队提出功能请求,询问是否最好的检测方法,查看眨眼引擎源代码(它是开源的),或者询问是否有一些navigator.prefersColorScheme在将来。否则,就不可能像做类似的事情一样丑陋地向前兼容(如果它是2025年并且此代码仍在运行,请发送弹出式通知以对其进行更新...您实际上不希望这样做,但这只是说明了一种担忧,即如果您无法将其关闭,则需要拥有一个永久的第三方依赖关系,您可以信任该第三方来更新事物,例如<script src="http://hypothetical-trusted-third-party.com/always-up-to-date-forcedark-polyfill">,它将花费无数的金钱和是一个安全漏洞)。

    我们接下来干吗?

    我想我注意到强制黑暗的工作方式有一个非常奇怪的微妙之处。在上面的代码中,我们注意到反转函数与colorbackground-color无关(也就是说,它们是影响因素,即更改background-color不会影响color,反之亦然)...并非总是如此。

    如果我们有一个<iframe>,并且iframe的BODY TAG的background-color属性严格小于<#333333,则会发生一件非常奇怪的事情。在这种情况下(截至撰写本文时),Chrome浏览器将其用于“检测”页面是否具有“暗淡”的意图,如果是,则关闭对文本的暗淡模式强制(但不会关闭对背景颜色的暗模式强制)。伪代码似乎是:
    function internalSecretChromeFunction(...) {
      websiteIsDarkish = (document.body.style.backgroundColor < #333333);
      if (websiteIsDarkish)
        enableColorInversionForText();
      enableColorInversionForBackgroundColor();
    }
    

    这使我们对SVG文本有了一个完整的了解,我之前说过也被视为普通文本。因此,我们可以使用canvas元素ctx.drawImage函数来复制SVG,它似乎是直接从内存中复制图像,而不是重新绘制图像(也许是安全问题?),但是我们可以使用它来构建#enable-force-dark检测器如下!首先注意可能状态的矩阵:
  • not(#force-dark)
  • 深色文字是深色
  • 浅色文字是浅色
  • #force-dark && body.bgcolor <#333
  • 深色文字是深色
  • 浅色文字是浅色
  • #force-dark && body.bgcolor> = #333
  • 暗文本反转(暗->浅)
  • 浅色文字反转(浅色->深色)

  • 如何进行?我们可以在几毫秒内异步确定:
  • 使用<iframe>创建一个<body style="background-white;">,其中<svg>包含<canvas>元素和drawImage元素
  • 使用ojit_code将SVG复制到 Canvas
  • 查询像素颜色以查看其是否反转;在iframe
  • 之外返回此值

    但是,这似乎依赖于可能是错误的行为(实际上是三个错误)... 1)如果body.background-color为深色,则background-color不反转,2)倾向于color-scheme:深色媒体不导出,3)复制SVG图像,而不是从头开始重新生成

    [编辑几个星期后希望收到的信息...有人应该真正联系眨眼团队]

    当然,此功能可能会更改,因此此答案有一天可能不再有效。

    关于javascript - 如何使用JavaScript在Chrome v78 +上检测#enable-force-dark标志?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58735674/

    相关文章:

    javascript - Typescript 转译是否处理从 ES6 到 ES5 的转译?

    javascript - 如何在 JavaScript session cookie 中存储变量?

    javascript - 将变量从 javascript 输出到 HTML 成本表中

    html - 创建 Chrome 扩展以隐藏 DIV 显示 :none; on a specific page

    google-chrome-extension - 如何在 Firefox 插件中实现 Chrome 扩展的 chrome.tabs.sendMessage API

    javascript - 在 Aurelia 中用模板本身替换自定义元素(而不是将其包含在自定义元素中)?

    html - 在浏览器内查看 PDF - 在 Chrome 中使用 POST 时出现问题

    css - 我如何找出导致我的 css 在 chrome 中被删除线的原因?

    google-chrome - 从 Google Chrome 中的 Inspect Element 复制 HTML,如图所示

    popup - 增加Chrome扩展程序中的最大弹出窗口宽度?