Tutorials
No Results
Tutorial

Beginner · 15 minutes or less

Real-Time Block Visualizer with Vue

This tutorial shows how to include the algosdk library to read Algorand data within a Vue project.

We’ll display falling squares in a canvas where each unit represents an Algorand blockchain block creation in real-time. Completing this tutorial will set you up for developing more complex Algorand applications with Vue.

Demo: https://algo-vue-block-viz.surge.sh/

Requirements

Background

Vue.js is a Javascript progressive framework for building user interfaces meanwhile p5.js is a library for creative coding with a focus on making coding accessible and inclusive. Combining their capabilities with Algorand’s data we are able to quickly create interesting and delightful projects.

Steps

1. Set Up Your Environment

You can use the PureStake API Service as a way to connect to the network or you can spin up a dockerized Algorand node using the sandbox. Once you have that you can now get a new Vue app up and running by using the following command:

vue create algo-vue

Make sure you have node and Vue installed on your machine. This sets up and configures your dev environment for you. When prompted about it choose the default preset.

Go inside our newly created Vue project and make sure to install the algosdk and the vue-p5 libraries.

cd algo-vue
npm install algosdk vue-p5

To learn more visit Introducing Sandbox

2. Create the Basic Vue App

Start by opening the base component file src/App.vue.

For this tutorial, we’ll create an extra component named AlgoViz in where we’ll handle both Algorand’s fetching of data and it’s visualization with P5.

Edit the default app template to include this new component.

<template>
  <div id="app">
    <AlgoViz />
  </div>
</template>

<script>
import AlgoViz from './components/AlgoViz.vue'

export default {
  name: 'App',
  components: {
    AlgoViz
  }
}
</script>

To learn more about vue components visit the Components Basics Doc

3. Create Algorand-P5 Component

Create the new component src/components/AlgoViz.vue. Include VueP5 component for displaying a canvas to interact with and also make sure to include Algorand component (not yet created) where we’ll handle all connections to the Algorand blockchain.

<template>
  <div class="container">
    <h1>Last block id: {{ lastRound }}</h1>
    <Algorand @lastRound="updateStatus" />
    <VueP5 @setup="setup" @draw="draw"></VueP5>
  </div>
</template>

<script>
import VueP5 from "vue-p5";
import Algorand from "./Algorand";

export default {
  name: "AlgoViz",
  components: {
    VueP5,
    Algorand
  },
  data: () => ({
    lastRound: undefined
  }),
  methods: {
    setup(sketch) {
      // ...
    },
    draw(sketch) {
      // ...
    },
    updateStatus(lastRound) {
      this.lastRound = lastRound
    }
  }
}
</script>

Template

VueP5
This component emits 2 types of events, draw and setup. The setup() event runs once and is typically used for initialization or for creating a program that does not need a loop running code repeatedly. The draw() event runs repeatedly, and is used for animation.

To learn more about P5 functions visit the P5 Overview Doc

Algorand
With this component we’ll emit the lastRound event to return the last block id for every new block written to the Algorand blockchain.

To learn more about vue events visit the Event Handling Doc

Script

Start by doing the basic structure and fill the details as we progress.
Import both VueP5 and Algorand components. Create the 3 necessary methods we’ll be using after catching their corresponding events. The only data variable is lastRound for keeping track of the ID of the latest block.

4. Connect to the Algorand Blockchain

It’s time to create src/components/Algorand.vue.

It won’t have any html code, only a placeholder.

You need to import the algosdk library. Make sure to use your pureStakeKey (or the sandbox credentials) and select any of the Algorand’s Networks (Beta, Testnet, Mainnet). This connection will be kept alive as long as the website is open.

On component creation get the latest block and emit a message with its ID. Wait for a newly created block, emit a new message and repeat.

<template>
  <div></div>
</template>

<script>
// CONSTANTS
const pureStakeKey = "YOUR_API_KEY";
const algosdk = require("algosdk");
const baseServer = "https://testnet-algorand.api.purestake.io/ps2";
const port = "";
const token = {
  "X-API-Key": pureStakeKey
};
const algodClient = new algosdk.Algodv2(token, baseServer, port);

