Distance based transition out

by Kenneth Luplau-Brøgger

HTML

<div id="app">
  <div>
    <div class="grid">
      <div v-for="row in grid" class="row">
        <div v-for="cell in row" class="cell" @click="activate(cell)">
          <transition name="fade">
            <div class="cellWrap" v-if="!cell.hidden">
              <div class="cellContent">
                {{ cell.x }} <span>/</span> {{ cell.y }}
              </div>
            </div>
          </transition>
        </div>
      </div>
    </div>
  </div>
</div>

SCSS

body {
  padding: 20px;
  font-family: Helvetica;
  color: #fff;
  background: #20262E;
}

.grid {
  width: 500px;
  height: 500px;

  .row {
    overflow: hidden;
  }
}

.cell {
  width: 31%;
  height: 0;
  padding-bottom: 31%;
  margin: 1%;
  float: left;
  position: relative;

  .cellWrap {
    background: #35404e;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    cursor: pointer;
    
    &.isAnimating {
      transition: transform cubic-bezier(0.68, -0.55, 0.265, 1.55) 0.5s;
    }
    
    &.isActive {
      transform: scale(1.1);
    }
  }

  .cellContent {
    align-self: center;
    font-size: 2rem;
    font-weight: bold;

    span {
      color: #4a4a4a;
    }
  }
}


.fade-leave-active {
  transition: 
    opacity .3s cubic-bezier(0.6, -0.28, 0.735, 0.045), 
    transform .3s cubic-bezier(0.6, -0.28, 0.735, 0.045);
}

.fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
  opacity: 0;
  transform: scale(0.75);
}

.fade-enter-active {
  transition: 
    opacity .5s cubic-bezier(0.175, 0.885, 0.32, 1.275), 
    transform .5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

.fade-enter /* .fade-leave-active below version 2.1.8 */ {
  opacity: 0;
  transform: scale(0.75);
}

Vue

const grid = [
  [1, 2, 3], 
  [4, 5, 6], 
  [7, 8, 9]
];

new Vue({
  el: "#app",
  data: {
  	activeCell: null,
    isAnimating: false,
  	grid: []
  },
  mounted() {
  	this.buildGrid();
    
    this.animateIn();
  },
  methods: {
    buildGrid() {
      let rows = Array.from(Array(3));

      rows.forEach((row, y) => {
        rows[y] = Array.from(Array(3)).map((row, x) => {
          return {
            hidden: true,
            x, 
            y 
          }
        });
      });

      this.grid = rows;
    },
    
    eachCell(fn, otherCell) {
    	if (!otherCell) {
      	otherCell = {
        	x: 0,
          y: 0
        }
      }
      
    	this.grid.forEach((row) => {
        row.forEach((cell) => {
          const dx = otherCell.x - cell.x;
          const dy = otherCell.y - cell.y;
          const distance = Math.sqrt(dx * dx + dy * dy);

          fn(cell, distance);
        })
      })
    },
    
    animateIn(fromCell) {
    	this.eachCell((cell, distance) => {
        setTimeout(() => {
          cell.hidden = false;
        }, Math.round(distance * 70));
      }, fromCell)
    },
    
    activate(currentCell) {
	    this.activeCell = currentCell;
  
      this.eachCell((cell, distance) => {
        if (currentCell === cell) {
          return;
        }
        
      	// remove
        setTimeout(() => {
          cell.hidden = true;
        }, Math.round(distance * 70));
      }, currentCell)

      // animate in again
      setTimeout(() => {
      	this.activeCell = null;
	      this.animateIn(currentCell);
      }, 2000);
    }
  }
})