首页 >web前端 >css教程 >% CSS:获取并窃取嵌入动画 SVG 中的服务器生成数据位

% CSS:获取并窃取嵌入动画 SVG 中的服务器生成数据位

Linda Hamilton
Linda Hamilton原创
2025-01-18 06:10:09390浏览

这是在 CSS 中获取 32 位 API 响应数据的直接后续内容

在 CSS 中,将 16 位响应数据放置在固有宽度和固有高度中,与根本无法获取 API 响应数据(没有 JavaScript)相比,这是一个巨大的改进...

然而,在我弄清楚这一点后的几天里,我的想法偏向一个方向:
如果是 16 位的话会更有趣32 次而不是仅仅两次。

Gif recording of a harsh terminal app setting several integer values to an array of variables one at a time

将 512 位打包到 SVG 中以进行渗透

我在睡前冥想,突然想到了另一个灵感 -

“如果图像文档本身可以为其自己内在大小设置动画怎么办?”

在灵感迸发的想法之后,通常会出现一系列的实现,每一个都闪现出对如何实现它的理解......我只需要弄清楚这样的图像类型是否存在。

我抓起手机,搜索了我所知道的所有动画图像格式,看看它们是否有能力。 svgwebpapnggif也许是奇怪的视频格式?我没找到。

早上,我内心的某个声音告诉我继续挖掘 SVG

我尝试过嵌入 CSS、媒体查询、使用定义、更多使用定义、深入研究众多 SVG 动画文档、试图欺骗它们,并阅读其他想法和动画相关属性 - 没有什么可以让我设置高度或宽度。

但是最后一个链接让我想到......

...viewBox 怎么样?我还有其他一些障碍需要解决,但是......那个可以制作动画吗?

vvv
就是这样!!
^^^

整理解空间

现在的问题是,如果你没有在根 svg 元素上设置宽度和高度属性,然后尝试使用 svg 作为伪元素的内容,它会呈现 0px x 0px,因为它是一个矢量文档,不再有内在尺寸。

所以我搜索并向其中添加了preserveAspectRatio...仍然是0x0...但是然后在我的CSS中,我将宽度固定为10px,并让viewBox保留的宽高比决定高度(我可以用嵌入 SVG 中的动画)aaand...包含它的 html 元素增长到预期高度。

:3

如果只有一帧,这会将我原来的 32 位切成两半;因为只有一个维度可以被渗透,而另一个维度是静态的。

但是!现在,我的第二个维度是时间,第一个维度受时间支配,所以有足够的数据可供使用。

多么令人兴奋!

我学习了所有关于如何控制 SVG 中的动画的知识,并编写了一个服务器端脚本来生成我的第一个动画 SVG:

<?php header('Content-type: image/svg+xml');

  $data = array(
    400,
    450,
    150,
    20,
    175
  );

  $datalen = count($data);

  $viewBoxXYWidth = '0 0 10 ';

  $frames = array_map(function ($val, $index) use ($viewBoxXYWidth) {
      return $viewBoxXYWidth . ((string) ($val));
  }, $data, range(1, $datalen));

  $dur = $datalen * 0.33; // total seconds

  $keytimeStep = 1 / ($datalen); // uniform portion per frame

  $keytimes = implode("; ", array_map(function ($index) use ($keytimeStep) {
      return ($index * $keytimeStep);
  }, range(0, $datalen - 1)));

  $values = implode("; ", $frames); 

  echo '<svg viewBox="0 0 10 100" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg">
    <animate attributename="viewBox" dur="' . $dur . 's" fill="freeze" begin="0.1s;" values="' . $values . '" keytimes="' . $keytimes . '" repeatcount="indefinite" calcmode="discrete"></animate>
  ';
?>

(为什么是 php?! - 因为我已经有了一台我已经支付多年费用的服务器,设置为运行 php......尽管我已经通过了解 JavaScript 和 Node 过着美好的生活真的很好,有时查找每个函数、运算符和语法以完成您知道可以在不知道具体细节的情况下可以做的事情是很有趣的。哈哈)

现在让我们从上一篇文章中分叉我的第一个 % CSS:获取并窃取嵌入动画 SVG 中的服务器生成数据位,以查看 CSS 随着 SVG 的变化而响应和改变大小 --vars:

确认!我们可以读取尺寸变化。就像上一篇文章一样,最终,它将使用视图时间轴测量技术而不是 tan(atan2()) 技术。

