5

Here my React component demo: https://codesandbox.io/s/epic-brown-osiq1, I am now using the viewBox's values, getBBox and getBoundingClientRect() to realize some calculations in order to position my element. Currently I have entered raw value based on the return the console have provided me from the getBoundingClientRect()'s logs. You can see it on the element I have implemented the getBoundingClientRect() on, namely the <svg>'s root element and the clip-path's text's element. Better but the text is more place tower the center of the screen that really aligned on center of the text's box-you can see the "So"'s word is at the start of the "Food"'s word instead of being aligned on the box's center. So I am at this point currently. Thanks for the feedback.*

note: You will see some comments providing information or parts of my former trials inside the sandbox. What my code does ? concretely I display a clip-path's text with some animated panel travelling the clip-path - this is the color_panel_group's element- giving some dynamic to the composition.There is also a shadow behind the text to give some depth to the composition.

  • Expectation: display a clip-path's text responsively positioned at the vertical and horizontal's centers of the viewport.
  • Problem: My clip-path hides a part of the text and my trials to center the element relative to viewport fails to be fructuous.
  • What I have tried: I have tried to play with the width of the element and the x's positions of the element -mainly text, clip-path, symbol and both case. Even tried to play with the use element by implementing some class in it, but at the end of the day very approximative result outcomed. Also In tspan and symbol I have tried to play with x's attribute, again with very approximative outcomes. I have tried to play with position absolute and a relative container -mainly on the SVG's CSS selector directly-, still with approximative outcomes.

I am wondering what I am missing. Maybe someone can bring some explanation on my code's behavior?

Here my second presentation's resulting code (approximately what React component produces):

body {
  background: orange;
}

svg {
  background: green;
  display: block;
  margin: auto;
  position: relative;
  z-index: 2;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
}

.component {
  min-width: 100vw;
  min-height: 100vh;
  font-size: 100%;
}

.fade_in_background {
  opacity: 1;
  visibility: visible;
  transition: all 1.5s ease-out 0s;
}

.brandtype {
  margin: auto;
  text-align: center;
}

.brandtype_use {
  position: absolute;
  transform: translate(-112.65px, 0)
}

.clipPath_text {
  text-align: center;
}

.color_panel_group {
  padding: 25px;
}

.shape_animation {
  transform-origin: 0;
  transform: scale(0, 1) translate(0, 0);
  animation: moving-panel 3s 1.5s 1 alternate forwards;
}

.shadow {
  transform: translate(10px, 10px)
}

.shape_animation_shadow {
  fill: black;
  fill-opacity: .505;
  transition: all 1.3s ease-out 0.3s;
}

.brandtype {
  font-size: 6.8em;
}

@keyframes moving-panel {
  to {
    transform: scale(1, 1) translate(20px, 0);
  }
}
<div class="component">
  <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 965 657">
    <defs>
      <symbol id="panel_animation" y="0">
        <clipPath class="clipPath_text" id="clipPath_text"><text class="brandtype" word-spacing="-.45em">
            <tspan x="0%" y="50%" dy="1.6em">So</tspan>
            <tspan x="0%" y="50%" dy="3em">Food</tspan>
          </text></clipPath>
        <g class="shadow" clip-path="url(#clipPath_text)">
          <rect class="shape_animation shape_animation_shadow" width="100%" height="100%" x="-25px">
          </rect>
        </g>
        <g class="color_panel_group" clip-path="url(#clipPath_text)">
          <rect class="shape_animation" fill="#F2385A" width="100%" height="100%"></rect>
          <rect class="shape_animation" fill="#F5A503" width="80%" height="100%"></rect>
          <rect class="shape_animation" fill="#E9F1DF" width="60%" height="100%"></rect>
          <rect class="shape_animation" fill="#56D9CD" width="40%" height="100%"></rect>
          <rect id="shape_animation_ref" class="shape_animation" fill="#3AA1BF" width="20%" height="100%" x="-25px">
          </rect>
        </g>
      </symbol>
    </defs>
    <rect width="100%" height="100%" filter="url(#background_light)"></rect>
    <use width="500px" height="100%" x="50%" xlink:href="#panel_animation" class="brandtype_use"></use>
  </svg>
</div>

Thanks for any hint.

