I have an SVG which has a simplified structure like that:
<svg viewbox="0 0 1000 2000">
<image width="1024" height="2048" transform="<matrix A>" … />
</svg>
And I want to replace the image with a html5 <video> element, usign the <foreignObject> like so:
<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>
Therefore I have this code:
[...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);
});
Basically everything works fine so far, but I cannot make the <video> appear at the same location an size as the image. The specs say:
(I)
[…] The included foreign graphical content is subject to SVG transformations, filters, clipping, masking and compositing […]
(II)
The HTML parser treats elements inside the ‘foreignObject’ equivalent to elements inside an HTML document fragment. […]
Taking (I) into account: By replacing the image with a <g> which has all the transformations copied over from the image, the video should »live in the coordinate space« than.
Taking (II) into account, the <video> should become the root node of a new Document Fragment, and fit into the given area.
But the result looks like that:
Whereby: The Black Area represents the entire SVG, the white Area the overall HTML document, which includes the the SVG. Both green areas are/seam to be the video, whereby the larger one is the one I would like to have the video.
Why is that/What am I missing?
UPDATE
Since this went too far I created this basic 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>
What works perfectly in Firefox, but not in Chromium. I can manage to get the video cropped by adding style="clip-path: url(#clip)" to the video, but the <text> does not appear in front of the video, in chromium.
Considering the coordinates of the video, this works:
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`;
Your viewport is 1000x2000 but the image is 1024x2048. I think that will clip your image. Might be part of the confusion.
Next: Images scale more freely than videos since you usually want to preserve the aspect ration of the video. That's why you get black borders when the video size doesn't fit the container. Make sure that the video size, the image size and the SVG viewport all agree.
I'm also a bit skeptical that a browser can apply all SVG transformations to video. At least in the past, browsers wouldn't render video themselves but reserve a rectangular area in the page and send the coordinates of this area to a video renderer (which could be in another process using an overlay window). If you can't get this to work, try to replace the whole SVG with the video.
Also, instead of doing this in JavaScript, build a small local HTML file that you can quickly tweak until the video looks correct. When it works, then start with code that can transform between the two. Otherwise, you're opening too many cans of worms.
[Update 1]
Welcome to Hell. Stay a while ...
You can try to use the developer tools to find out what Chromium is doing in this case. Maybe create a second local test case where you try to position a <div> with some text over the video. If that doesn't work, then Chromium is using an external renderer for this type of video.
This looks the same on the screen but behind the scenes, Chromium is asking another process to position another window on the screen in such a way that it looks as if it was part of the page. If you have a tool which can show you the coordinates of all application windows on the screen, you can see that there is one which stays on top of your browser window. Clicks go to that Window, Chromium can't see them and it can't render anything in front of that window because it's a window of someone else (just like you wouldn't want Chromium to render over a text editor that happens to be open over the browser window).
Options:
Convert to a format which Chromium can render by itself. See https://www.chromium.org/audio-video for a list. That's probably only VP8, VP9 and Theora (= any "open" video format). H.264 and MPEG-4 might not work because film studios panic that you could steal their precious.
Use text overlay where it works and move the text below the video where it doesn't.
Use a process on the server serving the video files to stamp the text into the video before passing it to the browser. Some video formats have support for subtitles which can be abused for this if you don't need anything too fancy. This would prevent the need to reencode the video.
Display a text and ask visitors to install a browser where it works.
I am drawing a simple two rectangle svg as follows:
<svg width="72px" height="72px" viewBox="0 0 72 72" xmlns="http://www.w3.org/2000/svg">
<rect stroke-dashoffset="0" x="2.4px" y="2.4px"></rect>
<rect ng-class="$ctrl.expiryClass"
x="2.4px" y="2.4px"
stroke-dasharray={{$ctrl.dashlength}}
stroke-dashoffset={{$ctrl.offset}}></rect>
</svg>
This works well in chrome and looks like this:
However, the svg is not showing up in FireFox and I am only seeing the purple 2h box.
Any idea whats going on?
I expect you are setting the rectangle's width and height using CSS. Correct?
If so, that's an SVG 2 thing that currently only works in Chrome. You'll need to use regular width and height attributes if you want this to be cross-browser compatible.
<rect stroke-dashoffset="0" x="2.4px" y="2.4px" width="100px" height="100px"/>
I got some moveable items in a svg-element as shown here:
JSfiddle: http://jsfiddle.net/xfvf4/37/
<svg xmlns="http://www.w3.org/2000/svg" height="300" width="300" version="1.1"></div>
As you can see, the items can be moved to the edge of the svg. I tried to achieve that the svg gets bigger as moving an element to the edge. i.e. if you move a rechtangle to an area near an edge (i.e. 20px), the svg gets bigger as you are moving the item.
I am trying to dynamically create SVG vectors with Javascript and I'm running into some problems.
I create the svg markup in javascript with document.createElement and append it to a div container. Then I do the same thing and create a rect and add it as a child to the svg id.
When I look at the DOM, I can see the elements being added, and their properties are such:
<svg id="gdc_container" xmlns="http://www.w3.org/2000/svg" version="1.1" width="500" height="300">
<rect id="gdc_background" width="500" height="300" style="rgb(0,0,255)"></rect>
</svg>
Everything looks fine, however the graphics are not showing up. When I use Chromes inspector, I can see the svg and rect elements, but the chrome tooltip shows their size as [ 0 x 0 ].
Anyone have any ideas?
Did you try using createElementNS? (developer.mozilla.org/en/DOM/document.createElementNS)
For some examples look at http://www.kevlindev.com/tutorials/basics/shapes/js_dom/index.htm.
I'm trying to create (what I thought would be!) a simple re-usable bit of SVG to show three lines of text, with a background colour - to simulate a 'post-it' note.
I have found some useful code here to get the Bounds of the Text http://my.opera.com/MacDev_ed/blog/2009/01/21/getting-boundingbox-of-svg-elements which I am using.
So: I'm creating an group of text elements like this in the 'defs' section of my SVG:
<svg id="canvas" width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="post_it">
<text x="0" y="30" id="heading" class="heading">My Heading</text>
<text x="0" y="45" id="description" class="description">This will contain the description</text>
<text x="0" y="60" id="company" class="company">Very Big Company Ltd.</text>
</g>
And I'm displaying the text with a 'use' element like this:
<use id="12345" class="postit" xlink:href="#post_it" onclick="showId(this);"/>
I'm using the onclick to trigger a call to the following javascript function (defined in 'defs' section):
function showId(elem) {
post_it_rect=getBBoxAsRectElement(elem);
document.getElementById('canvas').appendChild(post_it_rect);
}
(The 'getBBoxAsRectElement(elem)' is from the link I posted).
As this stands; this works just fine - however if I change my 'use' element to position the text in a different place like this:
<use x="100" y="100" id="12345" class="postit" xlink:href="#post_it" onclick="showId(this);"/>
Now, the text displays in the correct place, but the resultant 'background-color' (actually a 'rect' element with opacity of 0.5) still shows on the top-left of the svg canvass - and the function used to calculate the rect is returning '-2' rather than '100' ('-98'?) as I need (I think).
What do I need to do to line up the 'rect' elements and the text elements ?
The author of the (very helpful article btw) script provides a more advanced script to draw a box round any 'bb' in an SVG, but I couldn't get this to work (missing 'transform' functions?).
I'm using Firefox 7.x to render the SVG ; and I'm loading a .svg file (ie, not embedded in html etc) straight from disk to test this).
Yes, you may need to compensate yourself for the x and y attributes on the <use> element for the time being, I'll try to find some time to update the blogpost and script.
Here's a draft SVG 1.1 test that among other things checks that the effect of the x and y attributes are included in the bbox. The line starting [myUse] is the one that tests this case, if it's red then that subtest failed. Chromium and Opera Next both pass that subtest, while Firefox nightly and IE9 doesn't. Note that the test itself has not gone through full review yet, and that it may still change.