将<img/>替换为视频

时间:2019-03-18 08:48:24

标签: javascript html5 svg

我有一个SVG,它具有如下简化的结构:

 <svg viewbox="0 0 1000 2000">
   <image width="1024" height="2048" transform="<matrix A>" … />
 </svg>

我想用html5 <video>元素替换图像,像这样使用<foreignObject>

<svg viewbox="0 0 1000 2000">
   <g transform="<matrix A>">
     <foreignObject x="0" y="0" width="100%" height="100%">
       <video style="display:block; width: 100%; height: 100%;">
         <source src="…" type="…">
        </video>
     </foreignObject>
   </g>
   <!--
     <image width="1024" height="2048" transform="<matrix in here>" … />
   -->
 </svg>

因此我有以下代码:

 [...document.querySelectorAll('image')].forEach(img => {
   const 
     g     = document.createElementNS('…', 'g'),
     fo    = g.appendChild(document.createElementNS('…', 'foreignObject')),
     video = fo.appendChild(document.createElement('video')),
     src   = video.appendChild(document.createElement('source'))
   ;

   g.setAttribute('transform', img.getAttribute('transform'));

   fo.setAttribute('x', 0);
   fo.setAttribute('y', 0);
   fo.setAttribute('width', '100%');
   fo.setAttribute('height', '100%');

   video.style.cssText = `
     position: absolute;
     top: 100px;
     left: 100px;
     background-color: #00ff00;
   `

   video.setAttribute('width', 100);
   video.setAttribute('height', 100);

   img.parentNode.appendChild(g);
   img.parentNode.removeChild(img);
 });

到目前为止,基本上一切正常,但是我无法使<video>出现在与图像大小相同的位置。规范说:

(I)

  

[…]包含的外部图形内容需要进行SVG转换,过滤器,剪切,遮罩和合成[…]

(II)

  

HTML解析器将'foreignObject'中的元素等同于HTML文档片段中的元素。 […]

考虑(I):通过用<g>替换图像,该图像复制了图像中的所有变换,视频应“位于坐标空间中”,而不是“

考虑到(II)<video>应该成为新文档片段的根节点,并适合给定区域。

但是结果看起来像这样:

enter image description here

其中:黑色区域代表整个SVG,白色区域代表整个HTML文档,其中包括SVG。这两个绿色区域/都将成为视频,而较大的区域就是我想要的视频。

为什么/我想念什么?

更新

由于这太过分了,我创建了这个基本的svg:

<svg 
  width="200"
  height="200"
  viewBox="0 0 200 200" 
  preserveAspectRatio="xMidYMid meet"
  xmlns="http://www.w3.org/2000/svg">
  <defs>
    <clipPath id="clip">
      <circle cx="100" cy="100" r="100" />
    </clipPath>  
  </defs>
  <rect 
    x="0" 
    y="0" 
    width="200" 
    height="200" 
    style="fill:#ff0000;stroke:none" 
    clip-path="url(#clip)" />

  <g clip-path="url(#clip)">
    <foreignObject x="0" y="0" width="200" height="200" >
      <video 
        autoplay="" 
        width="100%" height="100%">
        <source src="/path/to/a/video.mp4" type="video/mp4" />
      </video>
    </foreignObject>
  </g>
  <text x="100" y="100">Hello</text>
</svg>

什么在Firefox中完美运行,但在Chromium中却无法正常运行。我可以通过在视频中添加style="clip-path: url(#clip)"来裁剪视频,但是<text>并没有出现在视频的前面(铬)。

考虑视频的坐标,这可行:

const 
  { top, 
    left, 
     width, 
     height } = foreignObject.getBoundingClientRect()
  ;


Object.entries({ width, height}).forEach(
  ([k,v]) => video.setAttribute(k,v)
);

video.style.left = `${left}px`;
video.style.top = `${top}px`;

1 个答案:

答案 0 :(得分:-1)

您的视口是1000x2000,但图像是1024x2048。我认为这会剪辑您的图像。可能是混乱的一部分。

下一步:由于您通常希望保留视频的纵横比,因此图像比视频更自由地缩放。这就是为什么视频大小不适合容器时会出现黑色边框的原因。确保视频大小,图像大小和SVG视口都一致。

我也有点怀疑浏览器可以将所有SVG转换应用于视频。至少在过去,浏览器不会自己渲染视频,而是在页面中保留一个矩形区域,并将该区域的坐标发送到视频渲染器(可以使用覆盖窗口在另一个过程中进行)。如果无法正常使用,请尝试用视频替换整个SVG。

此外,不要在JavaScript中执行此操作,而是构建一个小的本地HTML文件,您可以快速对其进行调整,直到视频看起来正确为止。当它起作用时,然后从可以在两者之间转换的代码开始。否则,您将打开太多的蠕虫罐头。

[更新1]

欢迎来到地狱。待一会儿...

在这种情况下,您可以尝试使用开发人员工具找出Chromium正在做什么。也许创建第二个本地测试用例,您尝试在视频中放置一些文本的<div>。如果这不起作用,则Chromium正在使用此类视频的外部渲染器。

这在屏幕上看起来相同,但在幕后,Chromium正在要求另一个进程在屏幕上放置另一个窗口,以使其看起来像是页面的一部分。如果您拥有一种可以在屏幕上显示所有应用程序窗口坐标的工具,则可以看到浏览器窗口上方有一个工具。点击进入该窗口,Chromium看不到它们,也无法在该窗口之前呈现任何内容,因为它是其他人的窗口(就像您不希望Chromium在恰好是在浏览器窗口中打开。)

选项:

  1. 转换为Chromium可以自行渲染的格式。有关列表,请参见https://www.chromium.org/audio-video。那可能只是VP8,VP9和Theora(=任何“开放”视频格式)。 H.264和MPEG-4可能无法正常工作,因为电影制片厂担心您会窃取它们的宝贵资源。
  2. 在有效的地方使用文字叠加层,然后在无效的视频下方移动文字。
  3. 在提供视频文件的服务器上使用一个进程将文本标记到视频中,然后再传递给浏览器。某些视频格式支持字幕,如果您不需要太高级的话,可以将其滥用。这样可以避免对视频进行重新编码。
  4. 显示文本,并要求访问者在其工作位置安装浏览器。