Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I center my clip-path's text relatively to the viewport and display all the clip-path's text?

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.

like image 605
Webwoman Avatar asked Nov 14 '19 17:11

Webwoman


1 Answers

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>
like image 145
myf Avatar answered Oct 03 '22 22:10

myf