I want to create a d3.js visualization - that showcases different stages of a polygon shape - so having it animate/morph changing shape and scale.
Where the user click's on the little rectangles they essentially scroll through an array of data of "dimensions/coordinates" that showcase a "frame" of a shape (like a film frame)
so imagine - we have these shapes in an array --- a way of morphing/animating one to the other -- or/and using the rectangles to click and switch to that shape.
So the data would be an array of shapes/positions
[
{"coordinateX": 20, "coordinateY": 30, "shape": [{"x":0.0, "y":25.0},
{"x":8.5,"y":23.4}, {"x":13.0,"y":21.0}, {"x":19.0,"y":15.5}], "frame": 0, "date": 2023-05-23 10:00},
{"coordinateX": 19, "coordinateY": 29, "shape": [{"x":0.3, "y":23.0},
{"x":8.5,"y":23.4}, {"x":33.0,"y":21.0}, {"x":19.0,"y":45.5}], "frame": 1, "date": 2023-05-24 11:00},
{"coordinateX": 19, "coordinateY": 30, "shape": [{"x":0.0, "y":25.0},
{"x":5.5,"y":23.7}, {"x":11.0,"y":21.0}, {"x":12.0,"y":13.5}], "frame": 2, "date": 2023-05-25 11:00}
]
Dimensions and coordinates - scale and adjustment of shape per frame - chronological order
so morph the existing shape - into others..
here is some code samples to draw a static polygon
//example of drawing a polygon Proper format for drawing polygon data in D3 http://jsfiddle.net/4xXQT/
var vis = d3.select("body").append("svg")
.attr("width", 1000)
.attr("height", 667),
scaleX = d3.scale.linear()
.domain([-30,30])
.range([0,600]),
scaleY = d3.scale.linear()
.domain([0,50])
.range([500,0]),
poly = [{"x":0.0, "y":25.0},
{"x":8.5,"y":23.4},
{"x":13.0,"y":21.0},
{"x":19.0,"y":15.5}];
vis.selectAll("polygon")
.data([poly])
.enter().append("polygon")
.attr("points",function(d) {
return d.map(function(d) { return [scaleX(d.x),scaleY(d.y)].join(","); }).join(" ");})
.attr("stroke","black")
.attr("stroke-width",2);
with the data array -
http://jsfiddle.net/5k9z30vh/
I want to figure out a way of aniamting/morphing polygons.
how would I look at animating/morphing - through the polygons - https://www.youtube.com/watch?v=K1zHa1sAno0 Loop D3 animation of polygon vertex
var vis = d3.select("body").append("svg")
.attr("width", 1000)
.attr("height", 667),
scaleX = d3.scale.linear()
.domain([-30,30])
.range([0,600]),
scaleY = d3.scale.linear()
.domain([0,50])
.range([500,0]),
poly = [{"x":0.0, "y":25.0},
{"x":8.5,"y":23.4},
{"x":13.0,"y":21.0},
{"x":19.0,"y":15.5}];
var data = [{
"coordinateX": 20,
"coordinateY": 30,
"shape": [{
"x": 0.0,
"y": 25.0
},
{
"x": 8.5,
"y": 23.4
}, {
"x": 13.0,
"y": 21.0
}, {
"x": 19.0,
"y": 15.5
}
],
"frame": 0,
"date": "2023-05-23 10:00"
},
{
"coordinateX": 19,
"coordinateY": 29,
"shape": [{
"x": 0.3,
"y": 23.0
},
{
"x": 8.5,
"y": 23.4
}, {
"x": 33.0,
"y": 21.0
}, {
"x": 19.0,
"y": 45.5
}
],
"frame": 1,
"date": "2023-05-24 10:00"
},
{
"coordinateX": 19,
"coordinateY": 30,
"shape": [{
"x": 0.0,
"y": 25.0
},
{
"x": 5.5,
"y": 23.7
}, {
"x": 11.0,
"y": 21.0
}, {
"x": 12.0,
"y": 13.5
}
],
"frame": 2,
"date": "2023-05-25 10:00"
}
]
vis.selectAll("polygon")
.data([data[0].shape])
.enter().append("polygon")
.attr("points",function(d) {
return d.map(function(d) { return [scaleX(d.x),scaleY(d.y)].join(","); }).join(" ");})
.attr("stroke","black")
.attr("stroke-width",2);
hand drawing a polygon https://jsfiddle.net/7pwaLfth/
https://lucidar.me/en/d3.js/part-06-basic-shapes/ https://gist.github.com/RiseupDev/b07f7ccc1c499efc24e9
/// 30th May 2023 update
I have managed to create this demo that shows the concept working - the polygon morphs into the next -- this would be useful to showcase a "wound" healing from day 1 to day x -- essentially it would shrink into eventual non-existence..
I would like to fix/add these features/issues
-- there is a bug if you click on autoplay too much like event bubbling - if there is a way to avoid a malfunction if the user clicks too quickly
- I'd like to add different speeds/fps options -- be able to watch a frame each second 1fps, or 2 frames a second 2fp ---- to try and make it more fluent watching a wound heal in timelapse almost --- fix the overall structure of the visualization -- create a sketlon - g placeholders -- fix the ability to scale the visualization cleanly
- the polygon holder has no transformation to really align things properly -- the polygons are really big - I dont know how to just make them smaller - and if I reduce the canvas size then polygons become clipped --I dont know how to clean up the attachment of the little rectangle bar controller - it just seemed created/appended in a clunky manner
jsfiddle http://jsfiddle.net/5kqLftzy/
code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 Drawing</title>
<script src="https://cdn.jsdelivr.net/d3js/3.5.9/d3.min.js"></script>
</head>
<body>
<h3>Draw a polygon 2</h3>
<div>
<button onclick="beginAutoPlay()">Start Autoplay</button>
<button onclick="stopAutoPlay()">Stop Autoplay</button>
</div>
<script>
let polygons = [
{
"id": "1",
"plot": "469,59.5625,246,256.5625,348,499.5625,527,482.5625,653,457.5625,693,406.5625,732,371.5625,743,260.5625,690,257.5625,650,172.5625,622,143.5625,589,98.5625,544,90.5625,511,55.5625"
},
{
"id": "2",
"plot": "462,79.5625,258,258.5625,361,470.5625,527,482.5625,653,457.5625,693,406.5625,718,363.5625,705,284.5625,684,241.5625,650,172.5625,622,143.5625,587,108.5625,544,90.5625,513,95.5625"
},
{
"id": "3",
"plot": "410,173.5625,324,259.5625,361,470.5625,527,435.5625,604,379.5625,672,374.5625,718,363.5625,692,281.5625,662,231.5625,650,172.5625,622,143.5625,582,132.5625,544,90.5625,513,95.5625"
},
{
"id": "4",
"plot": "422,186.5625,359,267.5625,374,430.5625,527,435.5625,604,379.5625,672,374.5625,691,325.5625,692,281.5625,662,231.5625,650,172.5625,622,143.5625,582,132.5625,543,111.5625,503,124.5625"
},
{
"id": "5",
"plot": "486,200.5625,359,267.5625,394,393.5625,527,435.5625,604,379.5625,617,332.5625,634,292.5625,624,252.5625,602,216.5625,606,192.5625,596,171.5625,585,148.5625,573,175.5625,525,175.5625"
},
{
"id": "6",
"plot": "486,200.5625,440,264.5625,419,370.5625,527,435.5625,604,379.5625,580,359.5625,589,330.5625,607,306.5625,610,277.5625,604,252.5625,588,246.5625,577,238.5625,549,220.5625,514,219.5625"
},
{
"id": "7",
"plot": "485,272.5625,469,294.5625,456,359.5625,491,396.5625,604,379.5625,580,359.5625,589,330.5625,607,306.5625,610,277.5625,604,252.5625,588,246.5625,571,265.5625,548,270.5625,521,277.5625"
},
{
"id": "8",
"plot": "499,297.5625,483,310.5625,488,333.5625,492,356.5625,534,366.5625,566,349.5625,589,330.5625,607,306.5625,610,277.5625,593,273.5625,578,275.5625,571,265.5625,548,270.5625,521,277.5625"
}
]
let width = 1000;
let height = 600;
let polyMaxY = 300
var vis = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height),
scaleX = d3.scale.linear()
.domain([-30, 30])
.range([0, 600]),
scaleY = d3.scale.linear()
.domain([0, 50])
.range([polyMaxY, 0])
let polyStage = vis.append("g")
.attr("class", "polyStage")
.attr("transform", "0 0")
let thePolygon = polyStage.append("polygon")
.attr("id", "mainPoly")
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("points", polygons[0])
let paddingBottom = 50
let chooserX = 100
let chooserY = polyMaxY + paddingBottom
let buttonWidth = 7
let buttonHeight = 50
let buttonRightPadding = 3
let selectedColor = "aqua"
let rectColor = "black"
let currentPoly = 0
let animationDuration = 2000
let goRightDelay = 1000
let autoplay = true;
let framePicker = vis.append("g")
.attr("class", "framePicker")
let buttonGroups = framePicker.selectAll("g")
.data(polygons)
.enter().append("g")
.attr("transform", buttonGroupTranslate)
buttonGroups.append("rect")
.attr("id", (d, i) => "chooserRect" + i)
.attr("width", buttonWidth)
.attr("height", buttonHeight)
.style("fill", rectColor)
buttonGroups.on("click", navigateTo)
// Listen to left and right arrow
d3.select("body").on("keydown", function() {
let keyCode = d3.event.keyCode
d3.event.stopPropagation()
if (keyCode == 37) { // 37 is left arrow
goLeft()
}
if (keyCode == 39) {
goRight()
}
})
function goLeft() {
let nextIndex = currentPoly - 1
if (nextIndex < 0) {
nextIndex = polygons.length - 1
}
navigateTo("", nextIndex)
}
function goRight() {
let nextIndex = currentPoly + 1
if (nextIndex >= polygons.length) {
nextIndex = 0
}
navigateTo("", nextIndex)
}
function buttonGroupTranslate(d, i) {
let groupX = chooserX + (i * (buttonWidth + buttonRightPadding))
return "translate(" + groupX + "," + chooserY + ")"
}
function navigateTo(d, i) {
buttonGroups.selectAll("rect").style("fill", rectColor) // reset all colors
d3.select("#chooserRect" + i).style("fill", selectedColor) // set color for the current one
d3.select("#mainPoly").transition()
.duration(animationDuration)
.attr("points", polygons[i]["plot"])
currentPoly = i
}
let timerCallBack = function() {
return function() {
if (autoplay) {
goRight()
d3.timer(timerCallBack(), animationDuration + goRightDelay) // Set up a new timer
}
return true; // Cancel the current timer
}
}
function beginAutoPlay() {
autoplay = true
d3.timer(timerCallBack(), animationDuration + goRightDelay)
}
function stopAutoPlay() {
autoplay = false
}
navigateTo("", currentPoly)
</script>
</body>
</html>
from
D3 animation morphing polygon shape per frame - chronological order