block by zanarmstrong 08833a326224d41fbb96

Create Your Own Texture!

Full Screen

This example uses texture.js to create custom patterns.

index.html

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <link rel="stylesheet" href="x.css">
  <link href='//fonts.googleapis.com/css?family=Raleway:400,700' rel='stylesheet' type='text/css'>
</head>
<body>
  <h2>Draw in the Box to Create a Pattern</h2>
  <div id="clickToClear"></div>
  <div id="example"></div>
  <script type="text/javascript" src="datgui-min.js"></script>
  <script src="//d3js.org/d3.v3.min.js"></script>
  <script src="textures.min.js"></script>
  <script src="x.js"></script>
</body>

textures.min.js

// from https://github.com/riccardoscalco/textures/blob/master/textures.min.js, which has an MIT license

(function(){var rand,root,__slice=[].slice;root=typeof exports!=="undefined"&&exports!==null?exports:this;rand=function(){return(Math.random().toString(36)+"00000000000000000").replace(/[^a-z]+/g,"").slice(0,5)};root.textures={circles:function(){var background,circles,complement,fill,id,radius,size,stroke,strokeWidth;size=20;background="";radius=2;complement=false;fill="#343434";stroke="#343434";strokeWidth=0;id=rand();circles=function(){var corner,g,_i,_len,_ref,_results;g=this.append("defs").append("pattern").attr("id",id).attr("patternUnits","userSpaceOnUse").attr("width",size).attr("height",size);if(background){g.append("rect").attr("width",size).attr("height",size).attr("fill",background)}g.append("circle").attr("cx",size/2).attr("cy",size/2).attr("r",radius).attr("fill",fill).attr("stroke",stroke).attr("stroke-width",strokeWidth);if(complement){_ref=[[0,0],[0,size],[size,0],[size,size]];_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){corner=_ref[_i];_results.push(g.append("circle").attr("cx",corner[0]).attr("cy",corner[1]).attr("r",radius).attr("fill",fill).attr("stroke",stroke).attr("stroke-width",strokeWidth))}return _results}};circles.heavier=function(_){if(!arguments.length){radius=radius*2}else{radius=_?radius*2*_:radius*2}return circles};circles.lighter=function(_){if(!arguments.length){radius=radius/2}else{radius=_?radius/(2*_):radius/2}return circles};circles.thinner=function(_){if(!arguments.length){size=size*2}else{size=_?size*2*_:size*2}return circles};circles.thicker=function(_){if(!arguments.length){size=size/2}else{size=_?size/(2*_):size/2}return circles};circles.background=function(_){background=_;return circles};circles.size=function(_){size=_;return circles};circles.complement=function(){complement=true;return circles};circles.radius=function(_){radius=_;return circles};circles.fill=function(_){fill=_;return circles};circles.stroke=function(_){stroke=_;return circles};circles.strokeWidth=function(_){strokeWidth=_;return circles};circles.id=function(_){if(!arguments.length){return id}else{id=_;return circles}};circles.url=function(){return"url(#"+circles.id()+")"};return circles},lines:function(){var background,id,lines,orientation,path,shapeRendering,size,stroke,strokeWidth;size=20;strokeWidth=2;stroke="#343434";id=rand();background="";orientation=["diagonal"];shapeRendering="auto";path=function(orientation){switch(orientation){case"0/8":return function(s){return"M "+s/2+", 0 l 0, "+s}(size);case"vertical":return function(s){return"M "+s/2+", 0 l 0, "+s}(size);case"1/8":return function(s){return"M "+s/4+",0 l "+s/2+","+s+" M "+-s/4+",0 l "+s/2+","+s+" M "+s*3/4+",0 l "+s/2+","+s}(size);case"2/8":return function(s){return"M 0,"+s+" l "+s+","+-s+" M "+-s/4+","+s/4+" l "+s/2+","+-s/2+" M "+3/4*s+","+5/4*s+" l "+s/2+","+-s/2}(size);case"diagonal":return function(s){return"M 0,"+s+" l "+s+","+-s+" M "+-s/4+","+s/4+" l "+s/2+","+-s/2+" M "+3/4*s+","+5/4*s+" l "+s/2+","+-s/2}(size);case"3/8":return function(s){return"M 0,"+3/4*s+" l "+s+","+-s/2+" M 0,"+s/4+" l "+s+","+-s/2+" M 0,"+s*5/4+" l "+s+","+-s/2}(size);case"4/8":return function(s){return"M 0,"+s/2+" l "+s+",0"}(size);case"horizontal":return function(s){return"M 0,"+s/2+" l "+s+",0"}(size);case"5/8":return function(s){return"M 0,"+-s/4+" l "+s+","+s/2+"M 0,"+s/4+" l "+s+","+s/2+"M 0,"+s*3/4+" l "+s+","+s/2}(size);case"6/8":return function(s){return"M 0,0 l "+s+","+s+" M "+-s/4+","+3/4*s+" l "+s/2+","+s/2+" M "+s*3/4+","+-s/4+" l "+s/2+","+s/2}(size);case"7/8":return function(s){return"M "+-s/4+",0 l "+s/2+","+s+" M "+s/4+",0 l "+s/2+","+s+" M "+s*3/4+",0 l "+s/2+","+s}(size);default:return function(s){return"M "+s/2+", 0 l 0, "+s}(size)}};lines=function(){var g,o,_i,_len,_results;g=this.append("defs").append("pattern").attr("id",id).attr("patternUnits","userSpaceOnUse").attr("width",size).attr("height",size);if(background){g.append("rect").attr("width",size).attr("height",size).attr("fill",background)}_results=[];for(_i=0,_len=orientation.length;_i<_len;_i++){o=orientation[_i];_results.push(g.append("path").attr("d",path(o)).attr("stroke-width",strokeWidth).attr("shape-rendering",shapeRendering).attr("stroke",stroke).attr("stroke-linecap","square"))}return _results};lines.background=function(_){background=_;return lines};lines.shapeRendering=function(_){shapeRendering=_;return lines};lines.heavier=function(_){if(!arguments.length){strokeWidth=strokeWidth*2}else{strokeWidth=_?strokeWidth*2*_:strokeWidth*2}return lines};lines.lighter=function(_){if(!arguments.length){strokeWidth=strokeWidth/2}else{strokeWidth=_?strokeWidth/(2*_):strokeWidth/2}return lines};lines.thinner=function(_){if(!arguments.length){size=size*2}else{size=_?size*2*_:size*2}return lines};lines.thicker=function(_){if(!arguments.length){size=size/2}else{size=_?size/(2*_):size/2}return lines};lines.orientation=function(){var args;args=1<=arguments.length?__slice.call(arguments,0):[];orientation=args;return lines};lines.size=function(_){size=_;return lines};lines.stroke=function(_){stroke=_;return lines};lines.strokeWidth=function(_){strokeWidth=_;return lines};lines.id=function(_){if(!arguments.length){return id}else{id=_;return lines}};lines.url=function(){return"url(#"+lines.id()+")"};return lines},paths:function(){var background,d,fill,height,id,paths,shapeRendering,size,stroke,strokeWidth,svgPath,width;size=20;height=1;width=1;strokeWidth=2;stroke="#343434";background="";d="";shapeRendering="auto";fill="transparent";id=void 0;svgPath=function(_){switch(_){case"squares":return function(s){return"M "+s/4+" "+s/4+" l "+s/2+" 0 l 0 "+s/2+" l "+-s/2+" 0 Z"}(size);case"nylon":return function(s){return"M 0 "+s/4+" l "+s/4+" 0 l 0 "+-s/4+" M "+s*3/4+" "+s+" l 0 "+-s/4+" l "+s/4+" 0 M "+s/4+" "+s/2+" l 0 "+s/4+" l "+s/4+" 0 M "+s/2+" "+s/4+" l "+s/4+" 0 l 0 "+s/4}(size);case"waves":return function(s){return"M 0 "+s/2+" c "+s/8+" "+-s/4+" , "+s*3/8+" "+-s/4+" , "+s/2+" 0 c "+s/8+" "+s/4+" , "+s*3/8+" "+s/4+" , "+s/2+" 0 M "+-s/2+" "+s/2+" c "+s/8+" "+s/4+" , "+s*3/8+" "+s/4+" , "+s/2+" 0 M "+s+" "+s/2+" c "+s/8+" "+-s/4+" , "+s*3/8+" "+-s/4+" , "+s/2+" 0"}(size);case"woven":return function(s){return"M "+s/4+","+s/4+"l"+s/2+","+s/2+"M"+s*3/4+","+s/4+"l"+s/2+","+-s/2+"M"+s/4+","+s*3/4+"l"+-s/2+","+s/2+"M"+s*3/4+","+s*5/4+"l"+s/2+","+-s/2+"M"+-s/4+","+s/4+"l"+s/2+","+-s/2}(size);case"crosses":return function(s){return"M "+s/4+","+s/4+"l"+s/2+","+s/2+"M"+s/4+","+s*3/4+"l"+s/2+","+-s/2}(size);case"caps":return function(s){return"M "+s/4+","+s*3/4+"l"+s/4+","+-s/2+"l"+s/4+","+s/2}(size);case"hexagons":return function(s){width=3;height=Math.sqrt(3);return"M "+s+",0 l "+s+",0 l "+s/2+","+s*Math.sqrt(3)/2+" l "+-s/2+","+s*Math.sqrt(3)/2+" l "+-s+",0 l "+-s/2+","+-s*Math.sqrt(3)/2+" Z M 0,"+s*Math.sqrt(3)/2+" l "+s/2+",0 M "+3*s+","+s*Math.sqrt(3)/2+" l "+-s/2+",0"}(size);default:return _(size)}};paths=function(){var g,path;path=svgPath(d);id=rand();g=this.append("defs").append("pattern").attr("id",id).attr("patternUnits","userSpaceOnUse").attr("width",size*width).attr("height",size*height);if(background){g.append("rect").attr("width",size*width).attr("height",size*height).attr("fill",background)}return g.append("path").attr("d",path).attr("fill",fill).attr("stroke-width",strokeWidth).attr("shape-rendering",shapeRendering).attr("stroke",stroke).attr("stroke-linecap","square")};paths.background=function(_){background=_;return paths};paths.shapeRendering=function(_){shapeRendering=_;return paths};paths.heavier=function(_){if(!arguments.length){strokeWidth=strokeWidth*2}else{strokeWidth=_?strokeWidth*2*_:strokeWidth*2}return paths};paths.lighter=function(_){if(!arguments.length){strokeWidth=strokeWidth/2}else{strokeWidth=_?strokeWidth/(2*_):strokeWidth/2}return paths};paths.thinner=function(_){if(!arguments.length){size=size*2}else{size=_?size*2*_:size*2}return paths};paths.thicker=function(_){if(!arguments.length){size=size/2}else{size=_?size/(2*_):size/2}return paths};paths.d=function(_){d=_;return paths};paths.size=function(_){size=_;return paths};paths.stroke=function(_){stroke=_;return paths};paths.strokeWidth=function(_){strokeWidth=_;return paths};paths.id=function(_){if(!arguments.length){return id}else{id=_;return paths}};paths.url=function(){return"url(#"+paths.id()+")"};return paths}}}).call(this);