它会耗尽我的 CPU,因此我们希望在渗漏完成后将其从内容中删除。

从概念上讲,如何在程序上渗透一维时间

上面的演示本身并不是很有用。只要它存在,它就会报告高度的副本,但我们需要保存它......如果您不知道并且不能信任该顺序,那么一堆 16 位值有什么用呢?

我知道我可以使用 CPU Hack 累积 CSS 中的值,并以数学方式确定哪个 --var 更新传入的值,而不是仅仅保留其先前的值,因此我不会专门担心 CSS。一般来说,我们如何随着时间的推移渗透一维?

哨兵值来救援!

即使我想将数据包本身限制为 16 位,我们使用的测量区域的大小也不必限制为 16 位。所以我们也可以在那里装一些哨兵。 css-api-fetch 能够处理高达 99999 的值,远高于 65535(16 位上限)。

那么我们需要知道什么?

我们可能会遇到什么问题?

如果数据中的两个值连续相同,我们需要中断才能知道这是两个不同的数据包。我已经决定我们的目标是 512 位,因此我们需要 SVG 动画最多有 32 个 16 位数据帧,中间有哨兵帧...

如果 CPU 感觉很重,SVG 动画可能会完全跳过离散步骤。这意味着我们需要某种方式来始终知道我们正在进行哪一步。因此,我们不是使用单个“数据帧之间”哨兵,而是使用数据索引(像 CSS nth-* 选择器一样基于 1)作为哨兵值,使其成为显示该索引数据的离散步骤之前的自己的离散步骤。

哨兵指数->数据->哨兵指数->数据...

这让我们知道它何时循环,可能是当我们到达哨兵 1 时。

但是我们怎么知道它没有跳到不同的数据框并且不小心让我们将它记录在错误的槽中

我们需要让它循环并继续直到正确,而了解数据是否正确的最好方法是校验和!所以我们需要另一个数据框,以及该值的哨兵。

创建校验和算法

我可以使用 css-bin-bits 来异或所有数据,但它相当繁重并且在其他任何地方都不需要 - 让我们选择一个在 CSS 中很容易实现的替代方案。

从数学上讲,如果您采用 16 位值,将其除以 256(底数到整数),然后再次采用 16 位值除以 256,您将得到高字节和低字节。将这 8 位值加在一起,就得到了 9 位。这感觉像是一个合理的校验和方法,但让我们回到这一点。

只要 最终校验和是 16 位,我们就不必保持在 16 位范围内来计算校验和,所以让我们将所有(最多)相加32 个值。

我们必须小心由于跳帧而导致的错误存储写入,所以让我们添加偶数索引值两次,这样就有一些相似的顺序

16 位值的总和,32 次,再加上额外的 16 次,大约为 22 位。将每一侧的 11 位除以模,回到之前的想法,然后将它们加在一起,得到 12 位作为我们的校验和响应。

似乎是合理的...它并不完全防错,但 SVG 必须跳过几个步骤来搞乱它,以便现在可能生成相同的校验和...无论如何,让我们也发回数据长度并也将其包含在校验和中,只需将其添加为校验和的最后一步即可。最大数据长度(我们想要处理的 16 位值的数量)仅为 32,因此将长度值添加到 12 位并不会让我们超过 16 位。耶!

剧透:这我所做的,但 CSS 在 21 位左右的某个地方变得有损,所以我将其分割并有效地执行相同的算法,但一次分成较小的块。服务器端完全按照描述使用 alg。

从技术上讲,根据我们描述的设置,动画中的顺序并不重要,只要每个哨兵告诉您下一帧应该在数据

还有一件事,让我们将数据长度值放在响应中的第一位,并为其添加一个哨兵(SVG 动画中的哨兵位于该值之前,与其余数据一样)。

这是34 个哨兵。 SVG viewBox 高度不能为 0,CSS 将受益于允许 0 在内部表示没有数据,所以假设我们有 35 个哨兵,其中 0 故意未使用。

所有数据框现在都嵌入到 SVG 中,并在其值上添加 35 。长度和校验和数据值也将 35 添加到视图框值中。 SVG 动画中代表哨兵的 viewBox 高度的值将是 0 到 34(跳过 0),每个值都准确地告诉我们 SVG 动画中的下一帧代表什么。

CSS 方面,我们只检查

