Skip to content

Instantly share code, notes, and snippets.

@emeeks
Last active April 12, 2018 09:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emeeks/8a3a12b0327f12560b1a to your computer and use it in GitHub Desktop.
Save emeeks/8a3a12b0327f12560b1a to your computer and use it in GitHub Desktop.
Cheap Sketchy Functions

I've been seriously enjoying (d3.sketchy)[https://github.com/sebastian-meier/d3.sketchy] lately, which gives you great functions for making sketchy looking shapes. But sometimes you want some simple sketchiness and you don't want to rely on clipping or maybe you just want to better understand native SVG path functions.

So here are a couple "cheap" sketchy functions. One is a sketchy outline that uses jitter to make the line less sure, the other a sketchy fill that draws a line from one part of the path outline to another. Notice the sketchy fill doesn't do such a good job with more irregular polygons.

What I do like about these cheap functions is that because they rely on the actual DOM node, you just point it at the existing path and it handles the rest.

You can drag the shapes if you want to see cheap animated sketchiness.

<html>
<head>
<title>Cheap Sketchy</title>
<meta charset="utf-8" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.16/d3.min.js" charset="utf-8" type="text/javascript"></script>
</head>
<style>
svg {
height: 500px;
width: 500px;
border: 1px solid gray;
}
</style>
<body>
<div id="viz">
<svg class="main">
</svg>
</div>
</body>
<footer>
<script>
//Create some shapes
function cheapSketchy(path) {
var length = path.getTotalLength();
var drawCode = "";
var i = 0;
var step = 2;
while (i < length / 2) {
var start = path.getPointAtLength(i);
var end = path.getPointAtLength(length - i);
drawCode += " M" + (start.x + (Math.random() * step - step/2)) + " " + (start.y + (Math.random() * step - step/2)) + "L" + (end.x + (Math.random() * step - step/2)) + " " + (end.y + (Math.random() * step - step/2));
i += step + (Math.random() * step);
}
return drawCode;
}
function cheapSketchyOutline(path) {
var j = 2;
var i = 0;
var length = path.getTotalLength();
var pointsArray = [];
while (i < length) {
newPoint = path.getPointAtLength(i);
pointsArray.push({x: newPoint.x + (j/2 - Math.random() * j), y: newPoint.y + (j/2 - Math.random() * j)});
i += j + (Math.random() * (j));
}
//Make sure to get the last point
pointsArray.push(path.getPointAtLength(length));
var line = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("basis");
return line(pointsArray);
}
d3.select("svg").append("g")
.attr("transform", "translate(50,50)")
.datum("orange")
.append("path").attr("d", "M 39.161481,0.33269981 C 23.836192,0.04604167 2.912469,6.6953575 0.19616865,24.750404 -3.3088936,48.048292 34.289385,59.946975 55.777303,57.949914 68.896857,56.730599 86.22939,44.922868 85.395921,31.773163 84.217,13.17323 57.795479,0.68124705 39.161481,0.33269981 z");
d3.select("svg").append("g")
.attr("transform", "translate(150,50)")
.datum("red")
.append("path").attr("d", "M 31.795661,16.537504 8.871749,4.9948244 0.19616865,42.919427 33.750491,52.465257 50.375701,82.502648 89.724318,56.772845 67.226898,33.737382 73.454028,5.5027808 z");
d3.select("svg").append("g")
.attr("transform", "translate(350,50)")
.datum("blue")
.append("path").attr("d", "M 44.072028,0.33269981 5.1067155,24.750404 C 36.091369,39.272988 70.224087,50.653364 39.572499,126.69757 L 81.467484,105.92242 C 100.86794,59.906873 85.538667,25.682721 44.072028,0.33269981 z");
d3.select("svg").selectAll("g > *")
.attr("class", "original")
.style("fill", function (d) {return d}).style("fill-opacity", 0.3);
var drag = d3.behavior.drag().on("drag", resketch);
d3.select("svg").selectAll("g").call(drag);
d3.select("svg").selectAll("g").each(function (d) {
var node = d3.select(this).select("*").node();
var fillCode = cheapSketchy(node);
var strokeCode = cheapSketchyOutline(node);
d3.select(this).append("path")
.style("stroke-width", "1px")
.style("stroke", d)
.attr("class", "sketchy-fill")
.attr("d", fillCode);
d3.select(this).append("path")
.style("stroke-width", "2px")
.style("stroke", d)
.style("fill", "none")
.attr("class", "sketchy-stroke")
.attr("d", strokeCode);
})
resketch();
function resketch() {
d3.select(this)
.attr("transform", "translate(" + (d3.event.x - 50) + "," + (d3.event.y - 50) + ")");
var node = d3.select(this).select("path.original").node();
var fillCode = cheapSketchy(node);
var strokeCode = cheapSketchyOutline(node);
d3.select(this).select("path.sketchy-fill")
.attr("d", fillCode);
d3.select(this).select("path.sketchy-stroke")
.attr("d", strokeCode);
}
</script>
</footer>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment