3

I am trying to increase the performance of my code below. I am looking to render multiple circles every X seconds onto the screen at the position of the mouse cursor. The gif shows it nicely.

This is how it currently looks when I hold down the mouse and drag, but I want to improve the performance so at least I am not using that many layers. See the warnings in google dev tools. GIF demonstrating the current effects and issues

This is my code for the component, it is written in Vue with Typescript. Both Vue with Typescript and Vue Konva are newish to me so if you notice any quick wins with my code please also let me know.

I don't like my approach to solving the problem and I feel there is a far better way of doing it, but I found no real example of this within the docs or through a google search, so this is what I managed to get working.

Thank you.

<template>
  <v-stage ref="stage" :config="stageSize" class="konva-stage" @mousemove="throttledMethod" :onMouseDown="onMouseDownHandler" :onMouseUp="onMouseUpHandler">
    <v-layer ref="layer" />
  </v-stage>
</template>

<script lang="ts">
import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator'
import { ITool } from '../types/canvas'
import Konva from 'konva'
import _ from 'lodash'

@Component({
  name: 'MapCanvas',
  data () {
    return {
      stageSize: {
        width: window.innerWidth,
        height: window.innerHeight
      },
      showPing: false
    }
  },
  methods: {
    throttledMethod: _.throttle(function (e) {
      this.onMouseMoveHandler(e)
    }, 100),
    onMouseUpHandler (e: any) : void {
      this.$data.showPing = false
    },
    onMouseDownHandler (e: any) : void {
      this.$data.showPing = true
    },
    addPing (e, stage) {
      const layer = new Konva.Layer()
      const amplitude = 25
      const period = 500

      let item = new Konva.Circle({
        x: e.evt.x,
        y: e.evt.y,
        radius: 0,
        stroke: 'red',
        strokeWidth: 5
      })
      layer.add(item)
      stage.add(layer)

      const anim = new Konva.Animation((frame: any) => {
        item.radius(amplitude * Math.sin((frame.time * Math.PI) / 1000))
      }, layer)
      anim.start()

      setTimeout(() => {
        layer.remove()
        anim.stop()
      }, period)
    },
    onMouseMoveHandler (e:any) : void {
      if (this.$data.showPing) {
          const stage = this.$refs.stage.getStage()
          this.addPing(e, stage)
        }
      }
    }
  }
})
export default class MapButtons extends Vue {
  @Prop() private id!: string;
}
</script>
<style scoped lang="scss">
.konva-stage {
  background-color: white;
  width: 100%;
  height: 100%;
  position: absolute;
}
</style>

SamuelB
  • 157
  • 9

2 Answers2

3

You can just use layer, that you already created in your template:

    addPing(e, stage) {
      const layer = this.$refs.layer.getNode();
      const amplitude = 25;
      const period = 500;

      let item = new Konva.Circle({
        x: e.evt.x,
        y: e.evt.y,
        radius: 0,
        stroke: "red",
        strokeWidth: 5
      });
      layer.add(item);

      const anim = new Konva.Animation(frame => {
        item.radius(amplitude * Math.sin((frame.time * Math.PI) / 1000));
      }, layer);
      anim.start();

      setTimeout(() => {
        item.destroy();
        anim.stop();
        layer.batchDraw();
      }, period);
    },
lavrton
  • 18,973
  • 4
  • 30
  • 63
3

And just for fun, here is @lavrton's answer in plain JS. Run the snippet and wave your mouse over the white canvas. Red circles ensue.

function addPing(e) {

  var amplitude = 25,
      period = 500;

  var item = new Konva.Circle({
          x: e.evt.x,
          y: e.evt.y,
          radius: 0,
          stroke: "red",
          strokeWidth: 5
        });

  layer.add(item);

  var anim = new Konva.Animation(
    function(frame) {
      item.radius(amplitude * Math.sin((frame.time * Math.PI) / 1000));
    }, layer);

  anim.start();

  setTimeout(function(){
      item.destroy();
      anim.stop();
      layer.batchDraw();
    }, period);

}

function setup() {

// Set up a stage and a shape
stage = new Konva.Stage({
  container: 'konva-stage',
  width: 800,
  height: 500
});


layer = new Konva.Layer();
stage.add(layer);

stage.draw()

stage.on('mousemove', function(e){
          addPing(e)
  })
}

var stage, layer;

setup()
.konva-stage {
  width: 100%;
  height: 100%;
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/4.0.13/konva.js"></script>

<div id="konva-stage"></div>
Vanquished Wombat
  • 9,075
  • 5
  • 28
  • 67