Webwoman
  • 10,196
  • 12
  • 43
  • 87
  • Your svg needs a viewBox attribute set in user units not in percents. Also set everything else in user units – enxaneta Nov 14 '19 at 20:01
  • thanks for answering, would you mind to explain the rationale behind your advice please? Also if I set all in user unit I would assume I would lost the benefits of possible responsivity? – Webwoman Nov 14 '19 at 21:24
  • 2
    Percentage values are not allowed in a `viewBox`. They must be unitless numbers. – Paul LeBeau Nov 15 '19 at 05:49
  • Thanks for the answsers, now I have still have my clip-path and centering problem, maybe you have an idea why this behavior occurs? – Webwoman Nov 15 '19 at 11:34
  • So basically your main trouble is horizontally centering two lines of SVG text, right? The clippings, animations, filters and React parts are not important at this moment? – myf Nov 17 '19 at 16:24
  • Do you want The word "So Food" to be in the same line? I mean the two "tspan" on the same line ? if so I can provide you a solution for this. – mostafa tourad Nov 17 '19 at 16:28
  • (I took liberty and made your snippet in answer "runnable" without React. I'm afraid clipping doesn't work in Firefox for some reason. Also, I think you don't need SVG for this effect: just `-webkit-background-clip: text` and animated `background-image:linear-gradient()` and/or `clip:rect()` could suffice.) – myf Nov 17 '19 at 16:32
  • @myf sorry for late answer all, okay, so myf yes and more precisely I want to vertically and horizontally my svg's, also as , you have said it, the React part is not important at this moment effectively. Okay for the note about the SVG's free possibility. I have discovered it with SVG, so that it, and more broadly I am interested to understand how SVG's centering works :) – Webwoman Nov 22 '19 at 16:41
  • @mostafatourad I would two differents line, I would be able to center with several Tspan and eventually with inline text, in all case to be more precise. That said on my precise case, it is about two different line here. – Webwoman Nov 22 '19 at 16:43

1 Answers1

6

Text alignment in SVG does not work the way we are used to from HTML and CSS where everything is box with some dimensions and we can apply e.g. text-align: center.

In <svg:text> the starting coordinates define point from which will text line expand. text-anchor attribute controls which direction this expansion will occur: center value means it will expand both ways so the initial anchor point will be in the middle of bounding box width (for horizontal writing systems). See excellent answer illustrating this text-anchor as the best mean for centering text in SVG: https://stackoverflow.com/a/23272367/540955. Also, there is no CSS position properties (left/top) inside SVG, only x/y coordinates, nor margins and rest of box-model as you know it in HTML.

So in your code adding text-anchor="middle" and moving the x coordinates further right would produce centered text. I'd advise to use bare <text> elements as opposed to <tspan>s, because shifting them with dx/dy is relative to the last preceding character and this character could be some white space from parent <text> (depending on code formatting) what would produce unbalanced centering. Also for easier calculations dominant-baseline="central" (or just middle for horizontal writing systems) is useful, because it moves the anchor point from the base line to "center line". So using dy attribute (as you already do) to move the first line "one half" of line-height up and the other down should do the trick:

<svg viewBox="0 0 800 200" text-anchor="middle" dominant-baseline="central" font-size="100">
 <!-- Outline and diagonals with center point for illustration: -->
 <path d="M0,0h800v200h-800zl800,200m0 -200L0,200" fill="#FC9" stroke-width="1" stroke="rgba(0,0,0,0.3)"></path>
 <circle cx="50%" cy="50%" r="10" fill="red"></circle>
 <!-- Centered text: -->
 <text x="50%" y="50%" fill="rgba(0,0,0,0.3)">So</text>
 <!-- Shifted up and down: -->
 <text x="50%" y="50%" dy="-0.5em">So</text>
 <text x="50%" y="50%" dy="+0.5em">Food</text>
</svg>

(Not entirely related: the clipping could be done in CSS only with background-clip: text; here is rough variation of your design as it appears in Chrome browser, with animated text background, but without shadows. Unfortunately adding shadows would require more elements or attributes, I think. This should work in any browser supporting background-clip.)

div {
 display: flex;
 flex-direction: column;
 align-items: center;
 font-size: 30vh;
 line-height: 30vh;
 font-weight: bold;
 font-family: Impact;
}
span {
 color: #fff;
 background-color: #000;
 width: 100%;
 text-align: center;
}
@supports (-webkit-text-fill-color: transparent) and (-webkit-background-clip: text) {
 span {
  -webkit-text-fill-color: transparent;
  -webkit-background-clip: text;
  animation: 2s wohoo infinite alternate cubic-bezier(1,0,1,1);
  background-position: 0 0;
  background-size: 100% 100%;
  background-color: transparent;
  background-image: linear-gradient(
   to right,
   #f2385a 0,
   #f2385a 20%,
   #f5a503 0,
   #f5a503 40%,
   #e9f1df 0,
   #e9f1df 60%,
   #56d9cd 0,
   #56d9cd 80%,
   #3aa1bf 0,
   #3aa1bf 100%
  );
  background-repeat: no-repeat;
  transform-origin: center center;
 }
}

@keyframes wohoo {
 from {
  background-size: 0 100%;
  background-position: -5vh 0;
  transform: scale(0.7);
 }
 50% {
  transform: scale(0.7);
 }
 90% {
  transform: scale(0.9);
 }
 to {
  background-size: 500% 100%;
  background-position: 0vh 0;
  transform: scale(0.9) 
 }
}

html,body{margin:0;overflow:hidden;}
body {
 background-color: #1d1f20;
 color: snow;
 display: flex;
 flex-direction: row;
 justify-content: center;
 width: 100%;
}
<div>
 <span>Baz</span>
 <span>Gazonk</span>
 <span>Qux</span>
</div>
myf
  • 9,874
  • 2
  • 37
  • 49
  • there is no CSS position properties (left/top) inside SVG, only x/y coordinates {...} => thanks a lot, very important to be fully aware of that fact indeed. {...} and this character could be some white space from parent , (depending on code formatting) what would produce unbalanced centering => when would you advice to use tspan relatively for example about centering element or svg formatting please? {...} dominant-baseline="central" (or just middle for horizontal writing systems) is useful {...} => I have edited a codepen: https://codepen.io/joondoe/pen/oNNOppp, thanks to MDN. – Webwoman Nov 22 '19 at 17:02
  • Thanks for your answer, it provide great insight about how handle this situation, a last question please,as far as you know is there any other element the community would be aware of? – Webwoman Nov 24 '19 at 17:43