原始测量值是否大于 34,它是数据,因此从中减去 35,如果它小于 35,它是哨兵。

A meme picture of Charlie Day from It's Always Sunny in Philadelphia with a crazed look standing in front of a board covered in paper with red lines connecting them hectically

开始用 CSS 渗透 512 位

在我完成 PHP 端详细生成 SVG 动画之后,我想到了开始 CSS 进行此渗透过程的具体方法。

这是 PHP 代码!
<?php header('Content-type: image/svg+xml');

  $data = array(
    400,
    450,
    150,
    20,
    175
  );

  $datalen = count($data);

  $viewBoxXYWidth = '0 0 10 ';

  $frames = array_map(function ($val, $index) use ($viewBoxXYWidth) {
      return $viewBoxXYWidth . ((string) ($val));
  }, $data, range(1, $datalen));

  $dur = $datalen * 0.33; // total seconds

  $keytimeStep = 1 / ($datalen); // uniform portion per frame

  $keytimes = implode("; ", array_map(function ($index) use ($keytimeStep) {
      return ($index * $keytimeStep);
  }, range(0, $datalen - 1)));

  $values = implode("; ", $frames); 

  echo '<svg viewBox="0 0 10 100" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg">
    <animate attributename="viewBox" dur="' . $dur . 's" fill="freeze" begin="0.1s;" values="' . $values . '" keytimes="' . $keytimes . '" repeatcount="indefinite" calcmode="discrete"></animate>
  ';
?>

有几种方法可以在 CSS 中实现此目的,并且最近添加的规范可能会提供更多方法。

我的第一种方法在概念上是最简单的 - 对每条数据使用视图时间线并一遍又一遍地执行相同的操作。它确实有效,但我对自己的进展感到不满,因为它是多么糟糕。如果我继续的话,:root 上将有近 40 个动画。

所以我去睡觉了。

当我醒来时,我躺在那里好一会儿,看着窗外微笑着,带着刚刚醒来或沉思的嗡嗡声,然后一连串的想法涌入我的脑海。我翻了个身,抓起我的笔记本和最近的笔,从床上坐起来,开始写下仅用 6 个 CSS 动画就能窃取它的算法。

my chicken scratch handwriting on a single piece of lined notebook paper with arrows pointing to a couple of nested boxes detailing the method described below

在纸面上解决了;这就是

完全它的实现方式。

我起身,打开电脑,忽略了之前的工作,打开了一个新的% CSS:获取并窃取嵌入动画 SVG 中的服务器生成数据位。

我在那里设置了鸡抓痕中指示的 4 个 html 元素,然后在 CSS 面板中填充了与它们对应的 4 个空类选择器周围的注释。它现在不会在 :root 上,但我们可以在里面放置任何依赖它的东西。

在从纸上复制笔记并在 % CSS:获取并窃取嵌入动画 SVG 中的服务器生成数据位 中以更具体的细节编写注释之前,没有添加任何功能。

完成后,我只是阅读笔记并开始实施他们所说的,一直到最终的工作结果。

(我写了“20”而不是“35”,因为我要使用 256 位进行测试)

我将深入探讨它是如何工作的。由于视图时间轴和时间轴范围,如果您可以想象表面动画并被吸进“洞”,回到狭窄的顶部,然后再向下流动,则可以将数据设置为以克莱因瓶形状流入再次越过表面,通过 dom 层获得大小和复杂性,然后通过黑洞循环回到更高的意识 (:root)。

主要是垂直循环(而不是主要是水平循环或静态循环)

使用 CSS 窃取 512 位

我们稍微调整并使其更加清晰的注释:

256 测试 ->第512章 决赛

我在里面添加了一个演示节点,这很棒,因为我也可以在算法执行时显示内部结构。

但这是最后的注释,没有所有的实现和演示噪音。这准确地描述了它是如何工作的。

对于一篇文章来说,在外部嵌入这么多细节可能不太好,但我将分解每个块来展示如何它是如何实现的。

主控制器

此结构的顶部是 4 个时间轴值及其动画。所以让我们把它们放进去。

这实现的数据流的关键部分是它使我们能够将嵌套在 DOM 深处的数据提升回主机(时间线范围)。它效率不高,因此我们希望限制执行此操作的频率。每个注册的属性及其动画都可以垂直托管单个数据。数据的值由结构深处某个元素的内联或块视图位置决定 - 我们稍后会讨论该部分。

