Issue
Let's say I have a div in which there are two SVG elements : svgPlan and svgIcon (which is an SVG Image element).
svgPlan:
svgIcon:
A set of transformations (perspective, rotateX, scale, and translate) are applied on the parent element (svgPlan) :
svg.style('transform', 'perspective(30em) rotateX(33deg) scale(1.7) translate(0%, -6%)');
svgPlan after transformation:
I want to display the svgIcon inside the svgPlan.
THE PROBLEM : the transformations are applied on both the parent element svgPlan and the child svgIcon. It seems that the child is automaticlly inherting the styles applied on the parent and this is not what I want. I want the icon to appear whitout any effect.
THE QUESTION : How can I Disinherit my child element from father's style (which I beleive is not possible on SVG at the moment), or apply an Inverse Transformation to make the Icon appear without any perspective or style ?
THE MINIMAL REPRODUCTIBLE EXAMPLE :
<div>
<svg id="svgPlan" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 2118.5 624.2" enable-background="new 0 0 2118.5 624.2" xml:space="preserve"
style="transform:perspective(30em) rotateX(30deg) scale(1.17)">
<rect width="1200" height="400" style="fill:rgb(224,224,224);stroke-width:3;stroke:rgb(0,0,0)" />
<g id="svgIcon">
<image x="550" y="120" width="120" height="120" xlink:href="https://svgshare.com/i/npz.svg">
</image>
</g>
</svg>
</div>
Thank you for your help.
Solution
This answer proposes solution that is not very related to SVG, instead it uses more general approach where perspective transforms from CSS are applied exclusively to HTML elements - some of those may be or may contain SVG images.
First some trivia:
- SVG has no third dimension ("z-axis"), so dimensional transforms or perspective are not possible there. (It hasn't even
z-index
and layers are just denoted the source order.) SVG in its nature really is just two dimensional vector graphic. - SVG maps many of CSS style properties to its presentational attributes, but since there is no
rotate3d
,translate3d
norrotatex()
in SVG, they have no effect. - CSS in HTML has those.
- In your snippet you are applying CSS transform to a HTML node that is by definition just a flat rectangle, like everything in the page. That node in your case is a SVG element, but it could be a
<div>
with background image or just HTML<img>
- in all cases those are just "viewport" rectangles showing (SVG) content within infinite (possibly transformed) plane. - So you cannot "pop something out of it"; you can only manipulate "planes" constituted by HTML elements.
As I understand it, your use-case is to display marker icons like "sprites" (that always face a camera) above some tilted image (like "map"). From your questions is not clear whether distant icons (those farther from the camera) should be smaller and overlapped by closer ones (what feels more natural, but contradicts that "un-inheriting" stance) or be same size each, with undefined overlapping strategy (what matches your formulation). Let's assume the former.
In "HTML" world you can nest transformed elements and let the children move with parent (with help of transform-style: preserve-3d;
) to get them to desired place, and then apply negated transform(s) to their children to make those camera-facing again.
Let's start with baseline pure SVG flat image - "map" overview with two "markers" without transforms:
<svg xmlns='http://www.w3.org/2000/svg' viewBox="0 0 800 400"
stroke-width="10" stroke="black" fill="none">
<g transform="translate(100 100)">
<rect width="500" height="200" fill="silver"></rect>
<path d="M 0 200 L 250 100 L 310 30 L 500 200" stroke="white"></path>
<g transform="translate(250 100)">
<circle r="20" fill="darkturquoise"></circle>
<image width="80" height="100" transform="translate(-40 -100)" preserveAspectRatio="none" href="data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-200 -200 400 516' stroke='black' stroke-width='20'%3E%3Cpath fill='red' d='M187-14c-15-232-359-232-374 0l-1 23c0 131 77 244 188 297A329 329 0 0 0 187-14z'/%3E%3Ccircle fill='darkturquoise' r='127'/%3E%3Ctext font-size='150' text-anchor='middle' stroke='none' %3Ea%3C/text%3E%3C/svg%3E"
></image>
</g>
<g transform="translate(310 30)">
<circle r="20" fill="red"></circle>
<image width="80" height="100" transform="translate(-40 -100)" preserveAspectRatio="none" href="data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-200 -200 400 516' stroke='black' stroke-width='20'%3E%3Cpath fill='darkturquoise' d='M187-14c-15-232-359-232-374 0l-1 23c0 131 77 244 188 297A329 329 0 0 0 187-14z'/%3E%3Ccircle fill='red' r='127'/%3E%3Ctext font-size='150' text-anchor='middle' stroke='none' %3Eb%3C/text%3E%3C/svg%3E"
></image>
</g>
</g>
</svg>
(I've extracted image icons to simplified dataURIs and changed positioning to group transforms for better reusability and independence.)
Now let's introduce the slope and icon "sprites".
- SVG is now without icons and icons are separate
<img>
. - Each
<img>
is positioned and transformed to corresponding location on the map. Custom properties here help with reusability, but static values could be hardcoded as well. - Custom property
--slope
andcalc
also ease "automation" and allows alterations initiated from the range input.
.scene {
position: fixed;
width: 500px;
height: 200px;
margin: 50px 10px;
transform-origin: center center;
transform-style: preserve-3d; /* this is important */
--rotx-positive: calc( var(--slope, 30) * 1deg );
--rotx-negative: calc( var(--rotx-positive) * -1 );
transform:
perspective(5em)
/* slope: */
rotateX(var(--rotx-positive));
}
img {
position: absolute;
top: calc(1px * var(--y));
left: calc(1px * var(--x));
transform:
/* to have the bottom center peak touching the POI: */
translate(-50%, -100%)
/* negate the slope: */
rotatex( var(--rotx-negative) );
transform-origin: bottom center;
}
Slope:
<input type="range" value="30" min="0" max="90" value="0"
oninput="s.style.setProperty('--slope', this.value);o.value=this.value">
<output id="o">30</output>°.
<div class="scene" id="s">
<svg xmlns='http://www.w3.org/2000/svg' viewBox="0 0 500 200"
stroke-width="10" stroke="black" fill="none">
<rect width="500" height="200" fill="silver"></rect>
<path d="M 0 200 L 250 100 L 310 30 L 500 200" stroke="white"></path>
<g transform="translate(250 100)">
<circle r="20" fill="darkturquoise"></circle>
</g>
<g transform="translate(310 30)">
<circle r="20" fill="red"></circle>
</g>
</svg>
<img style="--x: 250; --y: 100;" width="80" height="100" src="data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-200 -200 400 516' stroke='black' stroke-width='20'%3E%3Cpath fill='red' d='M187-14c-15-232-359-232-374 0l-1 23c0 131 77 244 188 297A329 329 0 0 0 187-14z'/%3E%3Ccircle fill='darkturquoise' r='127'/%3E%3Ctext font-size='150' text-anchor='middle' stroke='none' %3Ea%3C/text%3E%3C/svg%3E">
<img style="--x: 310; --y: 30;" width="80" height="100" src="data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-200 -200 400 516' stroke='black' stroke-width='20'%3E%3Cpath fill='darkturquoise' d='M187-14c-15-232-359-232-374 0l-1 23c0 131 77 244 188 297A329 329 0 0 0 187-14z'/%3E%3Ccircle fill='red' r='127'/%3E%3Ctext font-size='150' text-anchor='middle' stroke='none' %3Eb%3C/text%3E%3C/svg%3E">
</div>
For single 3D transform this fits; for multiple simultaneous transforms either more maths should have be involved or more nested wrappers for transformations. See example of nested transform layers producing sprite-like "dots" in 3d space in pen using this technique.
Answered By - myf Answer Checked By - David Goodson (PHPFixing Volunteer)
0 Comments:
Post a Comment
Note: Only a member of this blog may post a comment.