x.css

body {
  margin: 30px;
  font-family: 'Raleway', sans-serif;
  width: 1000px;
}

p {
  color: grey;
}

h1 {
  color: darkgrey;
}

.hidden {
  display: none;
}

.dashedLines {
	stroke: darkorange;
	stroke-dasharray: 5, 5;
	shape-rendering: "crispEdges";
	stroke-width: 1
}

.clickToClear {
	fill: grey;
	text-size: 16px;
}

.clickToClear:hover {
	fill: orange;
}

x.js

"use strict";

// goals: 
//    choose: color, width, interpolation
//    show code

// so that touchmove is not scrolling
document.body.addEventListener('touchmove', function(event) {
  event.preventDefault();
}, false);

// create svg
var width = 1000; 
var height = 1000;
var margin = {left: 22, top: 18, right: 0, bottom: 0};

var svg = d3.select('#example')
  .append('svg')
  .attr('width', width + margin.left + margin.right)
  .attr('height', height + margin.top + margin.bottom)
  .append('g')
  .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

// set major dimensions
var rectDim = {"x": 10,"y":10, "height": 400, "width": 400}
// drawable dimensions
var drawDim = {"x": 0,"y":0, "height": 440, "width": 440}

////////////////
// click to clear
////////////////

d3.select("#clickToClear").append("svg").attr("width", 250).attr("height", 30)
  .append("text")
  .on("click", function(d){
	d3.select(".userShape").classed('hidden',true);
	d3.select("circle").style("fill", "none");
	points = [];
	texturePoints = [];
  })
  .attr("class", "clickToClear")
  .attr({x: 0, y:20})
  .text("Click here to Clear")

