• Das Erstellen neuer Accounts wurde ausgesetzt. Bei berechtigtem Interesse bitte Kontaktaufnahme über die üblichen Wege. Beste Grüße der Admin

[FRAGE] Text krümmen mit CSS transform

mikdoe

Moderator
Frage steht quasi schon im Betreff. Wie kann ich mit CSS transform ein Wort aus sagen wir mal 13 Buchstaben mittig nach oben krümmen? Hab schon einiges gegoogelt und auch eine Menge Spielereien gefunden aber noch keine Vorlage zum Text krümmen. Muss man das mit diesem transform matrix Attribut machen oder weiß da jemand mehr?
 
Mit transform funktioniert das nicht, da das nur lineare Transformation und Translation kann. Für eine Biegung brauchst du mindestens eine Transformation zweiter Ordnung, was CSS nicht kann.

Für eine ganz normale Krümmung musst du jeden Buchstaben einzeln positionieren... oder JS bemühen:
Code:
<!DOCTYPE html>

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Fenstertitel</title>
<style type="text/css">
#round {
	position: relative;
	display: inline-block;
	white-space: nowrap;
	border: 1px dashed blue;
	font-size: 4em;
	text-align: center;
}
#round .char {
	position: relative;
	display: inline-block;
	transform-origin: 50% 100%;
	border: 1px dotted red;
}
#round, #round .char {
	border-width: 0px;
}
</style>
</head>
<body>
<span id="round">Das ist mein krummer Text.</span>
<script type="text/javascript" src="http://kkjs.kkapsner.de/modules/kkjs.load.js"></script>
<script type="text/javascript">
(function(){
	var round = kkjs.$("round");
	var text = round.textContent;
	round.innerHTML = "";
	var textNodes = text.replace(/\s/, " ").split("").map(function(char){
		return kkjs.node.create({
			tag: "span",
			className: "char",
			childNodes: [char === " "? "\xA0" :char]
		});
	});
	textNodes.forEach(function(node){
		round.appendChild(node);
	});
	
	var width = round.clientWidth;
	var angleRange = Math.PI / 4;
	var radius = width * 4 / Math.PI;
	var offset = {
		x: radius * Math.sin(angleRange / 2),
		y: radius
	};
	
	textNodes.forEach(function(node){
		var xBend = node.offsetLeft + node.offsetWidth / 2 - width / 2;
		var angle = -(Math.PI + angleRange * xBend / width);
		console.log(angle);
		kkjs.css.set(node, {
			rotation: kkjs.sprintf("%fdeg", 180 - angle * 180 / Math.PI),
			top: (offset.y + radius * Math.cos(angle)),
			left: offset.x + radius * Math.sin(angle) - node.offsetLeft - node.offsetWidth / 2
		});
	});
	
	kkjs.css.set(round, {
		paddingBottom: (radius - radius * Math.cos(angleRange / 2)) + "px",
		paddingRight: (round.clientHeight * Math.sin(angleRange / 2)) + "px",
		paddingLeft: (round.clientHeight * Math.sin(angleRange / 2)) + "px"
	});
}());
</script>
</body>
</html>
 
Wie geil ist das denn? Ich war auch fast dran. Schau mal:
gekrümmter.png
Und ich hab genau gemacht, was du jetzt auch gesagt hast, mit JS jeden einzelnen Buchstaben transformieren.
Hätte nur noch das Höherstellen der Buchstaben in Stufen machen müssen.

Jetzt ist noch meine Frage, ob du mir das in pures JS oder jQuery übersetzen würdest?
 
Ist jetzt nicht so schwer...
Code:
<!DOCTYPE html>

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Fenstertitel</title>
<style type="text/css">
#round {
	position: relative;
	display: inline-block;
	white-space: nowrap;
	border: 1px dashed blue;
	font-size: 4em;
	text-align: center;
}
#round .char {
	position: relative;
	display: inline-block;
	transform-origin: 50% 100%;
	border: 1px dotted red;
}
#round, #round .char {
	border-width: 0px;
}
</style>
</head>
<body>
<span id="round">Das ist mein krummer Text.</span>
<script type="text/javascript">
(function(){
	var round = document.getElementById("round");
	var text = round.textContent;
	round.innerHTML = "";
	var textNodes = text.replace(/\s/, " ").split("").map(function(char){
		var charNode = document.createElement("span");
		charNode.className = "char";
		charNode.appendChild(document.createTextNode(char === " "? "\xA0" :char));
		return charNode;
	});
	textNodes.forEach(function(node){
		round.appendChild(node);
	});
	
	var width = round.clientWidth;
	var angleRange = Math.PI / 4;
	var radius = width * 4 / Math.PI;
	var offset = {
		x: radius * Math.sin(angleRange / 2),
		y: radius
	};
	
	textNodes.forEach(function(node){
		var xBend = node.offsetLeft + node.offsetWidth / 2 - width / 2;
		var angle = -(Math.PI + angleRange * xBend / width);
		console.log(angle);
		node.style.transform = "rotate(" + (180 - angle * 180 / Math.PI) + "deg)";
		node.style.top = (offset.y + radius * Math.cos(angle)) + "px";
		node.style.left = (offset.x + radius * Math.sin(angle) - node.offsetLeft - node.offsetWidth / 2) + "px";
	});
	
	round.style.paddingBottom = (radius - radius * Math.cos(angleRange / 2)) + "px";
	round.style.paddingRight = (round.clientHeight * Math.sin(angleRange / 2)) + "px";
	round.style.paddingLeft = (round.clientHeight * Math.sin(angleRange / 2)) + "px";
}());
</script>
</body>
</html>
- funktioniert aber jetzt natürlich nur noch in den neueren Browsern...
 
