Based on http://bl.ocks.org/brattonc/5e5ce9beee483220e2f6 made by Curtis Bratton [brattonc]
You should keep SVG structure and hierarchy:
- <svg id={name} ... />
- <path id="area" ...> area to be animated
Based on http://bl.ocks.org/brattonc/5e5ce9beee483220e2f6 made by Curtis Bratton [brattonc]
You should keep SVG structure and hierarchy:
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> |
body { margin : 0; font-family: "Open Sans", sans-serif; background: #000; } |