(请参阅上面之前嵌入的循环示例,以更清晰地了解数据流)

我们在这里提取的四条数据是:

--xfl-cpu-phase - 这是一个 0 到 4 之间的数值,表示当前正在执行 CPU Hack 的哪个阶段。 (CPU Hack 的单个“帧”是 4 到 5 个 CSS 渲染帧,阶段的一个循环“勾选”CPU Hack)我将在本文后面更具体地演示这一点。

--xfl-raw-data - 无论 SVG 处于其动画周期中,它都托管 SVG 的高度。我们的原始数据。如前所述,如果该值小于 35,则 SVG 动画的这个离散步骤是哨兵值。如果它大于 34,则 SVG 动画的这个离散步骤就是我们的 16 位值 35,它对应于前面的哨兵指示的值。

--xfl-data-type - 这是最新的哨兵值。在遇到下一个标记之前,该值不会更改。从设置 --xfl-raw-data 到设置此值有 1 个 CSS 帧延迟。

--xfl-data-value - 这是从原始值中减去 35 后的当前数据值,如果我们尚未达到序列的这一步,则为 0。从设置 --xfl-data-type 到设置此值有 1 CSS 帧延迟。

我还预先将 svg-animation-current-state-reporter 包装在一个仅具有功能的条件下,并且仅在该过程不完整时加载动画 SVG。 (因此,当我们完成时,所有内部结构都会从内存中删除,并且重型动画 svg 也会从渲染中删除)

关键帧值从该数据的最大值变为 0。稍后我将解释为什么这些值是向后的 - 查找 Uno Reverse 卡的图像。

CPU 提取器

接下来我们设置 CPU Hack 的基本样板

CPU Hack 样板只是遵循变量名称模式来设置捕获和提升动画。

如果我们有 1 个整数 --xfl\1 我们想要水平循环(随着时间的推移),我们注册它并设置捕获和提升动画,如下所示:

<?php header('Content-type: image/svg+xml');

  $data = array(
    400,
    450,
    150,
    20,
    175
  );

  $datalen = count($data);

  $viewBoxXYWidth = '0 0 10 ';

  $frames = array_map(function ($val, $index) use ($viewBoxXYWidth) {
      return $viewBoxXYWidth . ((string) ($val));
  }, $data, range(1, $datalen));

  $dur = $datalen * 0.33; // total seconds

  $keytimeStep = 1 / ($datalen); // uniform portion per frame

  $keytimes = implode("; ", array_map(function ($index) use ($keytimeStep) {
      return ($index * $keytimeStep);
  }, range(0, $datalen - 1)));

  $values = implode("; ", $frames); 

  echo '<svg viewBox="0 0 10 100" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg">
    <animate attributename="viewBox" dur="' . $dur . 's" fill="freeze" begin="0.1s;" values="' . $values . '" keytimes="' . $keytimes . '" repeatcount="indefinite" calcmode="discrete"></animate>
  ';
?>

然后完成对托管两个CPU动画的.cpu-exfiltrator元素的循环分配。我现在只针对其中一个值执行此操作:

<?php header('Content-type: image/svg+xml');

  $data = array(
    400,
    450,
    150,
    20,
    175
  );

  $datalen = count($data);

  $viewBoxXYWidth = '0 0 10 ';

  // add 35 to all the values so we can use 0 to 34 for sentinels. 0 = CSS-side sentinel, 1-32 = data frames, 33 = length, 34 = checksum
  $frames = array_map(function ($val, $index) use ($viewBoxXYWidth) {
      return ($viewBoxXYWidth . ((string) $index) . '; ' . $viewBoxXYWidth . ((string) ($val + 35)));
  }, $data, range(1, $datalen)); // 1 up to 32 = indicator that next frame is the value(+35) for that index(1-based)

  // no matter how many are in the array, '33' indicates the next frame is data length, which is used in the checksum too
  array_unshift($frames, $viewBoxXYWidth . '33; ' . $viewBoxXYWidth . ((string) ($datalen + 35))); // + 35 b/c data
  // unshift so the length is (hopefully) the first value read and a sense of progress can be reported

  $fullsum = 0;

  for ($x = 0; $x <= ($datalen - 1); $x++) {
    // double the odd ones so there's some semblance of order accounted for
    // the odd ones with 0 based index is the even ones on the CSS side
    $fullsum += ($data[$x] + (($x & 1) * $data[$x]));
  }

  $checksum = floor($fullsum / 2048) + ($fullsum % 2048) + $datalen + 35; // + 35 because it's data

  // no matter how many are in the array, '34' indicates the next frame is checksum
  array_push($frames, $viewBoxXYWidth . '34; ' . $viewBoxXYWidth . $checksum);

  $actualNumItems = count($frames) * 2;

  $dur = $actualNumItems * 0.33; // total seconds

  $keytimeStep = 1 / ($actualNumItems); // uniform portion per frame

  $keytimes = implode("; ", array_map(function ($index) use ($keytimeStep) {
      return ($index * $keytimeStep);
  }, range(0, $actualNumItems - 1)));

  $values = implode("; ", $frames); 

  echo '<svg viewBox="0 0 10 100" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg">
    <animate attributename="viewBox" dur="' . $dur . 's" fill="freeze" begin="0.1s;" values="' . $values . '" keytimes="' . $keytimes . '" repeatcount="indefinite" calcmode="discrete"></animate>
  ';
