Published
Edited
Feb 8, 2022
1 star
Insert cell
md`# SVG + D3 Animated Fireball`
Insert cell
{

const svgBackgroundColor = '#020202';
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.style("background-color", svgBackgroundColor),
container = svg.append("g").attr("transform", "translate(120,120)"),
path = container.append("path"),
filters = svg.append("defs");

let noiseFilter = filters
.append("filter")
.attr("id","perlin");

noiseFilter
.append("feTurbulence")
.attr("id", "perlin-turbulence")
.attr("type", "turbulence")
.attr("baseFrequency", "0.03")
.attr("numOctaves","4")
.attr("seed","2")
.attr("result","turbulence")
noiseFilter
.append("feDisplacementMap")
.attr("in","SourceGraphic")
.attr("in2","turbulence")
.attr("scale","40");

let radialGradient = filters
.append("radialGradient")
.attr("id","blob-gradient-radial")
.attr("cx","50%")
.attr("cy","50%")
.attr("fx","50%")
.attr("fy","50%");
//radialGradient
// .attr("gradientTransform","skewX(270) rotate(5");

radialGradient.append("stop")
.attr("id","radial-gradient-stop-minus-one")
.attr("offset","5%")//random change between 10 and 30
.attr("stop-color","#faa307");
radialGradient.append("stop")
.attr("id","radial-gradient-stop-one")
.attr("offset","10%")//random change between 10 and 30
.attr("stop-color","#ffba08");

radialGradient.append("stop")
.attr("id","radial-gradient-stop-zero")
.attr("offset","70%")//random change between 10 and 30
.attr("stop-color","#e85d04");
radialGradient.append("stop")
.attr("id","radial-gradient-stop-two")
.attr("offset","90%")//random change 50-80
.attr("stop-color","#dc2f02")
.attr("stop-opacity",.8);

radialGradient.append("stop")
.attr("id", "radial-gradient-stop-three")
.attr("offset", "97%")//random change 90-97
.attr("stop-color", "#ff4800")
.attr("stop-opacity",0.7);

radialGradient.append("stop")
.attr("id", "radial-gradient-stop-four")
.attr("offset", "99%")
.attr("stop-color", "#ffaa00")
.attr("stop-opacity",0.8);

radialGradient.append("stop")
.attr("id", "radial-gradient-stop-five")
.attr("offset", "100%")
.attr("stop-color", "#e85d04")
.attr("stop-opacity",0.8);




yield svg.node();
let count = 0;
d3.timer(function() {
count += 1;
path
.attr("d", spline.spline(points, 1, true));
for(let i in points){
let point = points[i];
const nX = noise(point.noiseOffsetX, point.noiseOffsetX);
const nY = noise(point.noiseOffsetY, point.noiseOffsetY);
const x = mapUtil(nX, -1, 1, point.originX - 20, point.originX + 20);
const y = mapUtil(nY, -1, 1, point.originY - 20, point.originY + 20);
point.x = x;
point.y = y;
point.noiseOffsetX += noiseStep;
point.noiseOffsetY += noiseStep;
}
if(count % 10 == 0){

d3.select("#blob-gradient-radial")
.transition()
.duration(1200)
.attr("fx", `${Math.floor(Math.random() * (80 - 40) + 40)}%`)
.attr("fy", `${Math.floor(Math.random() * (80 - 40) + 40)}%`);
d3.select("#radial-gradient-stop-one")
.transition()
.duration(1200)
.attr("offset",`${Math.floor(Math.random() * (50 - 10) + 10)}%`)//offset between 10 and 30
/*
d3.select("#radial-gradient-stop-two")
.transition()
.duration(1200)
.attr("offset",`${Math.floor(Math.random() * (80 - 50) + 50)}%`)//offset between 50-80
*/
d3.select("#radial-gradient-stop-three")
.transition()
.duration(1200)
.attr("offset",`${Math.floor(Math.random() * (92 - 90) + 90)}%`)//offset between 90-97
//console.log("rotating");
//let randomRotation = Math.floor(Math.random() * (90 - 45) + 45);
//linearGradient.attr("gradientTransform", `rotate(${randomRotation})`);
}
path.attr("fill", "url(#blob-gradient-radial)");
path.attr("filter","url(#perlin)");
});
}
Insert cell
function getCirclePoints(center,radius,numPoints){
let points = [];
let angleDist = (Math.PI*2) / numPoints;
for(let i in d3.range(numPoints)){
let theta = i * angleDist;
const x = center.x + radius * Math.cos(theta);
const y = center.y + radius * Math.sin(theta);
points.push({
x: x,
y: y,
originX: x,
originY: y,
noiseOffsetX: Math.random() * 1000,
noiseOffsetY: Math.random() * 1000
})
}
return points;
}
Insert cell
function mapUtil(n,start1,end1,start2,end2){
return ((n - start1) / (end1 - start1)) * (end2 - start2) + start2;
}
Insert cell
points = getCirclePoints({x: 100, y: 100}, 150, 6)
Insert cell
function noise(x,y){
return simplex.noise2D(x,y);
}
Insert cell
width = 500;
Insert cell
height = 500;
Insert cell
numPoints = 10;
Insert cell
noiseStep = .009
Insert cell
simplex = new SimplexNoise();
Insert cell
d3 = require("d3@6")
Insert cell
SimplexNoise = require("simplex-noise@2.4")
Insert cell
spline = require('https://bundle.run/@georgedoescode/spline@1.0.1')
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more