• Edit
  • Download
  • <!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();
    });