export default {
  created () {
    this.waitForNewBlock();
  },
  methods: {
    async waitForNewBlock() {
      let status = (await algodClient.status().do());
      let lastRound = status["last-round"];
      // eslint-disable-next-line no-constant-condition
      while (true) {
        this.$emit('lastRound', lastRound)
        lastRound++;
        await algodClient.statusAfterBlock(lastRound).do();
      }
    }
  }
}
</script>

Run npm run serve so you can visit http://localhost:8080/ and see everything in action. It will only show the title displaying the ID of the latest block and a blank canvas below.

To learn more about the created function refer to the Lifecycle Diagram

5. Display Falling Blocks

Go back to the Algoviz component as we’ll start filling the gaps

data

The data for this component includes the desired width and height defined in pixels for the canvas. Specify the size in pixels for the falling squares. And the last requirement is an empty array blocks to keep track of the squares and their properties.

<script>
export default {
  // ...
  data: () => ({
    w: 600,
    h: 600,
    size: 20,
    blocks: [],
    lastRound: undefined
  })
  // ...
}

setup

A canvas with the dimensions defined in data is created. We’ll use a frameRate of 1 and a white background.

<script>
export default {
  // ...
  methods: {
    setup(sketch) {
      sketch.createCanvas(this.w, this.h);
      sketch.frameRate(1);
      sketch.background(255);
      sketch.noStroke();
    },
    // ...
  }
  // ...
}

draw

Each time the draw function is called the canvas will be painted with a white layer at 50% opacity creating a tail effect for our squares.
We want to update the vertical position of each block so they move one step closer to the bottom every frame, a step being the size of the square.
Update the fill tool before drawing the square so that it uses the block’s predefined color.

<script>
export default {
  // ...
  methods: {
    // ...
    draw(sketch) {
      sketch.background(255,255,255,128);
      this.blocks.forEach(block => {
        block.y += 1;
        sketch.fill(block.color.r, block.color.g, block.color.b);
        sketch.rect(
          block.x * this.size,
          block.y * this.size,
          this.size,
          this.size
        );
      });
    },
    // ...
  }
  // ...
}

updateStatus

If a new block is found, a random color and a random x position at the top is assigned to it before joining the block list.

<script>
export default {
  // ...
  methods: {
    // ...
    getRandomInt(max) {
      return Math.floor(Math.random() * Math.floor(max));
    },
    updateStatus(lastRound) {
      this.lastRound = lastRound
      const newBlock = {
        id: lastRound,
        y: 0,
        x: this.getRandomInt(this.w / this.size),
        color: {
          r: this.getRandomInt(255),
          g: this.getRandomInt(255),
          b: this.getRandomInt(255)
        }
      };
      console.log(newBlock);
      this.blocks.push(newBlock);
    }
  }
  // ...
}

6. All Together

You can now leverage your web apps with an Algorand-Vue integration for creating more complex and interesting applications.

Here is the complete code for Algoviz.

<template>
  <div class="container">
    <h1>Last block id: {{ lastRound }}</h1>
    <Algorand @lastRound="updateStatus" />
    <VueP5 @setup="setup" @draw="draw"></VueP5>
  </div>
</template>

<script>
import VueP5 from "vue-p5";
import Algorand from "./Algorand";

export default {
  name: "AlgoViz",
  components: {
    VueP5,
    Algorand
  },
  data: () => ({
    w: 600,
    h: 600,
    size: 20,
    blocks: [],
    lastRound: undefined
  }),
  methods: {
    setup(sketch) {
      sketch.createCanvas(this.w, this.h);
      sketch.frameRate(1);
      sketch.background(255);
      sketch.noStroke();
    },
    draw(sketch) {
      sketch.background(255,255,255,128);
      this.blocks.forEach(block => {
        block.y += 1;
        sketch.fill(block.color.r, block.color.g, block.color.b);
        sketch.rect(
          block.x * this.size,
          block.y * this.size,
          this.size,
          this.size
        );
      });
    },
    getRandomInt(max) {
      return Math.floor(Math.random() * Math.floor(max));
    },
    updateStatus(lastRound) {
      this.lastRound = lastRound
      const newBlock = {
        id: lastRound,
        y: 0,
        x: this.getRandomInt(this.w / this.size),
        color: {
          r: this.getRandomInt(255),
          g: this.getRandomInt(255),
          b: this.getRandomInt(255)
        }
      };
      console.log(newBlock);
      this.blocks.push(newBlock);
    }
  }
};
</script>

javascript

read

PureStake

VueJS

August 18, 2020