///////////////////////////////////
// set up input box
//////////////////////////////////
var dashedLinesVert = []
var dashedLinesHori = []
var bufferDist = 10;

for (var i = 0; i < 3; i++){
	dashedLinesVert[i] = {
		"x1": rectDim.x + (i + 1) * rectDim.width / 4,
		"x2": rectDim.x + (i + 1) * rectDim.width / 4,
		"y1": rectDim.y - bufferDist,
		"y2": rectDim.y + rectDim.height + bufferDist
	}
}

for (var i = 0; i < 3; i++){
	dashedLinesHori[i] = {
		"y1": rectDim.y + (i + 1) * rectDim.height / 4,
		"y2": rectDim.y + (i + 1) * rectDim.height / 4,
		"x1": rectDim.x - bufferDist,
		"x2": rectDim.x + rectDim.width + bufferDist
	}
}


function drawDashed(data, direction){
  svg.selectAll("." + direction)
    .data(data)
    .enter()
    .append("line")
    .attr("x1", function(d){return d.x1})
    .attr("x2", function(d){return d.x2})
    .attr("y1", function(d){return d.y1})
    .attr("y2", function(d){return d.y2})
    .attr("class", "dashedLines " + direction);
}

drawDashed(dashedLinesVert, "vert");
drawDashed(dashedLinesHori, "hori");


