|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<meta charset="utf-8"> |
|
<title>Fun with <text></title> |
|
<style> |
|
svg { |
|
background-color: darkgreen; |
|
width: 100%; |
|
height: 300px; |
|
overflow: hidden; |
|
} |
|
</style> |
|
</head> |
|
<body lang="en"> |
|
|
|
<div id="wrapper"> |
|
<svg> |
|
<defs> |
|
<filter id="whiteOutlineEffect" width="200%" height="200%" x="-50%" y="-50%" color-interpolation-filters="sRGB"> |
|
<feMorphology in="SourceAlpha" result="MORPH" operator="dilate" radius="1" /> |
|
<feColorMatrix in="MORPH" result="WHITENED" type="matrix" values="-1 0 0 0 1, 0 -1 0 0 1, 0 0 -1 0 1, 0 0 0 1 0" /> |
|
<feMerge> |
|
<feMergeNode in="WHITENED" /> |
|
<feMergeNode in="SourceGraphic" /> |
|
</feMerge> |
|
</filter> |
|
</defs> |
|
</svg> |
|
</div> |
|
|
|
<fieldset> |
|
<label for="fontSize">Font Size [<code>&fontSize=</code>]</label> |
|
<input type="number" id="fontSize" step="1" min="1" max="300"> |
|
</fieldset> |
|
|
|
<fieldset> |
|
<label for="strokeWidth">Stroke Width (Examples 1 and 2) [<code>&strokeWidth=</code>]</label> |
|
<input type="number" id="strokeWidth" step="0.2" min="0" max="20"> |
|
</fieldset> |
|
|
|
<fieldset> |
|
<label for="radius">Radius (Example 3) [<code>&radius=</code>]</label> |
|
<input type="number" id="radius" step="1" min="0" max="20"> |
|
</fieldset> |
|
|
|
<fieldset> |
|
<label for="textRendering">Text Rendering [<code>&textRendering=</code>]</label> |
|
<select id="textRendering"> |
|
<option selected>auto</option> |
|
<option>optimizeSpeed</option> |
|
<option>optimizeLegibility</option> |
|
<option>geometricPrecision</option> |
|
</select> |
|
</fieldset> |
|
|
|
<fieldset> |
|
<label for="rotation">Rotation [<code>&rotation=</code>]</label> |
|
<input type="number" id="rotation" step="3" min="0" max="360"> |
|
</fieldset> |
|
|
|
<fieldset> |
|
<label for="fontFamily">Font Family [<code>&fontFamily=</code>]</label> |
|
<input type="text" list="genericFamilies" id="fontFamily"> |
|
<datalist id="genericFamilies"> |
|
<option>serif</option> |
|
<option>sans-serif</option> |
|
<option>monospace</option> |
|
<option>cursive</option> |
|
<option>fantasy</option> |
|
<option>system-ui</option> |
|
</datalist> |
|
</fieldset> |
|
|
|
<fieldset> |
|
<label for="strokeLineCap">Stroke Line Cap [<code>&strokeLineCap=</code>]</label> |
|
<select id="strokeLineCap"> |
|
<option value="" selected> </option> |
|
<option>butt</option> |
|
<option>round</option> |
|
<option>square</option> |
|
</select> |
|
</fieldset> |
|
|
|
<fieldset> |
|
<label for="strokeLineJoin">Stroke Line Join [<code>&strokeLineJoin=</code>]</label> |
|
<select id="strokeLineJoin"> |
|
<option value="" selected> </option> |
|
<option>miter</option> |
|
<option>round</option> |
|
<option>bevel</option> |
|
</select> |
|
</fieldset> |
|
|
|
<p>[<code id="position"></code>]</p> |
|
|
|
<script src="https://d3js.org/d3.v4.js"></script> |
|
<script src="https://cdn.rawgit.com/jerrybendy/url-search-params-polyfill/357fd6bf/index.js"></script> |
|
<script> |
|
|
|
/* global d3 */ |
|
|
|
"use strict"; |
|
|
|
var settings = { |
|
fontSize: 35, |
|
strokeWidth: 2, |
|
radius: 1, |
|
textRendering: "auto", |
|
rotation: 0, |
|
fontFamily: null, |
|
strokeLineCap: null, |
|
strokeLineJoin: null |
|
}; |
|
|
|
var urlSearch = document.location.search || parent.document.location.search; |
|
var searchParams = new URLSearchParams(urlSearch); |
|
|
|
for (var key in settings) { |
|
if (!settings.hasOwnProperty(key)) { |
|
continue; |
|
} |
|
|
|
if (searchParams.has(key)) { |
|
settings[key] = searchParams.get(key); |
|
} |
|
|
|
document.getElementById(key).value = settings[key] !== null ? settings[key] : ""; |
|
} |
|
|
|
var wrapper = document.getElementById("wrapper"); |
|
|
|
var height = wrapper.offsetHeight; |
|
var width = wrapper.offsetWidth; |
|
|
|
var svg = d3.select("svg"); |
|
var outerG = svg.append("g"); |
|
var g = outerG.append("g"); |
|
|
|
var feMorphology = d3.select("#whiteOutlineEffect feMorphology"); |
|
|
|
var position = document.getElementById("position"); |
|
|
|
function zoomed() { |
|
outerG.attr("transform", d3.event.transform); |
|
|
|
var x = d3.event.transform.x; |
|
var y = d3.event.transform.y; |
|
var k = d3.event.transform.k; |
|
|
|
x -= width / 2; |
|
y -= height / 2; |
|
|
|
position.innerHTML = [ |
|
"x=" + x.toFixed(3), |
|
"y=" + y.toFixed(3), |
|
"k=" + k.toFixed(3) |
|
].join("&"); |
|
} |
|
|
|
var zoom = d3.zoom() |
|
.scaleExtent([1 / 8, 8]) |
|
.on("zoom", zoomed); |
|
|
|
svg.call(zoom); |
|
|
|
var x = 0, y = 0, k = 1; |
|
|
|
if (searchParams.has("x") && searchParams.has("y") && searchParams.has("k")) { |
|
x = +searchParams.get("x"); |
|
y = +searchParams.get("y"); |
|
k = +searchParams.get("k"); |
|
} |
|
|
|
x += width / 2; |
|
y += height / 2; |
|
|
|
svg |
|
.transition() |
|
.duration(800) |
|
.call( |
|
zoom.transform, |
|
d3.zoomIdentity |
|
.translate(x, y) |
|
.scale(k) |
|
); |
|
|
|
var example1 = g.append("g").attr("transform", "translate(0,-80)"); |
|
|
|
var ex1OutlineText = example1.append("text") |
|
.attr("id", "ex1OutlineText") |
|
.attr("stroke", "white") |
|
.attr("fill", "none") |
|
.attr("text-anchor", "middle") |
|
.attr("alignment-baseline", "middle") |
|
.text("Example 1"); |
|
|
|
var ex1Text = example1.append("text") |
|
.attr("id", "ex1Text") |
|
.attr("text-anchor", "middle") |
|
.attr("alignment-baseline", "middle") |
|
.text("Example 1"); |
|
|
|
var example2 = g.append("g").attr("transform", "translate(0,0)"); |
|
|
|
var ex2Text = example2.append("text") |
|
.attr("id", "ex2Text") |
|
.attr("stroke", "white") |
|
.attr("paint-order", "stroke") |
|
.attr("text-anchor", "middle") |
|
.attr("alignment-baseline", "middle") |
|
.text("Example 2"); |
|
|
|
var example3 = g.append("g").attr("transform", "translate(0,80)"); |
|
|
|
example3.attr("filter", "url(#whiteOutlineEffect)"); |
|
|
|
var ex3Text = example3.append("text") |
|
.attr("id", "ex3Text") |
|
.attr("text-anchor", "middle") |
|
.attr("alignment-baseline", "middle") |
|
.text("Example 3"); |
|
|
|
function update() { |
|
|
|
feMorphology.attr("radius", settings.radius); |
|
|
|
g.attr("transform", settings.rotation ? "rotate(" + settings.rotation + ")" : null); |
|
|
|
ex1OutlineText |
|
.attr("stroke-width", settings.strokeWidth) |
|
.attr("stroke-linecap", settings.strokeLineCap) |
|
.attr("stroke-linejoin", settings.strokeLineJoin) |
|
.attr("font-size", settings.fontSize) |
|
.attr("font-family", settings.fontFamily) |
|
.attr("text-rendering", settings.textRendering); |
|
|
|
ex1Text |
|
.attr("stroke-linecap", settings.strokeLineCap) |
|
.attr("stroke-linejoin", settings.strokeLineJoin) |
|
.attr("font-size", settings.fontSize) |
|
.attr("font-family", settings.fontFamily) |
|
.attr("text-rendering", settings.textRendering); |
|
|
|
ex2Text |
|
.attr("stroke-width", settings.strokeWidth) |
|
.attr("stroke-linecap", settings.strokeLineCap) |
|
.attr("stroke-linejoin", settings.strokeLineJoin) |
|
.attr("font-size", settings.fontSize) |
|
.attr("font-family", settings.fontFamily) |
|
.attr("text-rendering", settings.textRendering); |
|
|
|
ex3Text |
|
.attr("stroke-linecap", settings.strokeLineCap) |
|
.attr("stroke-linejoin", settings.strokeLineJoin) |
|
.attr("font-size", settings.fontSize) |
|
.attr("font-family", settings.fontFamily) |
|
.attr("text-rendering", settings.textRendering); |
|
} |
|
|
|
function windowResize() { |
|
var zoomTransform = d3.zoomTransform(svg.node()); |
|
var k = zoomTransform.k, |
|
x = zoomTransform.x, |
|
y = zoomTransform.y; |
|
x -= width / 2; |
|
y -= height / 2; |
|
height = wrapper.offsetHeight; |
|
width = wrapper.offsetWidth; |
|
zoom.extent([[0, 0], [width, height]]); |
|
x += width / 2; |
|
y += height / 2; |
|
zoom.transform(svg, d3.zoomIdentity.translate(x, y).scale(k)); |
|
} |
|
|
|
d3.select("#fontSize").on("change input textInput keyup blur", function () { |
|
settings.fontSize = this.value || null; |
|
update(); |
|
}); |
|
|
|
d3.select("#strokeWidth").on("change input textInput keyup blur", function () { |
|
settings.strokeWidth = this.value || null; |
|
update(); |
|
}); |
|
|
|
d3.select("#radius").on("change input textInput keyup blur", function () { |
|
settings.radius = this.value || 0; |
|
update(); |
|
}); |
|
|
|
d3.select("#textRendering").on("change input", function () { |
|
settings.textRendering = this.value || null; |
|
update(); |
|
}); |
|
|
|
d3.select("#rotation").on("change input textInput keyup blur", function () { |
|
settings.rotation = this.value || 0; |
|
update(); |
|
}); |
|
|
|
d3.select("#fontFamily").on("change input textInput keyup blur", function () { |
|
settings.fontFamily = this.value || null; |
|
update(); |
|
}); |
|
|
|
d3.select("#strokeLineCap").on("change input textInput keyup blur", function () { |
|
settings.strokeLineCap = this.value || null; |
|
update(); |
|
}); |
|
|
|
d3.select("#strokeLineJoin").on("change input textInput keyup blur", function () { |
|
settings.strokeLineJoin = this.value || null; |
|
update(); |
|
}); |
|
|
|
window.addEventListener("resize", windowResize); |
|
update(); |
|
|
|
// Hello Chromium team! |
|
|
|
function demo() { |
|
console.log("start"); |
|
svg.transition().duration(2500) |
|
.call(zoom.transform, d3.zoomIdentity.scale(1 / 8)) |
|
.on("end", function () { |
|
svg.transition().duration(2500) |
|
.call(zoom.transform, d3.zoomIdentity.scale(8)) |
|
.on("end", function () { |
|
console.log("end"); |
|
}); |
|
}); |
|
} |
|
|
|
function demoGood() { |
|
document.body.style.fontFamily = "Roboto"; |
|
demo(); |
|
} |
|
|
|
function demoBad() { |
|
document.body.style.fontFamily = "-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\""; |
|
demo(); |
|
} |
|
|
|
</script> |
|
|
|
</body> |
|
|
|
</html> |