?>

在 Chrome 中,除非两个动画同时运行,否则它们不会静态循环(成为初始值)。这可能是暂停动画设置数字属性的优化的一个奇妙的副作用。

最后,由于我们使用的是 CPU Hack 的新自动版本(您不必像原始 hack 中那样通过 :hover 来循环阶段),因此我们连接 --xfl-cpu-phase var来自之前的(托管在此处的父元素上,因此我们可以使用样式查询来响应它)并控制动画的播放状态。

我们还输出 --cpu-next-phase ,稍后将其提升回顶部,并使用其视图位置和时间线范围为 --xfl-cpu-phase 设置下一个值。

我添加了一个额外的阶段来保持 CPU Hack 暂停,直到 SVG 动画测量成功锁定下一个 --xfl-data-type

<?php header('Content-type: image/svg+xml');

  $data = array(
    400,
    450,
    150,
    20,
    175
  );

  $datalen = count($data);

  $viewBoxXYWidth = '0 0 10 ';

  $frames = array_map(function ($val, $index) use ($viewBoxXYWidth) {
      return $viewBoxXYWidth . ((string) ($val));
  }, $data, range(1, $datalen));

  $dur = $datalen * 0.33; // total seconds

  $keytimeStep = 1 / ($datalen); // uniform portion per frame

  $keytimes = implode("; ", array_map(function ($index) use ($keytimeStep) {
      return ($index * $keytimeStep);
  }, range(0, $datalen - 1)));

  $values = implode("; ", $frames); 

  echo '<svg viewBox="0 0 10 100" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg">
    <animate attributename="viewBox" dur="' . $dur . 's" fill="freeze" begin="0.1s;" values="' . $values . '" keytimes="' . $keytimes . '" repeatcount="indefinite" calcmode="discrete"></animate>
  ';
?>

(就像现在一样,数据类型仍然始终为 0,因此一旦连接了下一阶段,这将已经在循环 CPU Hack。一旦我们有了数据类型哨兵,它就不会循环,直到我们完成故意用0哨兵清除它)

稍后,我们还将添加上述条件,以防止 CPU 阶段 1 在数据全部到位之前启动。这将确保在锁定数据类型(哨兵)和锁定数据值(原始 - 35)之间,我们希望将 CPU Hack 留在捕获阶段。因此,正如亚伯拉罕·希克斯 (Abraham Hicks) 所说,它“已经做好准备”。

我将继续注册我们期望 SVG 动画报告的所有 32 个值以及校验和和长度。

由于 --xfl\1 到 --xfl\32 的注册是一个大块,并且 CPU 动画也只是样板,因此我将把所有这些移动到 hack 设置的底部,以便以后忽略。

自动CPU破解

这会将下一个 cpu 阶段连接到 --xfl-cpu-phase 值