Danke, Korbinian! Komme bisher damit zurecht. Vorab Fragen zum CSS oben:
border: 1px dotted red; und border: 1px dashed blue; ziehen bei mir weder im IE noch im FF, wofür sind die?
wann kommt #round, #round .char {} zum Tragen bzw. wofür ist es gedacht?

Nachtrag: Hab den Code mal "verbaut". Aber an einer entscheidenden Stelle komme ich nicht recht weiter.
Die Frage lautet wie ich für var schriftgroesse = ? ermitteln kann, wie groß die Zeichen sein müssen, damit sie in den Kreis passen?
Die Schrift soll am Ende so aussehen wie hier: https://de.fotolia.com/id/39516747

Mein Code bisher:
HTML:
<!DOCTYPE HTML> 
<html> 
	<head> 
		<title>Test Siegel</title> 
		<style type="text/css">
.stempel_aussen{
	position:			relative;
	display:			inline-block;
	white-space:		nowrap;
	color:				red;
	font-weight:		bold;
}
.stempel_zeichen{
	position:			relative;
	display:			inline-block;
	transform-origin:	50% 100%;
}
.stempel_kreis{
	background-color:	lightgreen;
	position:			relative;
	border:				1px solid black;
}
		</style>

		<script>
function stempel(ref,text,groesse,einheit) {
	var ziel = (typeof ref == 'string' ? document.getElementById(ref) : ref);
	var size = parseInt(groesse,10);
	var schriftgroesse = size;	//  <-- wie kann ich errechnen, wie breit und hoch die Zeichen sein müssen, damit sie in den Kreis passen?

	// Kreis malen
	var outer_round = document.createElement('div');
	outer_round.className = 'stempel_kreis';
	
	outer_round.style.borderRadius = parseInt(size / 2,10) + einheit;
	outer_round.style.width = size + einheit;
	outer_round.style.height = size + einheit;
	outer_round.style.lineHeight = (size * 2) + einheit;
	ziel.appendChild(outer_round);

	var round = document.createElement('div');
	outer_round.appendChild(round);
	round.className = 'stempel_aussen';
	round.style.fontSize = schriftgroesse + einheit;
	var textNodes = text.replace(/\s/, " ").split("").map(function(char){
		var charNode = document.createElement("span");
		charNode.className = 'stempel_zeichen';
		charNode.style.fontSize = schriftgroesse + einheit;
		charNode.appendChild(document.createTextNode(char === " "? "\xA0" :char));
		return charNode;
	});
	textNodes.forEach(function(node){
		round.appendChild(node);
	});
	
	var width = round.clientWidth;
	var angleRange = Math.PI / 4;
	var radius = width * 4 / Math.PI;
	var offset = {
		x: radius * Math.sin(angleRange / 2),
		y: radius
	};
	
	textNodes.forEach(function(node){
		var xBend = node.offsetLeft + node.offsetWidth / 2 - width / 2;
		var angle = -(Math.PI + angleRange * xBend / width);
		console.log(angle);
		node.style.transform = "rotate(" + (180 - angle * 180 / Math.PI) + "deg)";
		node.style.top = (offset.y + radius * Math.cos(angle)) + einheit;
		node.style.left = (offset.x + radius * Math.sin(angle) - node.offsetLeft - node.offsetWidth / 2) + einheit;
	});
	
	round.style.paddingBottom = (radius - radius * Math.cos(angleRange / 2)) + einheit;
	round.style.paddingRight = (round.clientHeight * Math.sin(angleRange / 2)) + einheit;
	round.style.paddingLeft = (round.clientHeight * Math.sin(angleRange / 2)) + einheit;
}
		</script>

	</head>
	<body>
		<div><a href="http://forum.jswelt.de/editpost.php?do=updatepost&postid=392162">Forum Thread</a></div>

		<hr>

		<div>
			Text davor<br>
			Text davor<br>
			Text davor<br>
			Text davor<br>
			Text davor<br>
		</div>

		<hr>

		<div id="stempeldiv"></div>

		<hr>

		<div>
			Text danach<br>
			Text danach<br>
			Text danach<br>
			Text danach<br>
			Text danach<br>
		</div>

		<script>
			stempel('stempeldiv','TEST',50,'px');
		</script>
	</body>
