By Tom Coombs
Built with blockbuilder.org
license: mit |
By Tom Coombs
Built with blockbuilder.org
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<script src="https://d3js.org/d3.v6.min.js"></script> | |
<script> | |
function main() { | |
//document.getElementById("button1").addEventListener("click", function () { controller.flash_d3() }); | |
//document.getElementById("button2").addEventListener("click", function () { controller.flash_ten() }); | |
var controller = new Controller() | |
} | |
class Controller { | |
constructor() { | |
var container_svg = d3.select("#svg1"); | |
container_svg.attr("width", 1600); | |
container_svg.attr("height", 1600); | |
var svg = container_svg.append('g').attr('transform', 'translate(200,200)') | |
this.shapes = {} | |
this.drawn_lines_ten = [] | |
this.drawn_lines_d3 = [] | |
this.shapes.dee = new ShapeCircle(svg, "dee", 10, .6).add_clip_path(dee.svg_path) //.debug_show_clip(); | |
this.shapes.three = new ShapeCircle(svg, "three", 10, .6).add_clip_path(three.svg_path) | |
this.shapes.one = new ShapeCircle(svg, "one", 230, .6).add_clip_path(one.svg_path) | |
this.shapes.zero = new ShapeCircle(svg, "zero", 230, .6).add_clip_path(zero.svg_path) | |
for (let index = 0; index < line_paths_d3.length; index++) { | |
this.drawn_lines_d3[index] = new Line(svg, line_paths_d3[index].path, line_paths_d3[index].delay) | |
} | |
for (let index = 0; index < line_paths_ten.length; index++) { | |
this.drawn_lines_ten[index] = new Line(svg, line_paths_ten[index].path, line_paths_ten[index].delay) | |
} | |
var tid = setInterval(loop_sequence, 5000); | |
var is_d3_next = true | |
var thisobject = this | |
function loop_sequence() { | |
if (is_d3_next) { | |
thisobject.flash_d3() | |
} else { | |
thisobject.flash_ten() | |
} | |
is_d3_next = !is_d3_next | |
} | |
loop_sequence() | |
// function abortTimer() { | |
// clearInterval(tid); | |
// } | |
} | |
flash_d3() { | |
for (let index = 0; index < this.drawn_lines_d3.length; index++) { | |
this.drawn_lines_d3[index].flash() | |
} | |
this.shapes.dee.flash() | |
this.shapes.three.flash() | |
} | |
flash_ten() { | |
for (let index = 0; index < this.drawn_lines_ten.length; index++) { | |
this.drawn_lines_ten[index].flash() | |
} | |
this.shapes.one.flash() | |
this.shapes.zero.flash() | |
} | |
} | |
class Line { | |
constructor(svg, shape_path, delay) { | |
var path = svg.append("path") | |
.attr("d", shape_path) | |
.attr("stroke", "#888") | |
.attr("stroke-width", "1") | |
.attr("fill", "none"); | |
var totalLength = path.node().getTotalLength(); | |
this.path = path | |
this.total_length = totalLength | |
this.delay = delay | |
this.path.attr("stroke-dasharray", totalLength + " " + totalLength) | |
this.path.attr("stroke-dashoffset", this.total_length) | |
} | |
debug_draw() { | |
this.path.attr("stroke-dasharray", "2 2") | |
} | |
flash() { | |
this.path.attr("stroke-dashoffset", this.total_length) | |
this.path.transition() | |
.delay(this.delay) | |
.duration(3500) | |
.ease(d3.easePolyInOut.exponent(0.4)) | |
.attr("stroke-dashoffset", -this.total_length) | |
// .on("end", repeat); | |
} | |
} | |
class ShapeCircle { | |
constructor(svg, name, color_start, start_angle) { | |
this.start_angle = start_angle | |
//this.conf = { orange: {}, arcs: { count: 6, start: 0, end: 1, size: 0.22 }, col: { range: 40 }, radius: { inner: 0, outer: 1200 }, offset:{x:-300,y:-300} } | |
this.conf = { swing: 130, arcs: { centre_angle: 2.7, count: 25, start: 0, end: 1, size: 0.22 }, col: { range: 40 }, radius: { inner: 0, outer: 1200 }, offset: { x: -300, y: -300 } } | |
this.conf.col.start = color_start | |
this.y_offset = 0 | |
this.clip_path_id = "clip_path_" + name | |
this.clipped_container_group = svg.append('g').attr("clip-path", `url(#${this.clip_path_id})`) | |
this.rotator_container_group = this.clipped_container_group.append('g').attr('transform', `translate(${this.conf.offset.x}, ${this.conf.offset.y})`) | |
//this.rotating_inner_group = this.rotator_container_group.append('g') | |
this.arc = d3.arc() | |
this.arc_def = { innerRadius: this.conf.radius.inner, outerRadius: this.conf.radius.outer } | |
this.data = [] | |
for (let index = 0; index < this.conf.arcs.count; index++) { | |
this.data.push({ now: Math.random() }) | |
} | |
this.add_color_wheel() | |
} | |
fadeupdown(t) { | |
return t < 0.5 ? 2 * t : 2 * (1 - t) // converts 0->1 to 0->1->0 | |
} | |
add_color_wheel() { | |
var thisobject = this | |
this.pie_arcs = this.rotator_container_group.selectAll(".pie_arc") | |
.data(this.data) | |
.enter() | |
.append('path') | |
.attr("class", "pie_arc") | |
.attr('d', function (d) { | |
thisobject.arc_def.startAngle = d.now | |
thisobject.arc_def.endAngle = d.now + 0.2 | |
return thisobject.arc(thisobject.arc_def) | |
}) | |
.style('fill', (d, i) => `hsl(${this.conf.col.start + Math.random() * this.conf.col.range},100%,70%)`) | |
.style('opacity', 0.9) | |
} | |
debug_show_clip() { | |
if (this.clip_path) { | |
this.clipped_container_group.append('path') | |
.attr('d', this.clip_path) | |
.style('fill', 'none') | |
.style('stroke', 'green') | |
.style('opacity', 0.5) | |
} | |
return this | |
} | |
gaussian_rand(rounds) { | |
var rounds = rounds ? rounds : 6 | |
var rand = 0; | |
for (var i = 0; i < rounds; i += 1) { | |
rand += Math.random(); | |
} | |
return rand / rounds; | |
} | |
randomize_data() { | |
var prior | |
for (let index = 0; index < this.data.length; index++) { | |
prior = this.data[index].now | |
// rand = (Math.random() + Math.random() + Math.random() + Math.random() + Math.random() + Math.random())/7 | |
this.data[index].now = this.conf.arcs.centre_angle - Math.PI + 2 * Math.PI * this.gaussian_rand() | |
this.data[index].interpolator = d3.interpolate(prior, this.data[index].now) | |
} | |
} | |
flash() { | |
var thisobject = this | |
this.randomize_data() | |
this.pie_arcs.merge(this.pie_arcs) | |
.transition() | |
.duration(3500) | |
.ease(d3.easePolyInOut.exponent(0.4)) | |
.styleTween("opacity", function (d, i) { | |
return function (t) { | |
return thisobject.fadeupdown(t) | |
} | |
}) | |
//.style('fill', (d, i) => `hsl(${Math.random() * thisobject.color_start},100%,70%)`) | |
// .attr('d', this.arc({ innerRadius: this.conf.radius.inner, outerRadius: this.conf.radius.outer, startAngle: 0.5, endAngle: 0.22 })) | |
.attrTween("d", function (d, i) { | |
return function (t) { | |
thisobject.arc_def.startAngle = d.interpolator(t) | |
thisobject.arc_def.endAngle = thisobject.arc_def.startAngle + 0.2 | |
return thisobject.arc(thisobject.arc_def) | |
} | |
}) | |
} | |
add_clip_path(clip_path) { | |
this.clip_path = clip_path | |
this.clipped_container_group.append("svg:clipPath") | |
.attr("id", this.clip_path_id) | |
.append('path') | |
.attr('d', this.clip_path) | |
return this | |
} | |
} | |
var dee = {} | |
dee.height = 200 | |
dee.thickness = dee.height * 20 / 90 | |
dee.dtrailer = dee.thickness * 7.75 / 20 | |
dee.overshoot = dee.height / 2 | |
dee.radius_inner = dee.height / 2 - dee.thickness | |
dee.radius_mid = dee.height / 2 - dee.thickness / 2 | |
dee.radius_outer = dee.height / 2 | |
dee.svg_path = '' | |
dee.svg_path += `M0,0` | |
dee.svg_path += `h${dee.dtrailer}` | |
dee.svg_path += `a${dee.radius_outer}, ${dee.radius_outer} 0 1 1 0, ${2 * dee.radius_outer}` | |
dee.svg_path += `h${-dee.dtrailer}` | |
dee.svg_path += `v${-dee.thickness}` | |
dee.svg_path += `h${dee.dtrailer}` | |
dee.svg_path += `a${dee.radius_inner}, ${dee.radius_inner} 0 1 0 0, ${-2 * dee.radius_inner}` | |
dee.svg_path += `h${-dee.dtrailer} z`//"M0,0 h7.75 a45.5, 45.5 0 1 1 0, 91 h-7.75 v-20 h7.75 a25.5, 25.5 0 1 0 0, -51 h -7.75 z" | |
dee.lines = {} | |
dee.lines.outer_circle = '' | |
dee.lines.outer_circle += `M0,0` | |
dee.lines.outer_circle += `h${dee.dtrailer}` | |
dee.lines.outer_circle += `a${dee.radius_outer}, ${dee.radius_outer} 0 1 1 0, ${2 * dee.radius_outer}` | |
dee.lines.outer_circle += `a${dee.radius_outer}, ${dee.radius_outer} 0 1 1 0, -${2 * dee.radius_outer}` | |
dee.lines.inner_circle = '' | |
dee.lines.inner_circle += `M${dee.dtrailer},${dee.thickness}` | |
dee.lines.inner_circle += `a${dee.radius_inner}, ${dee.radius_inner} 0 1 1 0, ${2 * dee.radius_inner}` | |
dee.lines.inner_circle += `a${dee.radius_inner}, ${dee.radius_inner} 0 1 1 0, -${2 * dee.radius_inner}` | |
dee.lines.path3 = `M0,-1000 v2000` | |
var three = {} | |
three.height = 200 | |
three.thickness = three.height * 20 / 91 | |
three.hollow_height = three.height * 15.5 / 91 | |
three.x_offset = three.height * 36.2510 / 91 | |
three.outer_radius = three.height * 27.75 / 91 | |
three.inner_radius = three.height * 7.75 / 91 | |
three.mid_radius = (three.thickness + three.hollow_height) / 2 | |
three.left_cutoff_radius = three.height * 53.6895 / 91 | |
three.inner_h = three.height * 7.75 / 91 | |
three.outer_h = three.height * 32 / 91 | |
three.mid_h = three.height * 13.2526 / 91 | |
three.vertex_dx = three.height * 21.331 / 91 | |
three.vertex_dy = three.height * 45.5 / 91 | |
three.cutoff_dx = three.height * 18.7464 / 91 | |
three.overshoot = 100 | |
// a x_radius y_radius x_axis_rotation large_arc_flag sweep_flag dx_of_end_point dy_of_end_point | |
three.svg_path = `M${three.x_offset},0` // initial offset | |
three.svg_path += `h${three.outer_h}` // top | |
three.svg_path += `a${three.outer_radius},${three.outer_radius} 0 0 1 ${three.vertex_dx},${three.vertex_dy}` // right outer top arc | |
three.svg_path += `a${three.outer_radius},${three.outer_radius} 0 0 1 ${-three.vertex_dx},${three.vertex_dy}` // right outer bottom arc | |
three.svg_path += `h${-three.outer_h}` // bottom | |
three.svg_path += `a${three.left_cutoff_radius},${three.left_cutoff_radius} 0 0 0 ${three.cutoff_dx}, ${-three.thickness}` // bottom cutoff arc | |
three.svg_path += `h${three.mid_h}` // lower hollow bottom | |
three.svg_path += `a${three.inner_radius},${three.inner_radius} 0 1 0 0, ${-three.hollow_height}` // lower hollow arc | |
three.svg_path += `h${-three.inner_h}` // lower hollow top | |
three.svg_path += `a${three.left_cutoff_radius},${three.left_cutoff_radius} 0 0 0 0, ${-three.thickness}` // mid cuttoff arc | |
three.svg_path += `h${three.inner_h}` // upper hollow bottom | |
three.svg_path += `a${three.inner_radius},${three.inner_radius} 0 1 0 0, ${-three.hollow_height}` // upper hollow arc | |
three.svg_path += `h${-three.mid_h}` // upper hollow top | |
three.svg_path += `a${three.left_cutoff_radius},${three.left_cutoff_radius} 0 0 0 ${-three.cutoff_dx}, ${-three.thickness}` // top cutoff arc | |
three.svg_path += `z` | |
three.lines = {} | |
three.lines.outer_upper = `M${three.x_offset + three.outer_h},0` // initial offset | |
three.lines.outer_upper += `a${three.outer_radius},${three.outer_radius} 0 0 1 ${0},${2 * three.outer_radius}` // right outer top arc | |
three.lines.outer_upper += `a${three.outer_radius},${three.outer_radius} 0 0 1 ${0},${-2 * three.outer_radius}` // right outer top arc | |
three.lines.outer_lower = `M${three.x_offset + three.outer_h},${three.height}` // initial offset | |
three.lines.outer_lower += `a${three.outer_radius},${three.outer_radius} 0 0 0 ${0},-${2 * three.outer_radius}` // right outer top arc | |
three.lines.outer_lower += `a${three.outer_radius},${three.outer_radius} 0 0 0 ${0},${2 * three.outer_radius}` // right outer top arc | |
three.lines.top = `M${-500},${0} h${700}` | |
three.lines.inner_upper_top = `M${-1000},${three.thickness} h${900}` | |
three.lines.inner_upper_bottom = `M${-500},${three.height - three.thickness} h${1200}` | |
three.lines.inner_lower_top = `M${-1600},${three.height} h${1000}` | |
three.lines.bottom = `M${-1600},${three.height} h${1000}` | |
three.lines.cutoff = `M${-101},${three.height / 2}` // initial offset | |
three.lines.cutoff += `a${three.left_cutoff_radius},${three.left_cutoff_radius} 0 0 0 ${2 * three.left_cutoff_radius},${0}` // right outer top arc | |
three.lines.cutoff += `a${three.left_cutoff_radius},${three.left_cutoff_radius} 0 0 0 ${-2 * three.left_cutoff_radius},${0}` // right outer top arc | |
three.lines.inner_upper = `M${three.x_offset + three.outer_h},${three.thickness}` // initial offset | |
three.lines.inner_upper += `a${three.inner_radius},${three.inner_radius} 0 0 1 ${0},${2 * three.inner_radius}` // right outer top arc | |
three.lines.inner_upper += `a${three.inner_radius},${three.inner_radius} 0 0 1 ${0},${-2 * three.inner_radius}` // right outer top arc | |
three.lines.inner_lower = `M${three.x_offset + three.outer_h},${2 * three.inner_radius + 2 * three.thickness}` // initial offset | |
three.lines.inner_lower += `a${three.inner_radius},${three.inner_radius} 0 0 1 ${0},${2 * three.inner_radius}` // right outer top arc | |
three.lines.inner_lower += `a${three.inner_radius},${three.inner_radius} 0 0 1 ${0},${-2 * three.inner_radius}` // right outer top arc | |
var zero = {} | |
zero.height = 200 | |
zero.x_offset = zero.height * 0.45 | |
zero.aspect_ratio = 0.75 | |
zero.thickness = zero.height * 20 / 91 | |
zero.outer_radius = zero.height / 2 | |
zero.outer_y_radius = zero.height / 2 | |
zero.outer_x_radius = zero.outer_y_radius * zero.aspect_ratio | |
zero.inner_radius = zero.outer_radius - zero.thickness | |
zero.inner_x_radius = zero.outer_x_radius - zero.thickness | |
zero.inner_y_radius = zero.outer_radius - zero.thickness | |
zero.mid_radius = zero.outer_radius - zero.thickness / 2 | |
zero.svg_path = '' | |
zero.svg_path += `M${zero.x_offset},${zero.outer_radius}` // | |
zero.svg_path += `a${zero.outer_x_radius},${zero.outer_y_radius} 0 0 1 ${2 * zero.outer_x_radius} ,0` // | |
zero.svg_path += `a${zero.outer_x_radius},${zero.outer_y_radius} 0 0 1 -${2 * zero.outer_x_radius},0` // | |
zero.svg_path += `h${zero.thickness}` // | |
zero.svg_path += `a${zero.inner_x_radius},${zero.inner_y_radius} 0 0 0 ${2 * zero.inner_x_radius} ,0` // | |
zero.svg_path += `a${zero.inner_x_radius},${zero.inner_y_radius} 0 0 0 -${2 * zero.inner_x_radius},0` // | |
zero.svg_path += `h-${zero.thickness}` // | |
zero.svg_path += `z` | |
zero.lines = {} | |
zero.lines.outer = '' | |
zero.lines.outer += `M${zero.x_offset},${zero.outer_radius}` // | |
zero.lines.outer += `a${zero.outer_x_radius},${zero.outer_y_radius} 0 0 0 ${2 * zero.outer_x_radius} ,0` // | |
zero.lines.outer += `a${zero.outer_x_radius},${zero.outer_y_radius} 0 0 0 -${2 * zero.outer_x_radius},0` // | |
zero.lines.outer += `z` | |
zero.lines.inner = '' | |
zero.lines.inner += `M${zero.x_offset + zero.thickness},${zero.outer_radius}` // | |
zero.lines.inner += `a${zero.inner_x_radius},${zero.inner_y_radius} 0 0 1 ${2 * zero.inner_x_radius} ,0` // | |
zero.lines.inner += `a${zero.inner_x_radius},${zero.inner_y_radius} 0 0 1 -${2 * zero.inner_x_radius},0` // | |
zero.lines.inner += `z` | |
var one = {} | |
one.height = 200 | |
one.thickness = zero.height * 20 / 91 | |
one.notch_height = one.height / 4 | |
one.notch_width = one.height / 12 | |
one.ledge_width = one.notch_width | |
one.ledge_height = one.height / 5 | |
one.svg_path = `M0,${one.notch_height}` | |
one.svg_path += `l${one.notch_width},${-one.notch_height}` // slope of notch | |
one.svg_path += `h${one.thickness}` // top | |
one.svg_path += `v${one.height - one.ledge_height}` // right side | |
one.svg_path += `h${one.ledge_width}` // right ledge | |
one.svg_path += `v${one.ledge_height}` // right side bottom | |
one.svg_path += `h-${one.thickness + 2 * one.ledge_width}` // base | |
one.svg_path += `v${-one.ledge_height}` // left side bottom | |
one.svg_path += `h${one.ledge_width}` // left ledge | |
one.svg_path += `v-${one.height - one.ledge_height - one.notch_height}` // left side | |
one.svg_path += `h${-one.notch_width}` // notch underside | |
one.lines = {} | |
one.lines.top = `M-1000,0 h2000` | |
one.lines.bottom = `M-1000,${one.height} h2000` | |
one.lines.left = `M${one.notch_width},-1000 v 2000` | |
one.lines.right = `M${one.notch_width + one.thickness},-1000 v 2000` | |
one.lines.notch = `M-${one.notch_width * (12 - 1)},${one.notch_height * 12} l${one.notch_width * 24},-${one.notch_height * 24}` | |
one.lines.ledge = `M-1000,${one.height - one.ledge_height} h2000` | |
var line_paths_d3 = [] | |
var line_paths_ten = [] | |
for (const line_path in one.lines) { | |
line_paths_ten.push({ delay: Math.random() * 1000, path: one.lines[line_path] }) | |
} | |
for (const line_path in zero.lines) { | |
line_paths_ten.push({ delay: Math.random() * 1000, path: zero.lines[line_path] }) | |
} | |
for (const line_path in dee.lines) { | |
line_paths_d3.push({ delay: Math.random() * 1000, path: dee.lines[line_path] }) | |
} | |
for (const line_path in three.lines) { | |
line_paths_d3.push({ delay: Math.random() * 1000, path: three.lines[line_path] }) | |
} | |
</script> | |
</head> | |
<body onLoad="main()"> | |
<!-- <button id="button1">D3</button> | |
<button id="button2">10</button> | |
<br> --> | |
<div> | |
<svg id='svg1'></svg> | |
</div> | |
</body> | |
</html> |