This is an example of a technique that's shown in the Vue manual, basically
you maintain two pieces of state, use one as the backing store and use a watcher
to update the derived state using tweening mechanisms such as TweenLite in
this case. Shown with a few hacks that are necessary to get it working with
the d3-cluster
node representation.
<template>
<div>
<svg viewBox="0 0 1024 768" width="1024" height="768"
xmlns="http://www.w3.org/2000/svg">
<circle v-for="descendant in tweenedDescendants"
:cx="descendant.x"
:cy="descendant.y"
r="10"
stroke="red" fill="grey"/>
</svg>
<button v-on:click="relayout">Relayout</button>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import {hierarchy, cluster, HierarchyNode} from 'd3';
import _ from 'lodash';
import {TweenLite} from 'gsap';
const familyData = {
"name": "Eve",
"children": [
{
"name": "Cain"
},
{
"name": "Seth",
"children": [
{
"name": "Enos"
},
{
"name": "Noam"
}
]
},
{
"name": "Abel"
},
{
"name": "Awan",
"children": [
{
"name": "Enoch"
}
]
},
{
"name": "Azura"
}
]
};
interface FamilyDatum {
};
function initialCluster(val: any): HierarchyNode<FamilyDatum> {
const theHierarchy = hierarchy(val);
const clusterLayout = cluster();
clusterLayout.size([1024, 768]);
clusterLayout(theHierarchy);
return theHierarchy;
}
export default Vue.extend({
data() {
return {
hierarchyRoot: initialCluster(familyData) as HierarchyNode<FamilyDatum>,
tweenedHierarchyRoot: initialCluster(familyData) as HierarchyNode<FamilyDatum>
};
},
mounted() {
window.setInterval(this.relayout, 1000);
},
methods: {
relayout() {
const clusterLayout = cluster();
// these hacks necessary because cluster() itself does some array
// mutations that fool the reactivity
const clonedHierarchy = _.clone(this.hierarchyRoot);
clusterLayout.size([Math.random() * 1024, Math.random() * 768]);
clusterLayout(clonedHierarchy);
this.hierarchyRoot = clonedHierarchy;
},
},
computed: {
descendants(): HierarchyNode<FamilyDatum>[] {
return this.hierarchyRoot.descendants();
},
tweenedDescendants(): HierarchyNode<FamilyDatum>[] {
return this.tweenedHierarchyRoot.descendants();
},
},
watch: {
// No deep watch needed because we always reassign the whole
// array
hierarchyRoot(val, oldVal) {
console.log("inside handler function");
// We know that descendants() function returns references to
// the nodes that can be directly modified by mutation.
// Because the x and y values have already been declared in the
// data() method -- that is, cluster() was already called once
// to add the properties -- Vue knows that it should react to changes
// on these properties.
const targetDescendants = val.descendants();
const tweenedDescendants = this.tweenedHierarchyRoot.descendants();
for (var i = 0; i < tweenedDescendants.length; i++) {
const node = tweenedDescendants[i];
const targetX = targetDescendants[i].x;
const targetY = targetDescendants[i].y;
TweenLite.to(node, 0.5, {x: targetX, y: targetY});
}
}
}
});
</script>
<style lang="less">
</style>
This is based on a technique from the Vue manual.