</html>
 
Die waren nur für die Entwicklung. Werden wegen dem border-width: 0px; nicht angezeigt. Nur deswegen ist der andere von dir erwähnte Selektor gut.

Ich hab' mir den Code nochmal genauer angesehen und gemerkt, dass der für deine Zwecke nicht perfekt geeignet ist und auch ein paar komische achen enthält:
Code:
<!DOCTYPE html>

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Fenstertitel</title>
<style type="text/css">
#round {
	position: relative;
	display: inline-block;
	white-space: nowrap;
	border: 1px solid blue;
	font-size: 2em;
	text-align: center;
	height: 0px;
	width: 0px;
	padding: 200px;
	border-radius: 100%;
}
#round .char {
	position: absolute;
	display: inline-block;
	transform-origin: 50% 0%;
	border: 0px solid red;
}
</style>
</head>
<body>
<span id="round">Das ist mein krummer Text.</span>
<script type="text/javascript">
(function(){
	var round = document.getElementById("round");
	var text = round.textContent;
	round.innerHTML = "";
	var textNodes = text.replace(/\s/, " ").split("").map(function(char){
		var charNode = document.createElement("span");
		charNode.className = "char";
		charNode.appendChild(document.createTextNode(char === " "? "\xA0" :char));
		return charNode;
	});
	textNodes.forEach(function(node){
		round.appendChild(node);
	});
	var textWidth = textNodes.reduce(function(width, node){
		return width + node.offsetWidth;
	}, 0);
	
	var angleRange = Math.PI/1.5;
	var radius = round.offsetWidth / 2;
	var xBend = 0;
	
	textNodes.forEach(function(node){
		xBend += node.offsetWidth / 2;
		var angle = -(Math.PI + angleRange * (-1 + 2 * xBend / textWidth) / 2);
		node.style.transform = "rotate(" + (180 - angle * 180 / Math.PI) + "deg)";
		node.style.top = (radius * (1 + Math.cos(angle))) + "px";
		node.style.left = (radius * (1 + Math.sin(angle)) - node.offsetWidth / 2) + "px";
		xBend += node.offsetWidth / 2;
	});
}());
</script>
</body>
</html>
- damit solltest du besser fahren.
 
Danke Korbinian. Hab den Text mal etwas verlängert auf
<span id="round">Das ist mein krummer Text und wenn der extrem lang wird, wird es spannend was damit passiert.</span>.

kreisschrift.png

Schön wäre, wenn der Text
  1. in erster Linie sich auf 360 Grad ausbreiten würde (aber ohne Vergrößerung der Schriftgröße und Zeichenabstände, also nicht auffüllen) und
  2. in zweiter Linie sich stauchen würde, bis er passt, wenn er mit der vorgegebenen Schriftart, -größe und Zeichenabstand nicht rein passen würde.
 
Das Erste ist einfach. Man muss einfach aus der Breite des Textes den Winkel, den das ganze überspannt ausrechnen.
Das Zweite ist etwas nervig, da man da wirklich Schritt für Schritt die Schriftgröße kleiner machen muss, bis es passt.