<?php header('Content-type: image/svg+xml');

  $data = array(
    400,
    450,
    150,
    20,
    175
  );

  $datalen = count($data);

  $viewBoxXYWidth = '0 0 10 ';

  // add 35 to all the values so we can use 0 to 34 for sentinels. 0 = CSS-side sentinel, 1-32 = data frames, 33 = length, 34 = checksum
  $frames = array_map(function ($val, $index) use ($viewBoxXYWidth) {
      return ($viewBoxXYWidth . ((string) $index) . '; ' . $viewBoxXYWidth . ((string) ($val + 35)));
  }, $data, range(1, $datalen)); // 1 up to 32 = indicator that next frame is the value(+35) for that index(1-based)

  // no matter how many are in the array, '33' indicates the next frame is data length, which is used in the checksum too
  array_unshift($frames, $viewBoxXYWidth . '33; ' . $viewBoxXYWidth . ((string) ($datalen + 35))); // + 35 b/c data
  // unshift so the length is (hopefully) the first value read and a sense of progress can be reported

  $fullsum = 0;

  for ($x = 0; $x <= ($datalen - 1); $x++) {
    // double the odd ones so there's some semblance of order accounted for
    // the odd ones with 0 based index is the even ones on the CSS side
    $fullsum += ($data[$x] + (($x & 1) * $data[$x]));
  }

  $checksum = floor($fullsum / 2048) + ($fullsum % 2048) + $datalen + 35; // + 35 because it's data

  // no matter how many are in the array, '34' indicates the next frame is checksum
  array_push($frames, $viewBoxXYWidth . '34; ' . $viewBoxXYWidth . $checksum);

  $actualNumItems = count($frames) * 2;

  $dur = $actualNumItems * 0.33; // total seconds

  $keytimeStep = 1 / ($actualNumItems); // uniform portion per frame

  $keytimes = implode("; ", array_map(function ($index) use ($keytimeStep) {
      return ($index * $keytimeStep);
  }, range(0, $actualNumItems - 1)));

  $values = implode("; ", $frames); 

  echo '<svg viewBox="0 0 10 100" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg">
    <animate attributename="viewBox" dur="' . $dur . 's" fill="freeze" begin="0.1s;" values="' . $values . '" keytimes="' . $keytimes . '" repeatcount="indefinite" calcmode="discrete"></animate>
  ';
?>

这里有一些 CSS 样板,可以使元素成为滚动容器并将其移出屏幕。重要的部分是:

视图时间线:--xfl-cpu-phase inline;

它表示这个伪元素的右边缘落在其 100px 宽的父元素中的位置,将其作为从左侧到我们从 0 移动到 4 的动画的“进度”连接起来......所以 25px 是完成的 25%,当 25% 在 0 到 4 之间时映射为 1。

picture of two 'reverse' cards from Uno 图像源自谷歌搜索并指向 Twitter

从技术上讲,动画是 4 比 0,并且从技术上讲,当视图向右移动时,它是从伪的右边缘开始测量的。因此,25px 宽的伪值距离其 100px 宽的滚动父级右侧为 75%,当 75% 介于 4 和 0 之间时,映射到值 1。

如果你不认知地处理反向逆向数学,只是接受最终结果是一个简单的进度 0 到 4,那么会更容易理解,因为动画中的最大值是 4(再次忽略动画 开始 4)。

我们还要编写就绪状态,将 CPU 保持在阶段 0,直到数据准备好。注释位于我们演示的第 64 行:

数据准备好 = 数据类型 > 0 && 原始帧数据 - 35 === 数据值

<?php header('Content-type: image/svg+xml');

  $data = array(
    400,
    450,
    150,
    20,
    175
  );

  $datalen = count($data);

  $viewBoxXYWidth = '0 0 10 ';

  $frames = array_map(function ($val, $index) use ($viewBoxXYWidth) {
      return $viewBoxXYWidth . ((string) ($val));
  }, $data, range(1, $datalen));

  $dur = $datalen * 0.33; // total seconds

  $keytimeStep = 1 / ($datalen); // uniform portion per frame

  $keytimes = implode("; ", array_map(function ($index) use ($keytimeStep) {
      return ($index * $keytimeStep);
  }, range(0, $datalen - 1)));

  $values = implode("; ", $frames); 

  echo '<svg viewBox="0 0 10 100" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg">
    <animate attributename="viewBox" dur="' . $dur . 's" fill="freeze" begin="0.1s;" values="' . $values . '" keytimes="' . $keytimes . '" repeatcount="indefinite" calcmode="discrete"></animate>
  ';
?>

等等,CSS 中的 === ?

这些现在已经非常过时了,今天我会以不同的方式做它们,在clamp()成为基线之前编写,但是当我需要它们时,我总是打开这个旧的codepen来无意识地复制粘贴数字比较器。更新这些并解释它们将是一篇很好的文章,但同时您可以阅读:https://codepen.io/propjockey/pen/YzZMNaz

