Firefox 4:使用 -moz-element 将任意元素作为背景绘制


这是一篇由 Markus Stange 撰写的客座文章。Markus 通常负责 Firefox Mac 主题的实现,但这次他进行了一次在 Gecko 布局引擎中的小侧游,以实现 -moz-element

在 Firefox Beta 4 中,我们为 CSS background-image 属性引入了新的扩展:能够使用 -moz-element(#elementID) 将任意元素作为背景绘制。

This element will be used as a background.

This box uses #myBackground1 as its background!



-moz-element() 图像的工作方式与普通的 url() 图像相同。这意味着它受所有熟悉的背景属性的影响,例如 background-positionbackground-repeat,甚至 background-size

例如,使用 background-size 可以创建所引用元素的缩略图

#thumbnails li {
  width: 160px;
  height: 120px;
  background-repeat: no-repeat;
  background-size: contain;
}



关于 -moz-element,需要注意三点

  1. 它是实时的:无论何时在引用元素中发生任何事情,-moz-element 背景图像都将更新。它还会显示诸如文本选择或闪烁的光标之类的内容。

  2. 它是纯视觉的。这意味着您无法“点击”到原始元素。这是设计使然。

  3. 它适用于任何 HTML 元素。甚至包括 <iframe>



    <video>



    … 和 <canvas>



事实上,将画布用作背景在某些应用程序中很有用。例如,如果您正在 在浏览器中将褐色调应用于 CSS 背景图像,则现在不再需要将处理后的画布图像 转换为数据 URI。相反,您可以直接将画布本身设置为背景图像。

使用画布作为背景图像也 受 Webkit 支持,使用 -webkit-canvas()

绘制循环

关于递归引用的简要说明:如果您尝试 绘制一个已经通过 -moz-element 绘制的元素,则会检测到绘制循环并阻止它。因此,您需要考虑其他方法来绘制您的 谢尔宾斯基地毯

隐藏引用的元素

有时您不希望原始引用元素可见,而只希望 -moz-element 背景图像可见。那么您该怎么做呢?您不能只是在元素上设置 display: nonevisibility: hidden,因为那样的话,-moz-element 背景图像中也没有任何内容可以绘制——它将是透明的。

相反,您需要阻止元素在屏幕上呈现而不真正隐藏它。一种方法是用另一个元素将其包装起来,并在该元素上设置 height: 0; overflow: hidden;

有三种类型的元素不受此规则约束:图像、画布和视频。这些类型的元素可以具有 display: none仍然可以在 -moz-element 中使用。事实上,它们甚至不需要在 DOM 中。

新的 DOM API
document.mozSetImageElement

我们在 document 对象中添加了一个新方法:document.mozSetImageElement(<elementID>, <element>)

考虑以下代码段

var slide5 = document.getElementById("slide-5");
document.mozSetImageElement("current-slide", slide5);