Code:
<!DOCTYPE html>

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Fenstertitel</title>
<style type="text/css">
#round {
	position: relative;
	display: inline-block;
	white-space: nowrap;
	border: 1px solid blue;
	font-size: 2em;
	text-align: center;
	height: 0px;
	width: 0px;
	padding: 200px;
	border-radius: 100%;
}
#round .char {
	position: absolute;
	display: inline-block;
	transform-origin: 50% 0%;
	border: 0px solid red;
}
</style>
</head>
<body>
<span id="round">Das ist mein krummer Text und wenn der extrem lang wird, wird es spannend was damit passiert.</span>
<script type="text/javascript">
(function(){
	var round = document.getElementById("round");
	var text = round.textContent;
	round.innerHTML = "";
	var radius = round.offsetWidth / 2;
	
	var textNodes = text.replace(/\s/, " ").split("").map(function(char){
		var charNode = document.createElement("span");
		charNode.className = "char";
		charNode.appendChild(document.createTextNode(char === " "? "\xA0" :char));
		return charNode;
	});
	textNodes.forEach(function(node){
		round.appendChild(node);
	});
	var textWidth = textNodes.reduce(function(width, node){
		return width + node.offsetWidth;
	}, 0);
	var textHeight = textNodes.reduce(function(height, node){
		return Math.max(height, node.offsetHeight);
	}, 0);
	var unit = "px";
	var fontSize = textNodes.reduce(function(fontSize, node){
		var font = window.getComputedStyle(node).fontSize;
		unit = font.replace(/\d+/g, "");
		return Math.max(fontSize, parseInt(font, 10));
	}, 0);
	
	while (fontSize > 0 && textWidth > 2 * Math.PI * (radius - textHeight)){
		fontSize -= 0.1;
		textNodes.forEach(function(node){
			node.style.fontSize = fontSize + unit;
		});
	var textWidth = textNodes.reduce(function(width, node){
		return width + node.offsetWidth;
	}, 0);
	var textHeight = textNodes.reduce(function(height, node){
		return Math.max(height, node.offsetHeight);
	}, 0);
	}
	
	var angleRange = textWidth / (radius - textHeight);
	var xBend = 0;
	
	textNodes.forEach(function(node){
		xBend += node.offsetWidth / 2;
		var angle = -(Math.PI + angleRange * (-1 + 2 * xBend / textWidth) / 2);
		node.style.transform = "rotate(" + (180 - angle * 180 / Math.PI) + "deg)";
		node.style.top = (radius * (1 + Math.cos(angle))) + "px";
		node.style.left = (radius * (1 + Math.sin(angle)) - node.offsetWidth / 2) + "px";
		xBend += node.offsetWidth / 2;
	});
}());
</script>
</body>
</html>
 
Noch eine Frage: wenn ich den Rand dicker und mit Schatteneffekt mache kommt die Schrift etwas aus dem Rahmen, wie kann ich das ändern?
gekrümmter2.png

Aktueller Code, mit dem ich getestet habe:
HTML:
<!DOCTYPE html>

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Fenstertitel</title>
<style type="text/css">
#round {
	position: relative;
	display: inline-block;
	white-space: nowrap;
	border: 6px outset blue;
	font-size: 2em;
	text-align: center;
	height: 0px;
	width: 0px;
	padding: 50px;
	border-radius: 100%;
}
#round .char {
	position: absolute;
	display: inline-block;
	transform-origin: 50% 0%;
	border: 0px solid red;
}
</style>
</head>
<body>
<span id="round">DIES IST EIN TEST MIT SIEGEL</span>
<script type="text/javascript">
(function(){
	var round = document.getElementById("round");
	var text = round.textContent;
	round.innerHTML = "";
	var radius = round.offsetWidth / 2;
	
	var textNodes = text.replace(/\s/, " ").split("").map(function(char){
		var charNode = document.createElement("span");
		charNode.className = "char";
		charNode.appendChild(document.createTextNode(char === " "? "\xA0" :char));
		return charNode;
	});
	textNodes.forEach(function(node){
		round.appendChild(node);
	});
	var textWidth = textNodes.reduce(function(width, node){
		return width + node.offsetWidth;
	}, 0);
	var textHeight = textNodes.reduce(function(height, node){
		return Math.max(height, node.offsetHeight);
	}, 0);
	var unit = "px";
	var fontSize = textNodes.reduce(function(fontSize, node){
		var font = window.getComputedStyle(node).fontSize;
		unit = font.replace(/\d+/g, "");
		return Math.max(fontSize, parseInt(font, 10));
	}, 0);
	
	while (fontSize > 0 && textWidth > 2 * Math.PI * (radius - textHeight)){
		fontSize -= 0.1;
		textNodes.forEach(function(node){
			node.style.fontSize = fontSize + unit;
		});
	var textWidth = textNodes.reduce(function(width, node){
		return width + node.offsetWidth;
	}, 0);
	var textHeight = textNodes.reduce(function(height, node){
		return Math.max(height, node.offsetHeight);
	}, 0);
	}
	
	var angleRange = textWidth / (radius - textHeight);
	var xBend = 0;
	
	textNodes.forEach(function(node){
		xBend += node.offsetWidth / 2;
		var angle = -(Math.PI + angleRange * (-1 + 2 * xBend / textWidth) / 2);
		node.style.transform = "rotate(" + (180 - angle * 180 / Math.PI) + "deg)";
		node.style.top = (radius * (1 + Math.cos(angle))) + "px";
		node.style.left = (radius * (1 + Math.sin(angle)) - node.offsetWidth / 2) + "px";
		xBend += node.offsetWidth / 2;
	});
}());
</script>
</body>
</html>
 
Zurück
Oben