<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ZingChart Demo: Xmas Tree</title>
<script nonce="undefined" src="https://cdn.zingchart.com/zingchart.min.js"></script>
<style>
.zc-body {
margin: 10px;
padding: 10px;
background: #fbfbfb;
}
.zc-info {
margin-bottom: 10px;
border-bottom: 1px solid #ccc;
}
.zc-info ol {
margin: 0 0 22px;
padding: 0 0 0 1rem;
font-size: 14px;
}
.zc-info p {
color: #dc1257;
}
.zc-controls {
padding: 5px 0;
}
.zc-controls input {
height: 40px;
border: 1px solid #ebebeb;
border-radius: 4px;
box-sizing: border-box;
font-size: 1rem;
padding: 0 10px;
}
.zc-controls input:focus {
font-size: 1rem;
}
.zc-controls button {
color: #fff;
background: #073c4e;
border: 1px solid #ebebeb;
border-radius: 4px;
cursor: pointer;
font-size: .8125rem;
padding: 0 10px;
height: 40px;
}
.zc-controls button[disabled] {
cursor: not-allowed;
}
</style>
</head>
<body class="zc-body">
<div class="zc-info">
<ol>
<li>Enter your full name in the text box and press <strong>Render Tree</strong>.</li>
<li>Based on your name, a unique, force-directed graph will be generated. While the structure will always be the same, the position of the nodes may (slightly) differ every time.</li>
<li>After render, you may make adjustments by dragging nodes around.</li>
<li>Right-click on the tree and choose <strong>View as PNG</strong>.</li>
<li>Right-click again and save your holiday tree.</li>
</ol>
<p>Enjoy! Happy Holidays from ZingChart!</p>
</div>
<div class="zc-controls">
<input type="text" id="renderInput" placeholder="Your full name here" value="world">
<button id="renderBtn">Render Tree</button>
</div>
<div id="myChart"></div>
<script>
ZC.LICENSE = ["569d52cefae586f634c54f86dc99e6a9", "b55b025e438fa8a98e32482b5f768ff5"]; // INIT
// -----------------------------
// Define Module Location
zingchart.MODULESDIR = "https://cdn.zingchart.com/modules/";
// DEFINE CHART
// -----------------------------
// Main chart render location
let chartId = 'myChart';
// DOM ELEMENTS
// -----------------------------
let renderBtn = document.querySelector('#renderBtn');
let renderInput = document.querySelector('#renderInput');
// RENDER BUTTON EVENTS
// -----------------------------
renderBtn.addEventListener('click', renderChart);
// HELPER FNS
// -----------------------------
// Render Chart
function renderChart() {
// Disable render button
renderBtn.setAttribute('disabled', 'disabled')
// Define constants
const COLORS = [
'#99ccff #336699',
'#6CB359 #0F4800',
'#9A5E9A #430443',
'#FFA153 #AC4E00',
'#FF653D #A62300',
'#57B8C6 #095B67',
'#FFE66D #AF930B',
'#EF668F #9D0A36'
];
const NAME = renderInput.value.trim() || 'Santa Claus';
let width = '100%';
let height = 560;
let colorSequence = 0;
let sequence = 0;
let tick = null;
// Define data
let chartConfig = {
type: 'tree',
title: {
text: 'Hello ' + NAME + ', here\'s your holiday tree',
color: '#333333',
fontSize: '17px',
padding: '25px'
},
options: {
aspect: 'graph',
attractionConstant: 0.1,
minSize: '16px',
maxSize: '16px',
minLinkWidth: '1px',
maxLinkWidth: '1px',
repulsionConstant: 400,
repulsionDistanceFactor: 5,
springLength: '35px',
textAttr: 'id',
weightedLinks: 1,
weightedNodes: 1,
link: {
lineColor: '#000000'
},
node: {
backgroundColor: 'none',
backgroundPosition: '45% 45%',
backgroundRepeat: 'no-repeat',
borderWidth: 0,
fillOffsetX: '-5px',
fillOffsetY: '-5px',
fillType: 'radial',
label: {
visible: false
},
tooltip: {
visible: false
}
}
},
plotarea: {
margin: '60px 40px 40px 60px'
},
images: [{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas0.png',
visible: false
},
{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas1.png',
visible: false
},
{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas2.png',
visible: false
},
{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas3.png',
visible: false
},
{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas4.png',
visible: false
},
{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas5.png',
visible: false
},
{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas6.png',
visible: false
},
{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas7.png',
visible: false
},
{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas8.png',
visible: false
},
{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas9.png',
visible: false
}
],
shapes: [],
source: {
fontSize: '12px',
margin: 'auto 25px 25px 25px',
text: 'Happy Holidays from ZingChart!'
},
series: []
};
// Build layout
let rows = Math.round(height / 20);
let cols = Math.round(width / 20);
let iCharCode;
for (let row = 0; row < rows; row++) {
iCharCode = NAME.charCodeAt(row % NAME.length);
chartConfig.shapes.push({
type: 'star5',
flat: true,
backgroundColor: COLORS[iCharCode % COLORS.length],
size: 4 + iCharCode % 4,
x: 10,
y: 10 + row * 20
}, {
type: 'star5',
flat: true,
backgroundColor: COLORS[iCharCode % COLORS.length],
size: 4 + iCharCode % 4,
x: width - 10,
y: 10 + row * 20
});
}
for (let col = 1; col < cols - 1; col++) {
iCharCode = NAME.charCodeAt(col % NAME.length);
chartConfig.shapes.push({
type: 'star5',
flat: true,
backgroundColor: COLORS[iCharCode % COLORS.length],
size: 4 + iCharCode % 4,
x: 10 + col * 20,
y: 10
}, {
type: 'star5',
flat: true,
backgroundColor: COLORS[iCharCode % COLORS.length],
size: 4 + iCharCode % 4,
x: 10 + col * 20,
y: height - 10
});
}
// Render the chart
zingchart.render({
id: chartId,
width,
height,
output: 'canvas',
data: chartConfig,
events: {
load: function() {
tick = window.setInterval(fnStep, 150);
}
}
});
function fnStep() {
let i, sSource, sTarget, aPairs, oLink;
let iCharCode = NAME.charCodeAt((sequence + colorSequence) % NAME.length);
let iColorCharCode = NAME.charCodeAt(colorSequence % NAME.length);
// get data from graph
let aData = zingchart.exec(chartId, 'tree.getdata') || [];
// count links for each node
let oNodesLinks = {},
aNodesIds = [],
aLinksIds = [];
for (i = 0; i < aData.length; i++) {
if (aData[i].type === 'link') {
oNodesLinks[aData[i].source] = ++oNodesLinks[aData[i].source] || 1;
oNodesLinks[aData[i].target] = ++oNodesLinks[aData[i].target] || 1;
aLinksIds.push(aData[i].source + '-' + aData[i].target);
aLinksIds.push(aData[i].target + '-' + aData[i].source);
} else if (aData[i].type === 'node') {
aNodesIds.push(aData[i].id);
}
}
// remove all nodes with no links
if (aNodesIds.length > 2) {
for (i = 0; i < aNodesIds.length; i++) {
if (!oNodesLinks[aNodesIds[i]]) {
zingchart.exec(chartId, 'tree.removenode', {
id: aNodesIds[i]
});
}
}
}
// from time to time remove oldest leaves
if (sequence % 5 === 0 && aNodesIds.length > 5) {
for (i = 0; i < aNodesIds.length; i++) {
if (oNodesLinks[aNodesIds[i]] === 1) {
zingchart.exec(chartId, 'tree.removenode', {
id: aNodesIds[i]
});
sequence++;
return;
}
}
}
// from time to time remove nodes with too many links
if (sequence % 13 === 0 && aNodesIds.length > 5) {
for (i = 0; i < aNodesIds.length; i++) {
if (oNodesLinks[aNodesIds[i]] > 3) {
zingchart.exec(chartId, 'tree.removenode', {
id: aNodesIds[i]
});
sequence++;
return;
}
}
}
// from time to time just create a link between existing nodes
if (sequence % 7 === 0 && aNodesIds.length > 3) {
aPairs = [];
for (i = 0; i < aNodesIds.length; i++) {
if (oNodesLinks[aNodesIds[i]] === 1) {
aPairs.push(aNodesIds[i]);
if (aPairs.length === 2) {
oLink = {
source: aPairs[0],
target: aPairs[1],
value: 1 + iCharCode % 2
};
zingchart.exec(chartId, 'tree.addlink', {
data: oLink,
update: true
});
sequence++;
return;
}
}
}
}
// otherwise, create a new node
sTarget = 'n' + sequence;
oNode = {
id: sTarget,
text: sTarget,
value: 1,
style: {
backgroundImage: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas' + (sequence % 10) + '.png',
backgroundColor: COLORS[iColorCharCode % COLORS.length]
}
};
colorSequence++;
zingchart.exec(chartId, 'tree.addnode', {
data: oNode,
update: (aNodesIds.length === 0)
});
if (aNodesIds.length > 0) {
sSource = aNodesIds[iCharCode % aNodesIds.length];
oLink = {
source: sSource,
target: sTarget,
value: 1 + iCharCode % 2
};
zingchart.exec(chartId, 'tree.addlink', {
data: oLink,
update: (aNodesIds.length > 0)
});
}
sequence++;
if (sequence > 99) {
window.clearInterval(tick);
renderBtn.removeAttribute('disabled');
}
};
}
window.addEventListener('load', function() {
renderChart();
});
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ZingChart Demo: Xmas Tree</title>
<script src="https://cdn.zingchart.com/zingchart.min.js"></script>
</head>
<body class="zc-body">
<div class="zc-info">
<ol>
<li>Enter your full name in the text box and press <strong>Render Tree</strong>.</li>
<li>Based on your name, a unique, force-directed graph will be generated. While the structure will always be the same, the position of the nodes may (slightly) differ every time.</li>
<li>After render, you may make adjustments by dragging nodes around.</li>
<li>Right-click on the tree and choose <strong>View as PNG</strong>.</li>
<li>Right-click again and save your holiday tree.</li>
</ol>
<p>Enjoy! Happy Holidays from ZingChart!</p>
</div>
<div class="zc-controls">
<input type="text" id="renderInput" placeholder="Your full name here" value="world">
<button id="renderBtn">Render Tree</button>
</div>
<div id="myChart"></div>
</body>
</html>
.zc-body {
margin: 10px;
padding: 10px;
background: #fbfbfb;
}
.zc-info {
margin-bottom: 10px;
border-bottom: 1px solid #ccc;
}
.zc-info ol {
margin: 0 0 22px;
padding: 0 0 0 1rem;
font-size: 14px;
}
.zc-info p {
color: #dc1257;
}
.zc-controls {
padding: 5px 0;
}
.zc-controls input {
height: 40px;
border: 1px solid #ebebeb;
border-radius: 4px;
box-sizing: border-box;
font-size: 1rem;
padding: 0 10px;
}
.zc-controls input:focus {
font-size: 1rem;
}
.zc-controls button {
color: #fff;
background: #073c4e;
border: 1px solid #ebebeb;
border-radius: 4px;
cursor: pointer;
font-size: .8125rem;
padding: 0 10px;
height: 40px;
}
.zc-controls button[disabled] {
cursor: not-allowed;
}
// INIT
// -----------------------------
// Define Module Location
zingchart.MODULESDIR = "https://cdn.zingchart.com/modules/";
// DEFINE CHART
// -----------------------------
// Main chart render location
let chartId = 'myChart';
// DOM ELEMENTS
// -----------------------------
let renderBtn = document.querySelector('#renderBtn');
let renderInput = document.querySelector('#renderInput');
// RENDER BUTTON EVENTS
// -----------------------------
renderBtn.addEventListener('click', renderChart);
// HELPER FNS
// -----------------------------
// Render Chart
function renderChart() {
// Disable render button
renderBtn.setAttribute('disabled', 'disabled')
// Define constants
const COLORS = [
'#99ccff #336699',
'#6CB359 #0F4800',
'#9A5E9A #430443',
'#FFA153 #AC4E00',
'#FF653D #A62300',
'#57B8C6 #095B67',
'#FFE66D #AF930B',
'#EF668F #9D0A36'
];
const NAME = renderInput.value.trim() || 'Santa Claus';
let width = '100%';
let height = 560;
let colorSequence = 0;
let sequence = 0;
let tick = null;
// Define data
let chartConfig = {
type: 'tree',
title: {
text: 'Hello ' + NAME + ', here\'s your holiday tree',
color: '#333333',
fontSize: '17px',
padding: '25px'
},
options: {
aspect: 'graph',
attractionConstant: 0.1,
minSize: '16px',
maxSize: '16px',
minLinkWidth: '1px',
maxLinkWidth: '1px',
repulsionConstant: 400,
repulsionDistanceFactor: 5,
springLength: '35px',
textAttr: 'id',
weightedLinks: 1,
weightedNodes: 1,
link: {
lineColor: '#000000'
},
node: {
backgroundColor: 'none',
backgroundPosition: '45% 45%',
backgroundRepeat: 'no-repeat',
borderWidth: 0,
fillOffsetX: '-5px',
fillOffsetY: '-5px',
fillType: 'radial',
label: {
visible: false
},
tooltip: {
visible: false
}
}
},
plotarea: {
margin: '60px 40px 40px 60px'
},
images: [{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas0.png',
visible: false
},
{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas1.png',
visible: false
},
{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas2.png',
visible: false
},
{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas3.png',
visible: false
},
{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas4.png',
visible: false
},
{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas5.png',
visible: false
},
{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas6.png',
visible: false
},
{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas7.png',
visible: false
},
{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas8.png',
visible: false
},
{
src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas9.png',
visible: false
}
],
shapes: [],
source: {
fontSize: '12px',
margin: 'auto 25px 25px 25px',
text: 'Happy Holidays from ZingChart!'
},
series: []
};
// Build layout
let rows = Math.round(height / 20);
let cols = Math.round(width / 20);
let iCharCode;
for (let row = 0; row < rows; row++) {
iCharCode = NAME.charCodeAt(row % NAME.length);
chartConfig.shapes.push({
type: 'star5',
flat: true,
backgroundColor: COLORS[iCharCode % COLORS.length],
size: 4 + iCharCode % 4,
x: 10,
y: 10 + row * 20
}, {
type: 'star5',
flat: true,
backgroundColor: COLORS[iCharCode % COLORS.length],
size: 4 + iCharCode % 4,
x: width - 10,
y: 10 + row * 20
});
}
for (let col = 1; col < cols - 1; col++) {
iCharCode = NAME.charCodeAt(col % NAME.length);
chartConfig.shapes.push({
type: 'star5',
flat: true,
backgroundColor: COLORS[iCharCode % COLORS.length],
size: 4 + iCharCode % 4,
x: 10 + col * 20,
y: 10
}, {
type: 'star5',
flat: true,
backgroundColor: COLORS[iCharCode % COLORS.length],
size: 4 + iCharCode % 4,
x: 10 + col * 20,
y: height - 10
});
}
// Render the chart
zingchart.render({
id: chartId,
width,
height,
output: 'canvas',
data: chartConfig,
events: {
load: function() {
tick = window.setInterval(fnStep, 150);
}
}
});
function fnStep() {
let i, sSource, sTarget, aPairs, oLink;
let iCharCode = NAME.charCodeAt((sequence + colorSequence) % NAME.length);
let iColorCharCode = NAME.charCodeAt(colorSequence % NAME.length);
// get data from graph
let aData = zingchart.exec(chartId, 'tree.getdata') || [];
// count links for each node
let oNodesLinks = {},
aNodesIds = [],
aLinksIds = [];
for (i = 0; i < aData.length; i++) {
if (aData[i].type === 'link') {
oNodesLinks[aData[i].source] = ++oNodesLinks[aData[i].source] || 1;
oNodesLinks[aData[i].target] = ++oNodesLinks[aData[i].target] || 1;
aLinksIds.push(aData[i].source + '-' + aData[i].target);
aLinksIds.push(aData[i].target + '-' + aData[i].source);
} else if (aData[i].type === 'node') {
aNodesIds.push(aData[i].id);
}
}
// remove all nodes with no links
if (aNodesIds.length > 2) {
for (i = 0; i < aNodesIds.length; i++) {
if (!oNodesLinks[aNodesIds[i]]) {
zingchart.exec(chartId, 'tree.removenode', {
id: aNodesIds[i]
});
}
}
}
// from time to time remove oldest leaves
if (sequence % 5 === 0 && aNodesIds.length > 5) {
for (i = 0; i < aNodesIds.length; i++) {
if (oNodesLinks[aNodesIds[i]] === 1) {
zingchart.exec(chartId, 'tree.removenode', {
id: aNodesIds[i]
});
sequence++;
return;
}
}
}
// from time to time remove nodes with too many links
if (sequence % 13 === 0 && aNodesIds.length > 5) {
for (i = 0; i < aNodesIds.length; i++) {
if (oNodesLinks[aNodesIds[i]] > 3) {
zingchart.exec(chartId, 'tree.removenode', {
id: aNodesIds[i]
});
sequence++;
return;
}
}
}
// from time to time just create a link between existing nodes
if (sequence % 7 === 0 && aNodesIds.length > 3) {
aPairs = [];
for (i = 0; i < aNodesIds.length; i++) {
if (oNodesLinks[aNodesIds[i]] === 1) {
aPairs.push(aNodesIds[i]);
if (aPairs.length === 2) {
oLink = {
source: aPairs[0],
target: aPairs[1],
value: 1 + iCharCode % 2
};
zingchart.exec(chartId, 'tree.addlink', {
data: oLink,
update: true
});
sequence++;
return;
}
}
}
}
// otherwise, create a new node
sTarget = 'n' + sequence;
oNode = {
id: sTarget,
text: sTarget,
value: 1,
style: {
backgroundImage: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/374756/xmas' + (sequence % 10) + '.png',
backgroundColor: COLORS[iColorCharCode % COLORS.length]
}
};
colorSequence++;
zingchart.exec(chartId, 'tree.addnode', {
data: oNode,
update: (aNodesIds.length === 0)
});
if (aNodesIds.length > 0) {
sSource = aNodesIds[iCharCode % aNodesIds.length];
oLink = {
source: sSource,
target: sTarget,
value: 1 + iCharCode % 2
};
zingchart.exec(chartId, 'tree.addlink', {
data: oLink,
update: (aNodesIds.length > 0)
});
}
sequence++;
if (sequence > 99) {
window.clearInterval(tick);
renderBtn.removeAttribute('disabled');
}
};
}
window.addEventListener('load', function() {
renderChart();
});