var points = [];
var texturePoints = [];

// draw rect
var canvas = svg.append("rect")
  .attr(rectDim)
  .style({
      "fill": "transparent",
      "stroke": "darkorange",
      "shape-rendering": "crispEdges",
      "stroke-width": 2
  });
var drawSurface = svg.append("rect")
.attr(drawDim)
.style({
  fill: "transparent"
})

// thanks to enjalot for replacing mouseover w/ click & drag functionality
// http://tributary.io/inlet/8c7d398f505adda2cb08

var drag = d3.behavior.drag()
.on("drag", function(d) {
  updateArray([d3.event.x, d3.event.y])
})
.on("dragstart", function(d) {
  points.push([]);
  texturePoints.push([]);
})

drawSurface.call(drag)


////////////////////////
// set up easy options
///////////////////////


var userOptions = function() {
  this.basesize = 120
  this.baseEdge = this.basesize / 4;
  this.thickness = 2;
  this.patternColor = '#ffffff';
  this.interpolation = 'basis';
};

var text = new userOptions();


/////////////////////
// set up Texture
/////////////////////
var makeItaPath = d3.svg.line()
  .x(function(d){return d.x})
  .y(function(d){return d.y})
  .interpolate("linear");

var t = textures.paths()
  .d(function(s) {
      return makeItaPath([]);
  })
  .size(text.basesize)
  .strokeWidth(1)
  .thicker(2)
  .stroke("blue");

svg.call(t);

svg.append("circle")
  .attr({"cx": rectDim.x + rectDim.width * 3/2 + 50, "cy": rectDim.height / 2 + rectDim.y, "r": rectDim.height / 2, "stroke": "darkorange"})
  .style("fill", t.url());

svg.append("path")
  .attr("d", makeItaPath([]))
  .attr("class", "userShape")
  .attr("fill", "none")
  .attr("stroke", "blue")
  .attr("stroke-width", 5);

function allPaths(arrayOfPoints) {
  //console.log(arrayOfPoints)
  var path = ""
  for(var i = 0; i < arrayOfPoints.length; i++){
    path = path + makeItaPath(arrayOfPoints[i])
  }
  return path;
}

//////////////////////
// let user draw
//////////////////////



function updateArray(coord){
  points[points.length - 1].push({
    x: coord[0],
    y: coord[1]
  })

  texturePoints[texturePoints.length - 1].push({
    x: (coord[0] - rectDim.x) / (rectDim.width / text.baseEdge),
    y: (coord[1] - rectDim.y) / (rectDim.width / text.baseEdge)
  })

  d3.select(".userShape").attr("d", allPaths(points)).classed("hidden", false);

  updateTexture();
}

function updateTexture(){
	var t = textures.paths()
      .d(function(s) {
          return allPaths(texturePoints);
      })
      .size(text.basesize)
      .strokeWidth(2)
      .thicker(2)
      .stroke("blue");
	svg.call(t);
	d3.select("circle").style("fill", t.url());
}