读取 SVG 动画

这最初非常类似于 CPU Exfiltrator 部分,因为它是相同的技术,测量数据并将数据从 DOM 向上移动到其托管位置(范围)。

我们将测量并报告我们最初设置的基本元素的最后 3 个值。

在 ::before 之前,我们将渲染 SVG 并使用块视图位置设置 --xfl-raw-data,该位置是动画 SVG 高度的测量值。 (记住,我们将宽度固定为 10px)

在 ::after 上,我们将设置 --xfl-data-type 内联(标记值 0 到 34)和 --xfl-data-value 块(16 位值)。

父级需要足够宽才能渲染 SVG(至少 10 像素)并准确提供哨兵值的测量值(0 到 34)。

父级还需要足够高才能测量 16 位值 ( 35)。由于我们在第一步中设置了 100k 的最大值,因此即使它比我们需要的大 30% 左右,我们也只会使用它。

并将其移出屏幕到顶部和左侧,这样就不会导致滚动条。

因此,

.svg-animation-current-state-reporter

得到

<?php header('Content-type: image/svg+xml');

  $data = array(
    400,
    450,
    150,
    20,
    175
  );

  $datalen = count($data);

  $viewBoxXYWidth = '0 0 10 ';

  // add 35 to all the values so we can use 0 to 34 for sentinels. 0 = CSS-side sentinel, 1-32 = data frames, 33 = length, 34 = checksum
  $frames = array_map(function ($val, $index) use ($viewBoxXYWidth) {
      return ($viewBoxXYWidth . ((string) $index) . '; ' . $viewBoxXYWidth . ((string) ($val + 35)));
  }, $data, range(1, $datalen)); // 1 up to 32 = indicator that next frame is the value(+35) for that index(1-based)

  // no matter how many are in the array, '33' indicates the next frame is data length, which is used in the checksum too
  array_unshift($frames, $viewBoxXYWidth . '33; ' . $viewBoxXYWidth . ((string) ($datalen + 35))); // + 35 b/c data
  // unshift so the length is (hopefully) the first value read and a sense of progress can be reported

  $fullsum = 0;

  for ($x = 0; $x <= ($datalen - 1); $x++) {
    // double the odd ones so there's some semblance of order accounted for
    // the odd ones with 0 based index is the even ones on the CSS side
    $fullsum += ($data[$x] + (($x & 1) * $data[$x]));
  }

  $checksum = floor($fullsum / 2048) + ($fullsum % 2048) + $datalen + 35; // + 35 because it's data

  // no matter how many are in the array, '34' indicates the next frame is checksum
  array_push($frames, $viewBoxXYWidth . '34; ' . $viewBoxXYWidth . $checksum);

  $actualNumItems = count($frames) * 2;

  $dur = $actualNumItems * 0.33; // total seconds

  $keytimeStep = 1 / ($actualNumItems); // uniform portion per frame

  $keytimes = implode("; ", array_map(function ($index) use ($keytimeStep) {
      return ($index * $keytimeStep);
  }, range(0, $actualNumItems - 1)));

  $values = implode("; ", $frames); 

  echo '<svg viewBox="0 0 10 100" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg">
    <animate attributename="viewBox" dur="' . $dur . 's" fill="freeze" begin="0.1s;" values="' . $values . '" keytimes="' . $keytimes . '" repeatcount="indefinite" calcmode="discrete"></animate>
  ';
?>

之前变成

@keyframes capture {
  0%, 100% {
    --xfl\1-captured: var(--xfl\1);
  }
}

@keyframes hoist {
  0%, 100% {
    --xfl\1-hoist: var(--xfl\1-captured, 0);
  }
}

和::获取后

  --xfl\1: calc(
    var(--xfl\1-hoist, 0) + 1
  );

本质上,这些后值的存储介质是 1px 正方形伪相对于其父滚动容器的视图位置。我们在左侧和顶部的计算中减去 1px,因为伪值本身是 1x1,我们希望它在对应值为 0 时报告 0。

这与之前所做的非常相似。

我们如何计算这些值应该有几个复杂之处,如注释所示:

  @container style(--xfl-cpu-phase: 4) {
    animation-play-state: paused, paused;
    --cpu-next-phase: calc(
      min(1, var(--xfl-data-type)) * 4
    );
  }

