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> |
| <?xml version="1.0" encoding="utf-8"?> | |
| <svg version="1.1" id="sampleB" 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 id="area" fill="#228CC8" d="M-57-115.3c-25.3,43.9,6.4,98.8,56.9,98.8s82.5-54.9,56.9-98.8c-18.9-32.8-38.1-66-56.9-98.8 | |
| C-18.9-181.3-38.1-148.2-57-115.3z"/> | |
| <path stroke="#228CC8" stroke-width="3" fill="none" d="M-71.2-123.5C-102.9-68.6-63.4,0-0.1,0s103.1-68.6,71.2-123.5C47.3-164.7,23.5-205.7-0.1-247 C-23.9-206-47.7-164.7-71.2-123.5z"/> | |
| </svg> |
| body { margin : 0; font-family: "Open Sans", sans-serif; background: #000; } |