现在,所有具有 background-image: -moz-element(#current-slide) 的元素都将绘制 ID 为 slide-5 的元素,即使存在 ID 为 current-slide真实元素!

调用 document.mozSetImageElement("current-slide", null) 将停止覆盖。

此 API 在各种用例中都非常方便。我之前部分已经提到了其中之一:使用 mozSetImageElement,您可以使用不属于 DOM 树的画布和图像元素。

var img = new Image();
img.src = "my_image.png";
document.mozSetImageElement("image", img);

var canvas = document.createElement("canvas");
canvas.width = canvas.height = 100;
var ctx = canvas.getContext("2d");
// ... draw into ctx ...
document.mozSetImageElement("canvas", canvas);

查看演示

另一个受益于 mozSetImageElement 的场景涉及 JavaScript 实用程序库。您可能有一个这样的函数

var runningNumber = 0;
function addReflectionToElement(reflectedElement) {
  var referenceID = "reflected-element-" + runningNumber++;
  var reflection = document.createElement("div");
  reflection.className = "reflection";
  reflection.style.backgroundImage =
    "-moz-element(#" + referenceID + ")";
  document.mozSetImageElement(referenceID, reflectedElement);
  // ... insert reflection into the DOM ...
}

这样,您可以最大程度地减少实用程序函数的影响,因为您不必操作传入元素的 ID。

最后,mozSetImageElement 还允许您引用来自其他文档的元素,例如来自 iframe 内部——当然,要遵守同源策略。

-moz-element 用于 SVG 绘制服务器:图案和渐变

如果您曾经手动编写过任何 SVG,那么您就会熟悉 绘制服务器 的概念:当您不只是想要单一纯色时,可以在 fillstroke 属性中使用这些东西。现在,您也可以在 HTML 元素上使用它们,使用 -moz-element

This element has both types of SVG paint servers in its background: a pattern and a gradient.


请注意,由于我们的 新的 HTML5 解析器,我们甚至不需要使用 XHTML 就能嵌入 SVG。

此功能与 CSS 渐变 和 SVG 图像的功能重叠,但在某些情况下非常有用,例如动画。例如,假设您想创建一个具有如下所示的动画渐变的进度条

您可以使用 CSS 渐变和一些定期更新 background-position 属性的 JavaScript 来实现这一点。但您也可以使用通过 SMIL 进行动画处理的 SVG 渐变,无需任何 JavaScript

查看演示

可以使用 CSS 动画实现相同的效果,但在 Gecko 中尚未实现这些动画之前,您可以使用此方法。

对 SVG 作为 CSS 背景的支持(错误 276431)将很快添加。

此外,这里还有一个供您参考的 CSS + SVG 吃豆人

应用

我对 -moz-element 的用法还有两个建议

反射

什么是反射?

#reflection {
  /* It's a copy of the original element... */
  background: -moz-element(#reflected-element)
              bottom left no-repeat;

  /* ... turned upside down ... */
  -moz-transform: scaleY(-1);

  /* ... with a gradual fade-out effect towards the bottom. */
  mask: url(#reflection-mask);
}



因为我们可以对反射应用任意样式,所以我们可以生成诸如 动画水波纹 之类的效果。

花哨的幻灯片切换

在此演示中,我希望有一个幻灯片切换效果,看起来像是上一张幻灯片的上半部分向下折叠以显示下一张幻灯片

您将如何实现这一点?您显然需要使用某种转换,但要在哪个元素上使用?幻灯片的上半部分需要与下半部分具有不同的转换,因此您不能只在幻灯片本身上设置转换。

我最终创建了四个新元素:#previousUpper、#previousLower、#nextUpper 和 #nextLower。我将它们放入一个名为 #transition 的单独容器中,该容器仅在转换正在进行时可见。然后,我为它们提供了正确的尺寸,并使用 background-image: -moz-element(#previous/nextSlide) 和正确的 background-position 将上一张/下一张幻灯片的相应子图像分配给它们。最后,我在这些辅助元素上设置了转换。

不过,它的代码变得相当复杂,因此我将直接引导您到 完成的演示

更多?

我目前对 -moz-element 演示的想法已经用完了,但肯定还有更多可以使用它的东西。现在轮到你了!

鸣谢

这里的大部分功劳应该归于 Robert O’Callahan,他早在 2008 年就完成了 最初的 实现。不过,在他最初的实验之后,他不得不处理更重要的事情,因此他的补丁休眠了一年左右,直到他在 2009 年 7 月 开始了一个新闻组主题来确定正确的 API。不久之后,Ryo Kawaguchi 恢复了 roc 的工作,并在 他在 Mozilla 实习的最后几周 致力于此。一年后,我使补丁准备就绪以供审查,并将其推动到最终阶段直至签入。

mozRequestAnimationFrame 相同的警告适用:-moz-elementdocument.mozSetImageElement 是实验性 API。我们不保证永远支持它们,并且我们不会鼓励网站依赖它们。我们实现了它们,以便人们可以对其进行实验,并且我们可以收集反馈。我们将建议将其作为标准(显然,不带 moz 前缀),并且作者对我们实现的反馈将帮助我们制定更好的标准。

关于 Paul Rouget

Paul 是一位 Firefox 开发人员。

更多 Paul Rouget 的文章…


37 条评论

  1. DesignMango

    太棒了,简直太棒了。

    2010 年 8 月 24 日 14:38

  2. Ken Snyder

    太棒了!这样的创新推动了标准,推动了其他浏览器制造商,并激励了开发者。

    2010 年 8 月 24 日 18:14

  3. foxfan

    您不能将 css 渐变用作反射蒙版的第二个背景图像吗?…… 没事了,只有当从黑色到透明或白色到透明以匹配背景颜色时才会起作用。

    但说到透明渐变。如果您使用关键字“transparent”作为颜色停止点,似乎会得到一个过渡到透明灰色的效果。不太好看。

    附言
    焦急地等待音频和视频的循环属性

    继续努力

    2010 年 8 月 24 日 20:00

    1. Markus Stange

      这是因为“transparent”实际上表示“透明黑色”,因此,当颜色的不透明度过渡到零时,颜色值会过渡到黑色。如果您希望在不透明白色和透明白色之间具有渐变,则需要使用 rgba(255, 255, 255, 0) 而不是“transparent”作为颜色停止点。

      2010 年 8 月 25 日 01:11

  4. narendra

    太棒了!但我正在考虑此功能的安全问题。
    是否可以将 html 页面作为背景图像发布到其他服务器?

    2010 年 8 月 24 日 22:19

  5. Robert O’Callahan

    不可以。

    2010 年 8 月 24 日 23:36

  6. JM

    mozSetImageElement 是否可以接受 HTMLElement 作为其第一个参数?例如,在其中使用 querySelector 会很有用。

    2010 年 8 月 25 日 02:38

    1. Markus Stange

      我不明白。mozSetImageElement 将字符串作为第一个参数(被覆盖的 ID),因此您问题的答案可能是“否”。您具体想要实现什么?

      当然可以执行以下操作
      document.mozSetImageElement(“someid”, document.querySelector(“#nav li.active a”)),但我不确定这是否就是您的意思。

      2010 年 8 月 26 日 02:46

  7. Ric

    很棒的内容。查看示例确实展示了它的强大功能…

    2010 年 8 月 25 日 02:44

  8. Ric

    还有一件事,Prism 是否会在不久的将来更新到最新版本的 Gecko?我喜欢将其用于其 IT 部门不喜欢安装 Firefox 的客户端。

    2010 年 8 月 25 日 02:50

  9. Alexis Deveria

    太棒了!我喜欢它如何为类似的 webkit 属性提供了一种替代解决方案。Robert、Ryo 和 Markus 做得很好。

    一个问题:您是否计划为此编写规范并提交给 CSS 工作组?

    2010 年 8 月 25 日 05:32

    1. Robert O’Callahan

      是的,当然!:-)

      2010 年 8 月 25 日 21:37

  10. Tiago Sá

    我可以看到用户方面存在一个潜在问题。也就是说,基本上,那些不希望用户复制其文本的网站将在其文本之上使用不可点击的文本副本,仅此而已。如果他们之前使用 Flash 做过,这对他们和用户来说都是好事,但如果变得如此简单以至于变得普遍,那就不是那么好了。

    如果我有时间,我会尝试制作一个概念验证,并尝试查看我们方面可能可以做些什么。

    2010 年 8 月 25 日 05:48

    1. Robert O’Callahan

      已经有很多方法可以做到这一点,例如通过在内容上放置一个 DIV 来捕获鼠标点击。

      好消息是,与 Flash 不同,在 Web 上,类似 Greasemonkey 的 hack 始终可以通过少量努力让用户重新掌控。

      2010 年 8 月 25 日 15:00

  11. Andy L

    太棒了!

    2010年8月25日 06:04

  12. dimmaq

    > 花哨的幻灯片切换

    错误:未捕获异常:[异常…“索引或大小为负或大于允许量” 代码:“1” nsresult:“0x80530001 (NS_ERROR_DOM_INDEX_SIZE_ERR)” 位置:“https://hacks.mozilla.ac.cn/wp-content/uploads/2010/08/tada.html 第180行”]

    2010年8月25日 06:27

  13. Danny Moules

    问题:WebGL 支持怎么样?

    2010年8月25日 08:52

    1. Robert O’Callahan

      怎么样?-moz-element() 引用 WebGL canvas 应该可以正常工作。

      2010年8月25日 21:38

  14. Andrés Delfino

    请注意,这对 Flash 不起作用,我相信其他插件也不受支持。

    我想这是设计使然,对吧?

    2010年8月25日 09:51

    1. Robert O’Callahan

      无窗口插件 (wmode=transparent/opaque) 应该可以正常渲染。有窗口的插件无法以这种方式捕获其渲染,这是一个基本的技术限制。

      2010年8月25日 15:01

  15. Matthew Holloway

    好主意!

    我认为必须将其包围在 height:0px 中似乎有点愚蠢……它仍然可供屏幕阅读器使用,因此出于可访问性原因,您不希望前景页面中有背景内容。也许您需要在 -moz-element() 表达式中添加一个标志,例如:

    -moz-element(#myBackground1, display:none);

    2010年8月25日 16:13

    1. Markus Stange

      在 Robert 的博客 [1] 上,Dave Hyatt 建议为该用例添加 display:paint-server。这可能值得一试。

      [1] http://weblogs.mozillazine.org/roc/archives/2008/07/the_latest_feat.html#comments

      2010年8月26日 02:32

  16. Jose

    太棒了!关于反射的一个问题:Webkit 浏览器正在使用 -webkit-box-reflect。这是 -moz-box-reflect 不会出现的迹象吗?是否与 Webkit 开发人员(或其他浏览器)就 -moz-element 进行过任何沟通?如果是这样,他们实施此功能的前景如何?谢谢!

    2010年8月25日 16:38

    1. Robert O’Callahan

      -webkit-box-reflect 更方便,但功能远不如 -moz-element 反射。我喜欢额外的功能,特别是能够对反射应用任意样式的能力。我不确定是否值得为反射提供比 -moz-element 提供的更方便的语法。如果我们添加了小的扩展 -moz-element(self) 来引用带样式的元素,则可以创建如下所示的反射
      .reflect { position:relative; }
      .reflect::after { position:absolute; left:0; top:100%; width:100%; height:100%; transform:scaleY(-1); mask:url(effects.svg#gradientMask); background:element(self); }
      也许还不错。它为您提供了在定位、转换、蒙版、过滤等方面的大量灵活性。

      另一种选择可能是引入 ::reflection CSS 伪元素,这样您就可以编写如下内容
      .reflect::reflection { reflection:bottom; mask:url(effects.svg#gradientMask); }
      我不知道是否值得。我们需要作者的经验来帮助我们指导。

      几年前,当我第一次在我的博客上提出 -moz-element 时,Apple 的 Dave Hyatt 认为这是一个好主意。但从那以后我们就没有和他们谈论过这件事。我们将为此提出规范,看看大家的反应。

      2010年8月25日 21:53

      1. Jose

        谢谢,这一切都说得通。关于 (self) 扩展,我认为这将是锦上添花。

        2010年8月26日 02:06

      2. Stan Rogers

        设计师为什么要与不必要的 SVG 纠缠不清?至少应该可以选择将蒙版设置为简单的渐变。并且此实现会影响文档布局,与 -webkit-box-reflect 不同,因此这两种实现将需要对反射元素设置不同的边距。这让我们离开了渐进增强(如果在浏览器 X 中不起作用,则没有坏处)回到了浏览器嗅探和错误修复的世界。这很糟糕。

        2010年8月31日 16:59

  17. narendra

    只是好奇,是否可以进行递归?
    两个 div 元素相互引用作为背景图像!!

    2010年8月25日 23:25

    1. Markus Stange

      这是不可能的,请参阅“绘画循环”下的部分。

      2010年8月26日 02:39

  18. James Tang

    这真是太棒了,功能也很强大!

    2010年8月26日 00:28

  19. Lachu

    很多人不明白 -moz-element 的理念。在我看来,更令人印象深刻的是 background-size: contain。

    我们可能只会使用 -moz-element 来显示 svg 作为背景,但我们也可以使用 z-order。

    2010年8月26日 01:52

  20. pawel

    太疯狂了,夜间版本支持通过新的元素嵌入 SVG,但不支持 url(circle.svg)。

    此元素将用作背景。

    2010年8月26日 10:01

    1. Robert O’Callahan

      SVG 图像的支持已接近完成,很快就会上线。

      2010年8月26日 14:09

  21. Jason

    我实际上尝试将背景设置为 Flash 对象的 -moz-element。在我尝试过的那个中,它确实减慢了页面和渲染速度。当然,对于大面积使用普通重复模式,这可能是不建议的。

    我可以看到 -moz-element 对单页网站上的导航很有用,其中缩略图显示了跳转的位置。

    我想您还可以尝试使用不同颜色的正方形(例如棋盘格图案)构建简单的几何图案作为背景。

    2010年8月26日 11:30

  22. LG

    在背景中发光并四处飞行的动画文本,使用 text-shadow 使它们看起来很空灵,也许是用户自己的 Twitter 提要可以四处飞舞……再加上在文本框聚焦以回复时对飞行文本进行排序的可能性,以及将鼠标悬停在推文中会将发布推文的用户名添加到文本框中,以便更容易回复?

    所有这些都不使用 Flash、WebGL 或 SVG?
    这甚至可能吗?

    2010年9月1日 15:55

    1. LG

      当然,当您在云端撰写对推文的回复时,您可以在形成回复时看到下一条推文在云端中的预览。

      2010年9月2日 02:35

  23. josh

    现在有哪些网页正在使用它?

    2010年9月3日 18:36

  24. Dan

    我不喜欢它如何鼓励您在页面中放置各种纯粹的多余的装饰性标记。

    2011年3月19日 09:55

本文的评论已关闭。