理解如何解决这个问题的关键是任何比较器值都设置为 0 或 1。如果为真则为 1,如果为假则为 0。然后将其乘以某个值。如果为 false,则结果保持为 0,否则变为任何值。

Ana Tudor 在这里深入探讨了这个想法的运作方式

然后,如果我们进行两次比较,对第二个值进行不同或相反的比较,并将它们相加(确保最多一个比较器为 1),那么其中两个相加只是说“else如果”。

如果没有准备好 * 使用旧值,否则
如果准备好了 * 使用这个新值

这就是我们在报告类型后在 SVG 动画离散步骤的持续时间内保持哨兵值的方式。

实现它的 CSS 代码从第 191 行开始,就在我们放在底部的 --xfl\... 属性注册的大块上方
@property --xfl\1 { 语法:“”;初始值:0;继承:真实; }
...
并且它包含附加注释:

设置特定的 CSS --var 值(寻址分配)

我们刚刚接触的逻辑与我们用于所有 --xfl\1, 2, 32 值的概念完全相同。

<?php header('Content-type: image/svg+xml');

  $data = array(
    400,
    450,
    150,
    20,
    175
  );

  $datalen = count($data);

  $viewBoxXYWidth = '0 0 10 ';

  $frames = array_map(function ($val, $index) use ($viewBoxXYWidth) {
      return $viewBoxXYWidth . ((string) ($val));
  }, $data, range(1, $datalen));

  $dur = $datalen * 0.33; // total seconds

  $keytimeStep = 1 / ($datalen); // uniform portion per frame

  $keytimes = implode("; ", array_map(function ($index) use ($keytimeStep) {
      return ($index * $keytimeStep);
  }, range(0, $datalen - 1)));

  $values = implode("; ", $frames); 

  echo '<svg viewBox="0 0 10 100" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg">
    <animate attributename="viewBox" dur="' . $dur . 's" fill="freeze" begin="0.1s;" values="' . $values . '" keytimes="' . $keytimes . '" repeatcount="indefinite" calcmode="discrete"></animate>
  ';
?>

您读取 --xfl-set\1 就好像 --xfl-data-type 等于 1 使用 --xfl-data-is-ready 并隐含 else 0

--xfl-data-is-ready 是早些时候建立的,作为将我们保持在第 0 阶段的标志,直到需要翻转到第 1 阶段。

这意味着我们的条件是 && 逻辑。两个标志都必须为 1 才能通过。

然后你继续读取 --xfl\1 就好像 --xfl-set\1 使用 --xfl-data-value (当前的 SVG 动画值),否则如果不是 --xfl-set\1 使用 -- xfl\1-hoist(CPU hack 为 --xfl1 保留的先前值)

这是高度重复的内容,几乎描述了此次渗透的其余全部内容。

最后的步骤是运行基本的 calc() 和 mod() 数学来构建前面所述的校验和,然后将计算出的校验和 === 嵌入到 SVG 动画中的校验和添加到 CPU Hack 中,以便我们知道它何时完全的。一切都一样。

所以现在是时候了。 :)

演示:CSS 动画 SVG 渗透黑客

因为我想向这个黑客的每一个部分展示每个元素一个值,所以这个演示非常繁重。超过 2000 行 HTML 和超过 400 行 CSS。另外,我使用 css-bin-bits 将每个寄存器转换为二进制等

(点击 codepen 框架右下角的重新运行即可实时查看它发生的情况!)

没有 JavaScript!


打开联系方式?

如果您认为这很简洁并希望了解更多信息,请联系我们!总是很乐意回答问题。

% CSS:获取并窃取嵌入动画 SVG 中的服务器生成数据位 % CSS:获取并窃取嵌入动画 SVG 中的服务器生成数据位 DEV Blog % CSS:获取并窃取嵌入动画 SVG 中的服务器生成数据位 % CSS:获取并窃取嵌入动画 SVG 中的服务器生成数据位
% CSS:获取并窃取嵌入动画 SVG 中的服务器生成数据位 % CSS:获取并窃取嵌入动画 SVG 中的服务器生成数据位 DEV Blog % CSS:获取并窃取嵌入动画 SVG 中的服务器生成数据位 % CSS:获取并窃取嵌入动画 SVG 中的服务器生成数据位

?@JaneOri.% CSS:获取并窃取嵌入动画 SVG 中的服务器生成数据位

?@Jane0ri

以上是% CSS:获取并窃取嵌入动画 SVG 中的服务器生成数据位的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn