Skip to content

Instantly share code, notes, and snippets.

@vkuchinov
Last active April 30, 2018 11:06
Show Gist options
  • Select an option

  • Save vkuchinov/9a22f79e34f5a032bc769ffb238a50d3 to your computer and use it in GitHub Desktop.

Select an option

Save vkuchinov/9a22f79e34f5a032bc769ffb238a50d3 to your computer and use it in GitHub Desktop.
D3.JS Liquid Shapes (external SVG)
d3.liquid = function(parent_, index_, object_) {
var x = object_.x, y = object_.y;
var g = parent_.append("g").attr("id", "shape" + index_).attr("transform", "translate(" + (x) + ", " + (y) + " )");
d3.xml(object_.asset).mimeType("image/svg+xml").get(function(error, xml) {
if (error) throw error;
var imported = document.importNode(xml.documentElement, true);
d3.select("#shape" + index_)
.each(function() { this.appendChild(imported); });
d3.select("#" + object_.name).select("#area").attr("fill", "#228CC8");
var o = d3.select("#" + object_.name).select("#area");
this.xy = o.node().getBBox();
d3.select("#" + object_.name).append("clipPath").attr("id", "clip").append("rect")
.attr("id", "clip")
.attr("x", this.xy.x - object_.strokeWidth / 2)
.attr("y", this.xy.y - object_.strokeWidth / 2)
.attr("width", this.xy.width + object_.strokeWidth)
.attr("height", this.xy.height + object_.strokeWidth)
.attr("fill", "#FF00FF")
.attr("opacity", 0.4);
d3.select("#" + object_.name).append("text")
.attr("id", "full")
.attr("dx", this.xy.x + this.xy.width / 2 + object_.offset.x)
.attr("dy", this.xy.y + this.xy.height / 1.75 + object_.offset.y)
.attr("text-anchor", "middle")
.attr("font-size", object_.fontsize)
.attr("fill", "#FFFFFF")
.attr("stroke", "#none")
.html("<tspan>" + map(object_.value, 0, 1, 0, object_.units).toFixed(object_.decimals) + "</tspan><tspan style='font-size: " + object_.fontsize / 2 + "'>" + "ml</tspan>");
d3.select("#" + object_.name).append("text")
.attr("id", "clipped")
.attr("dx", this.xy.x + this.xy.width / 2 + object_.offset.x)
.attr("dy", this.xy.y + this.xy.height / 1.75 + + object_.offset.y)
.attr("fill", "#FFFFFF")
.attr("text-anchor", "middle")
.attr("font-size", object_.fontsize)
.attr("fill", "#000000")
.attr("stroke", "#000000")
.attr("stroke-width", 2)
.html("<tspan>" + map(object_.value, 0, 1, 0, object_.units).toFixed(object_.decimals) + "</tspan><tspan style='font-size: " + object_.fontsize / 2 + "'>" + "ml</tspan>");
this.cx = this.xy.x + this.xy.width / 2, this.cy = + this.xy.y + this.xy.height / 2;
this.halfWidth = this.xy.width / 2, this.halfHeight = this.xy.height / 2;
this.ratio = this.halfHeight/this.halfWidth;
this.waveCount = 1;
this.waveOffset = 0;
this.waveHeight = 5;
this.waveLength = this.halfWidth * 2 / this.waveCount;
this.waveClipCount = 1 + this.waveCount;
this.waveClipWidth = this.waveLength * this.waveClipCount;
this.gaugeCircleX = d3.scaleLinear().range([0, 2 * Math.PI]).domain([0, 1]);
this.gaugeCircleY = d3.scaleLinear().range([0, this.halfWidth]).domain([0, this.halfWidth]);
this.waveScaleX = d3.scaleLinear().range([0, this.waveClipWidth]).domain([0, 1]);
this.waveScaleY = d3.scaleLinear().range([0, this.waveHeight]).domain([0, 1]);
waveAnimateScale = d3.scaleLinear().range([0, this.waveClipWidth - this.halfWidth * 2]).domain([0, 1]);
this.data = [];
for(var i = 0; i <= 32 * this.waveClipCount; i++){ this.data.push({x: i / (32 * this.waveClipCount), y: (i / (32))}); }
this.clipArea = d3.area()
.x(function(d) { return this.waveScaleX(d.x); } )
.y0(function(d) { return this.waveScaleY(Math.sin(Math.PI * 2 * this.waveOffset * -1 + Math.PI * 2 * ( 1 - this.waveCount) + d.y * 2 * Math.PI));} )
.y1(function(d) { return (this.halfWidth * this.ratio * 2 + this.waveHeight); } );
this.clip = d3.select("#" + object_.name).append("clipPath")
.attr("id", "clipWave" + index_)
.attr("transform", "translate(" + (this.cx + this.halfWidth - this.waveClipWidth) + ", " + (this.cy + map(object_.value, 0, 1, this.halfWidth * this.ratio, -this.halfWidth * this.ratio)) + ")");
this.wave = this.clip.append("path")
.datum(this.data)
.attr("d", this.clipArea)
.attr("T", 0)
.attr("fill", "#0FFFF0");
d3.select("#" + object_.name).select("#area").attr("clip-path","url(#clipWave" + index_ + ")");
d3.select("#" + object_.name).select("#clipped").attr("clip-path","url(#clipWave" + index_ + ")");
animate(this.wave, this.waveAnimateScale);
});
function animate(wave_, waveAnimateScale_){
wave_.attr("transform","translate("+ waveAnimateScale_(wave_.attr("T"))+", 0)");
wave_.transition()
.ease(d3.easeLinear)
.duration(object_.waveAnimateTime * (1 - wave_.attr("T")))
.attr("transform","translate(" + waveAnimateScale_(1)+", 0)")
.attr("T", 1)
.on("end", function(){ wave_.attr("T", 0); animate(wave_, waveAnimateScale_); });
}
}
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>D3.JS Liquid Shapes</title>
<link rel="stylesheet" href="styles.css?v=1.0">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="d3.liquid.js"></script>
</head>
<body>
<script>
function map(value_, min0_, max0_, min1_, max1_){ return min1_ + (value_ - min0_) / (max0_ - min0_) * (max1_- min1_); }
var objects = [
{ name : "sampleA", asset: "sampleA.svg", waveAnimateTime: 1800, x: -150, y: window.innerHeight / 4, value: 0.75, units: 568, strokeWidth: 3, fontsize: 28, decimals: 0, offset: { x: 0, y: 0 } },
{ name : "sampleB", asset: "sampleB.svg", waveAnimateTime: 1800, x: 150, y: window.innerHeight / 4, value: 0.33, units: 0.5, strokeWidth: 3, fontsize: 48, decimals: 2, offset: { x: 0, y: 24 } }
]
var svg = d3.select("body").append("svg")
.attr("width", window.innerWidth)
.attr("height", window.innerHeight);
var shapeA = d3.liquid(svg, 0, objects[0]);
var shapeB = d3.liquid(svg, 1, objects[1]);
</script>
</body>
</html>
Display the source blob
Display the rendered blob
Raw
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="sampleA" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="-450 -450 900 900" xml:space="preserve">
<path stroke="#228CC8" stroke-width="3" fill="none" d="M0,0c21.3-0.5,25.2-0.9,28-1.3c2.8-0.4,4.6-0.9,6.4-1.6s3.7-1.5,5.6-2.5s3.7-2,4.9-3.5
c1.2-1.5,1.6-3.3,1.9-14.1s0.3-30.4,0.1-54.9c-0.2-24.5-0.7-53.8-1.1-69.6s-0.7-18.1-1.2-20.4c-0.5-2.3-1.3-4.5-2.1-6.8
c-0.8-2.3-1.7-4.7-2.7-7.2s-2.1-5.1-3.5-8.1c-1.4-2.9-3.1-6.2-4.6-9.1c-1.5-2.9-2.8-5.3-4.4-8.9c-1.5-3.5-3.3-8.1-4.3-11.6
s-1.3-6-1.8-13s-1.2-18.6-1.5-24.6s-0.3-6.5-0.2-6.9c0.2-0.4,0.5-0.7,0.9-1c0.4-0.4,1-0.8,1.2-1.9c0.3-1,0.3-2.6,0.2-4
c-0.1-1.4-0.3-2.6-0.6-3.5c-0.3-0.8-0.6-1.2-0.9-1.6s-0.6-0.8-0.8-1.4c-0.2-0.6-0.4-1.3-0.3-1.9s0.4-1,0.9-1.3
c0.4-0.4,0.9-0.7,1.2-1.3c0.3-0.6,0.4-1.5,0.1-2.3s-0.9-1.5-1.8-2s-2.1-0.8-3.7-1s-3.7-0.4-15.9-0.5c-12.3,0.1-14.3,0.2-15.9,0.5
c-1.6,0.2-2.8,0.6-3.7,1c-0.9,0.5-1.5,1.2-1.8,2s-0.2,1.7,0.1,2.3c0.3,0.6,0.8,0.9,1.2,1.3s0.8,0.8,0.9,1.3c0.1,0.6-0.1,1.3-0.3,1.9
s-0.5,1-0.8,1.4s-0.7,0.8-0.9,1.6c-0.3,0.8-0.5,2.1-0.6,3.5s-0.1,3,0.2,4s0.8,1.5,1.2,1.9s0.7,0.7,0.9,1c0.2,0.4,0.2,0.9-0.2,6.9
c-0.3,6-1,17.6-1.5,24.6s-0.8,9.5-1.8,13s-2.8,8.1-4.3,11.6s-2.8,6-4.4,8.9c-1.5,2.9-3.2,6.1-4.6,9.1c-1.4,2.9-2.5,5.5-3.5,8.1
c-1,2.5-1.9,4.9-2.7,7.2c-0.8,2.3-1.6,4.5-2.1,6.8s-0.8,4.6-1.2,20.4s-0.9,45.1-1.1,69.6S-47-33.7-46.8-23
c0.3,10.8,0.8,12.6,1.9,14.1c1.2,1.5,3,2.5,4.9,3.5c1.9,1,3.8,1.8,5.6,2.5s3.6,1.2,6.4,1.6S-21.3-0.5,0,0z"/>
<path id="area" fill="#228CC8" d="M-17.3-250.2c0,0.4-0.1,0.8-0.1,1.3c-0.2,3.1-0.4,6.3-0.6,9.4c-0.1,1.9-0.3,3.9-0.4,5.8
c-0.1,1-0.1,2.1-0.2,3.1c-0.1,0.7-0.1,1.4-0.2,2.1c0,0.6-0.1,1.2-0.2,1.8c-0.1,0.5-0.1,1.1-0.2,1.6c-0.1,0.5-0.1,1-0.2,1.4
c-0.1,0.5-0.1,0.9-0.2,1.3c-0.1,0.4-0.2,0.9-0.3,1.3c-0.1,0.4-0.2,0.8-0.3,1.3c-0.1,0.4-0.2,0.9-0.4,1.3c-0.1,0.5-0.3,0.9-0.4,1.3
c-0.1,0.5-0.3,0.9-0.5,1.4s-0.3,1-0.5,1.4c-0.2,0.5-0.4,1-0.5,1.5c-0.3,0.9-0.7,1.9-1.1,2.8c-0.4,0.9-0.7,1.8-1.1,2.7
c-0.4,0.8-0.7,1.6-1.1,2.5c-0.3,0.7-0.7,1.5-1,2.2s-0.7,1.4-1,2c-0.3,0.7-0.7,1.3-1,2c-0.4,0.7-0.7,1.3-1.1,2
c-0.2,0.3-0.4,0.7-0.6,1c-0.7,1.4-1.5,2.8-2.2,4.3c-0.3,0.7-0.7,1.4-1,2.1c-0.2,0.5-0.5,1-0.7,1.5c-0.3,0.6-0.6,1.3-0.9,1.9
c-0.5,1.2-1,2.4-1.5,3.6c-0.2,0.6-0.5,1.2-0.7,1.8s-0.4,1.1-0.7,1.7c-0.4,1.1-0.8,2.2-1.2,3.3c-0.2,0.5-0.4,1.1-0.6,1.6
c-0.1,0.3-0.2,0.6-0.3,0.8c-0.2,0.5-0.4,1-0.5,1.6c-0.2,0.5-0.3,1-0.5,1.5c-0.1,0.5-0.3,1-0.4,1.5c-0.1,0.4-0.2,0.8-0.3,1.2
c-0.1,0.2-0.1,0.5-0.1,0.7c-0.1,0.3-0.1,0.6-0.1,0.8c-0.1,0.4-0.1,0.8-0.1,1.1c-0.1,0.5-0.1,1.1-0.1,1.6c0,0.6-0.1,1.2-0.1,1.7
c0,0.9-0.1,1.8-0.1,2.7c0,1.2-0.1,2.3-0.1,3.5c0,1.5-0.1,2.9-0.1,4.4c0,0.9,0,1.7-0.1,2.6c0,1.7-0.1,3.5-0.1,5.2
c0,2.3-0.1,4.5-0.1,6.8c-0.1,2.5-0.1,5.1-0.1,7.6c-0.1,5.6-0.2,11.3-0.3,16.9c-0.1,6-0.2,12-0.2,18c-0.1,5.9-0.1,11.8-0.2,17.6
c0,5.4-0.1,10.7-0.1,16.1c0,4.7,0,9.4,0,14.2c0,2.1,0,4.3,0,6.4c0,1.9,0,3.8,0,5.7c0,1.7,0,3.3,0.1,5c0,1.4,0,2.9,0.1,4.3
c0,1.2,0.1,2.4,0.1,3.6c0,1,0.1,1.9,0.1,2.9c0,0.6,0.1,1.1,0.1,1.7s0.1,1.3,0.2,1.9c0,0.3,0.1,0.5,0.1,0.8c0,0.2,0.1,0.4,0.1,0.6
c0,0.2,0.1,0.3,0.1,0.5c0,0.1,0.1,0.3,0.1,0.4c0,0.1,0.1,0.2,0.1,0.3s0,0.2,0.1,0.2c0,0.1,0,0.1,0.1,0.2c0,0,0,0.1,0.1,0.1
c0,0,0,0.1,0.1,0.1l0.1,0.1l0.1,0.1c0,0.1,0.1,0.1,0.1,0.1c0.1,0.1,0.2,0.2,0.3,0.3c0.1,0.1,0.2,0.2,0.4,0.3
c0.1,0.1,0.3,0.2,0.4,0.3c0.3,0.2,0.7,0.4,1,0.6c0.4,0.2,0.8,0.4,1.2,0.6c0.3,0.2,0.7,0.4,1,0.5c0.8,0.4,1.7,0.8,2.5,1.2
c0.8,0.3,1.5,0.6,2.3,0.9c0.4,0.1,0.8,0.3,1.2,0.4c0.1,0.6,0.5,0.7,0.9,0.8c0.2,0.1,0.5,0.1,0.7,0.2c0.5,0.1,1,0.2,1.5,0.3
c0.3,0.1,0.6,0.1,0.9,0.1c0.2,0,0.4,0.1,0.6,0.1c0.3,0.1,0.7,0.1,1,0.1c0.6,0.1,1.1,0.1,1.7,0.2c0.6,0.1,1.3,0.1,1.9,0.1
c0.9,0.1,1.7,0.1,2.6,0.1c1.1,0.1,2.3,0.1,3.4,0.2c1.5,0.1,2.9,0.1,4.4,0.2c1.8,0.1,3.7,0.1,5.5,0.2C-4.5-3-2.2-3,0-2.9
C2.2-3,4.5-3,6.7-3.1c1.8,0,3.7-0.1,5.5-0.2c1.5,0,2.9-0.1,4.4-0.2c1.1,0,2.3-0.1,3.4-0.2c0.9,0,1.7-0.1,2.6-0.1
c0.6,0,1.3-0.1,1.9-0.1C25-3.9,25.6-4,26.1-4c0.3,0,0.7-0.1,1-0.1c0.2,0,0.4-0.1,0.6-0.1c0.3,0,0.6-0.1,0.9-0.1
c0.5-0.1,1-0.2,1.5-0.3c0.2-0.1,0.5-0.1,0.7-0.2C31.2-4.9,31.6-5,32-5.1c0.4-0.1,0.8-0.3,1.2-0.4c0.8-0.3,1.6-0.6,2.3-0.9
c0.8-0.4,1.7-0.8,2.5-1.2c0.3-0.2,0.7-0.4,1-0.5c0.4-0.2,0.8-0.4,1.2-0.6c0.3-0.2,0.7-0.4,1-0.6c0.1-0.1,0.3-0.2,0.4-0.3
s0.2-0.2,0.4-0.3c0.1-0.1,0.2-0.2,0.3-0.3l0.1-0.1l0.1-0.1l0.1-0.1c0,0,0-0.1,0.1-0.1c0,0,0-0.1,0.1-0.1c0-0.1,0-0.1,0.1-0.2
c0-0.1,0.1-0.1,0.1-0.2c0-0.1,0.1-0.2,0.1-0.3s0.1-0.3,0.1-0.4c0-0.2,0.1-0.3,0.1-0.5s0.1-0.4,0.1-0.6c0-0.3,0.1-0.5,0.1-0.8
s0.1-0.6,0.1-0.9c0-0.3,0.1-0.7,0.1-1c0-0.6,0.1-1.1,0.1-1.7c0-1,0.1-1.9,0.1-2.9c0-1.2,0.1-2.4,0.1-3.6c0-1.4,0.1-2.9,0.1-4.3
c0-1.7,0-3.3,0.1-5c0-1.9,0-3.8,0-5.7c0-2.1,0-4.3,0-6.4c0-4.7,0-9.4,0-14.2c0-5.4,0-10.7-0.1-16.1c0-5.9-0.1-11.8-0.2-17.6
c-0.1-6-0.2-12-0.2-18c-0.1-5.6-0.2-11.3-0.3-16.9c-0.1-4.8-0.2-9.6-0.3-14.4c-0.1-2.6-0.1-5.2-0.2-7.8c0-1.5-0.1-2.9-0.1-4.4
c0-1.2-0.1-2.3-0.1-3.5c0-0.9-0.1-1.8-0.1-2.7c0-0.6-0.1-1.2-0.1-1.7s-0.1-1.1-0.1-1.6c0-0.4-0.1-0.8-0.1-1.1s-0.1-0.6-0.1-0.8
s-0.1-0.5-0.1-0.7c-0.2-0.9-0.5-1.8-0.7-2.7c-0.3-1-0.7-2-1-3.1c-0.1-0.3-0.2-0.6-0.3-0.8c-0.2-0.5-0.4-1.1-0.6-1.6
c-0.4-1.1-0.8-2.2-1.2-3.3c-0.2-0.6-0.4-1.1-0.7-1.7c-0.2-0.6-0.5-1.2-0.7-1.8c-0.5-1.2-1-2.4-1.5-3.6c-0.3-0.6-0.6-1.3-0.9-1.9
c-0.2-0.5-0.5-1-0.7-1.5c-0.3-0.7-0.7-1.4-1-2.1c-0.7-1.4-1.5-2.8-2.2-4.3c-0.5-1-1.1-2-1.6-3.1c-0.3-0.7-0.7-1.3-1-2s-0.7-1.3-1-2
c-0.4-0.7-0.7-1.5-1-2.2c-0.4-0.8-0.7-1.6-1.1-2.5s-0.8-1.8-1.1-2.7c-0.4-0.9-0.7-1.8-1.1-2.8c-0.2-0.5-0.4-1-0.5-1.5
c-0.3-0.9-0.7-1.9-1-2.8c-0.1-0.4-0.3-0.9-0.4-1.3c-0.1-0.4-0.2-0.9-0.4-1.3c-0.1-0.4-0.2-0.8-0.3-1.3c-0.1-0.4-0.2-0.9-0.3-1.3
c-0.1-0.4-0.2-0.9-0.2-1.3c-0.1-0.5-0.1-1-0.2-1.4c-0.1-0.5-0.1-1.1-0.2-1.6c-0.1-0.6-0.1-1.2-0.2-1.8c-0.1-0.7-0.1-1.4-0.2-2.1
c-0.1-1-0.1-2.1-0.2-3.1c-0.1-1.9-0.3-3.9-0.4-5.8c-0.1-1.1-0.1-2.1-0.2-3.2c-0.1-2.1-0.3-4.2-0.4-6.3c0-0.4-0.1-0.8-0.1-1.3
C5.7-250.2-5.8-250.2-17.3-250.2z"/>
</svg>
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
body { margin : 0; font-family: "Open Sans", sans-serif; background: #000; }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment