<div id="app">

  <div id='info-box' :class="{ show: showInfo}">
    <h1>
      Info
    </h1>
    <h3>Tips</h3>
    <div class='text'>
       <li> Click on add transform to choose which transform option you woud like to add. Remember order is taken into consideration in transform property.  </li>
      <li> Use mousehweel on range elements to get a precise reduction / increment value. </li>
      <li> Controls and CSS Style box become translucent and will show on hover. </li>
     
    </div>
    <h3>Projects</h3>
    <div class='text'>
      <li>Check my other projects at <a href='https://codepen.io/khr2003/' target=_blank>CodePen</a></li>

    </div>
  </div>
  <div id='controls' :class="{ transparent: hideonout, info : showInfo, 'add-transform' : addTransform }" @click="hideonout = true">
    <div id='reset' @click='resetControls'>
      RESET
    </div>
    <div id='info' @click="showInfo = !showInfo">
      INFO
    </div>

    <div id='add-transform-box' :class="{ show: addTransform}">
      <span class='close' @click='addTransform = false'>×</span>
      <h2>
        Select a Transform
      </h2>
      <div class='transforms'>
        <span v-for="(transform, index) in transformTypes" :transform='transform' :key='index' @click='updateTransformsList(transform, index)'>{{transform.name}}</span>
      </div>
    </div>

    <h1>
      Transform Playground
    </h1>

    <div id="preserve-3d">
      <input id='preserve-3d-checkbox' type="checkbox" v-model='transformStyle'>
      <label for='preserve-3d-checkbox'>
       <span class='switch'></span>
    </label>
      <span>3D Transforms</span>
    </div>

    <transform-origin :position='transformOrigin' @update="updateOrigin"></transform-origin>

    <range v-for="(transform, index) in transformsList" :transform='transform' :key='transform.id' @remove="removeTransform(transform, index, $event)"></range>

    <div id='add-transform' @click="addTransform = true">
      Add Transform
    </div>
  </div>

  <div class='show-effects' :class='{"transform-3d": transformStyle}'>
    <div class='transform-box' :style="style">FRONT</div>
  </div>
  <transition name="fade">
    <div id='css' v-show='style.filter != ""'>
      <span>transform-origin: {{style["transform-origin"]}};</span>
      <span v-if='style.transform != ""'>transform: {{style.transform}};</span>
    </div>
  </transition>

</div>

<template id='range'>
	<div class='control slide' @wheel="handleScroll">
	<span class='title'>{{transform.name}}</span>
	<span class='range'><input type="range" :min="transform.min" :max="transform.max" :step ='transform.step' v-model='transform.amount'  /></span>
		<span class='amount'>{{transform.amount}}{{transform.unit}}
    <span class='delete' title='Delete' @click="$emit('remove')">×</span>
      
    </span>
    

	</div>
</template>

<template id='transform-origin'>
	<div class='control multi'>
	<span class='title'>{{position.name}}</span>
	<span class='options' > 
     <span v-for="(option, index) in position.options" :data-option='option' :class='{highlight: option == position.value}' @click="$emit('update', option)"></span>
    </span>
	</div>
</template>
@import url("https://fonts.googleapis.com/css?family=Roboto:400,500,900");
@import url("https://fonts.googleapis.com/css?family=Handlee");

body {
  background-color: #ecf0f1;
  font-size: 16px;

  * {
    box-sizing: border-box;
    font-family: "Roboto", sans-serif;
  }
}

#app > #info-box {
  left: 250px;
  transform: translateX(-100%);
  opacity: 0;

  &.show {
    transform: translateX(0);
    opacity: 1;
  }

  h1::after {
    content: "Info";
    transform: translate(-15%, -25%);
    top: 0;
    left: 0;
  }

  h3 {
    font-weight: bold;
    color: rgba(180, 180, 180, 1);
  }

  .text {
    color: rgba(180, 180, 180, 1);
    line-height: 1.5em;

    li {
      padding: 10px;
      list-style: none;

      a {
        font-weight: bold;
        color: rgba(180, 180, 180, 1);
      }
    }
  }
}

#controls,
#info-box {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: 250px;
  padding: 0 20px;
  box-shadow: 2px 0 2px 0 rgba(0, 0, 0, 0.15);
  overflow-y: auto;
  background-color: white;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  z-index: 2;
  transition: all 0.2s ease-in;

  /* iOS Safari */
  -webkit-touch-callout: none;
  /* Safari */
  -webkit-user-select: none;
  /* Konqueror HTML */
  -khtml-user-select: none;
  /* Firefox */
  -moz-user-select: none;
  /* Internet Explorer/Edge */
  -ms-user-select: none;
  /* Non-prefixed version, currently supported by Chrome and Opera */
  user-select: none;

  &:hover {
    opacity: 1;
  }

  &.transparent:not(:hover):not(.info):not(.add-transform) {
    opacity: 0.03;
  }

  #reset {
    position: absolute;
    top: 1%;
    left: 5%;
    padding: 5px 10px;
    font-size: 0.7em;
    color: #e74c3c;
    border: 1px solid darken(#e74c3c, 5%);
    border-radius: 5px;
    cursor: pointer;
    transition: all 0.2s;

    &:hover {
      background-color: #e74c3c;
      color: white;
    }
  }

  #info {
    position: absolute;
    top: 1%;
    right: 5%;
    padding: 5px 10px;
    font-size: 0.7em;
    background-color: #3498db;
    color: white;
    border: 1px solid darken(#3498db, 5%);
    border-radius: 5px;
    cursor: pointer;
    transition: all 0.2s;

    &:hover {
      background-color: lighten(#3498db, 10%);
    }
  }

  h1 {
    position: relative;
    margin: 40px 0 60px 0;
    color: darken(#3498db, 25%);
    font-size: 2.3em;
    font-weight: bold;
    line-height: 1.2em;
    font-family: "Handlee";
    z-index: -2;

    &::after {
      content: "Transform Playground";
      position: absolute;
      top: 50%;
      left: 50%;
      font-size: 1.7em;
      line-height: 1em;
      transform: translate(-50%, -50%);
      opacity: 0.1;
      font-weight: bold;
      color: transparentize(#3498db, 0.1);
    }
  }

  h3 {
    width: 100%;
    text-align: left;
    text-transform: capitalize;
    font-size: 1em;
    color: rgba(180, 180, 180, 0.5);
    text-indent: 5%;
    margin-top: 15px;
    margin-bottom: 10px;
  }

  #add-transform {
    padding: 10px;
    font-size: 0.7em;
    background-color: #3498db;
    color: white;
    border: 1px solid darken(#3498db, 5%);
    border-radius: 5px;

    cursor: pointer;
    transition: all 0.2s;
    text-align: center;
    text-transform: uppercase;
    &:hover {
      background-color: lighten(#3498db, 10%);
    }
  }
  #preserve-3d {
    display: flex;
    margin-bottom: 10px;
    color: rgba(180, 180, 180, 0.5);
    input {
      display: none;
      &:checked + label {
        background-color: #3498db;

        .switch {
          left: 55%;
        }
      }
    }

    label {
      width: 30px;
      height: 15px;
      border-radius: 10px;
      background-color: gray;
      display: inline-block;
      margin-right: 10px;
      position: relative;
      transition: all 0.3s;
      cursor: pointer;

      span {
        background-color: white;
        height: 80%;
        width: 40%;
        position: absolute;
        border-radius: 50%;
        left: 5%;
        top: 9%;
        transition: all 0.3s;
      }
    }
  }
  .control {
    padding: 15px 0;
    width: 100%;
    position: relative;
    display: flex;
    height: 40px;

    .title {
      width: 100%;
      text-align: left;
      text-transform: capitalize;
      display: inline-block;
      font-size: 1em;
      position: absolute;
      top: 0%;
      color: rgba(180, 180, 180, 0.5);
      text-indent: 10%;
      pointer-events: none;
      transition: all 0.2s;
      z-index: -1;
      line-height: 1;
      padding: 0;
      margin: 0;
    }

    .amount {
      width: 20%;
      display: inline-flex;
      font-size: 0.7em;
      justify-content: flex-end;
      align-items: center;
      position: relative;

      .delete {
        width: 100%;
        height: 200%;
        position: absolute;
        display: inline-flex;
        font-size: 1.2em;
        font-weight: bold;
        justify-content: center;
        align-items: center;
        opacity: 0;
        background-color: red;
        color: white;
        transition: all 0.2s;
        cursor: pointer;
      }

      &:hover {
        .delete {
          opacity: 1;
        }
      }
    }

    .range {
      width: 75%;
      display: inline-flex;
      align-items: center;
    }

    &.multi {
      height: 165px;

      .options {
        display: flex;
        width: 100%;
        height: 100%;
        flex-wrap: wrap;
        border-radius: 4px;
        overflow: hidden;

        > span {
          text-align: center;
          display: flex;
          justify-content: center;
          align-items: center;
          font-size: 0.6em;
          color: rgba(180, 180, 180, 1);
          background-color: rgba(180, 180, 180, 0.1);
          flex-grow: 1;
          cursor: pointer;
          text-transform: uppercase;
          transition: all 0.3s;
          width: calc(100% / 3);
          padding: 2px 7px;
          position: relative;
          
          &.highlight
          {
            background-color: rgba(180, 180, 180, 0.3);
          }
          &::after {
            content: "⮝";
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 1.8em;
            pointer-events: none;
          }

          &:nth-child(1)::after {
            transform: rotate(-45deg);
          }

          &:nth-child(3)::after {
            transform: rotate(45deg);
          }

          &:nth-child(4)::after {
            transform: rotate(-90deg);
          }

          &:nth-child(5)::after {
            content: "·";
            font-size: 10em;
          }

          &:nth-child(6)::after {
            transform: rotate(90deg);
          }

          &:nth-child(7)::after {
            transform: rotate(-135deg);
          }

          &:nth-child(8)::after {
            transform: rotate(180deg);
          }

          &:nth-child(9)::after {
            transform: rotate(135deg);
          }
          &:hover:not(.highlight) {
            background-color: rgba(180, 180, 180, 0.2);
          }
        }
      }
    }
  }

  #add-transform-box {
    transition: all 0.2s;
    transform: translateX(100%);
    background-color: white;
    width: 100%;
    height: 100%;
    position: absolute;
    left: 0;
    top: 0;
    z-index: 3;
    padding: 40px 20px;

    /* iOS Safari */
    -webkit-touch-callout: none;
    /* Safari */
    -webkit-user-select: none;
    /* Konqueror HTML */
    -khtml-user-select: none;
    /* Firefox */
    -moz-user-select: none;
    /* Internet Explorer/Edge */
    -ms-user-select: none;
    /* Non-prefixed version, currently supported by Chrome and Opera */
    user-select: none;
    h2 {
      font-weight: 500;
      margin: 20px 0;
      color: rgba(0, 0, 0, 0.6);
    }

    &.show {
      transform: translateX(0);
    }

    .close {
      position: absolute;
      top: 1%;
      right: 5%;
      cursor: pointer;
      width: 1.2em;
      height: 1.2em;
      display: flex;
      justify-content: center;
      align-items: center;
      font-size: 0.8em;
      font-weight: bold;
      transition: all 0.2s;
      color: rgba(0, 0, 0, 0.5);

      &:hover {
        color: black;
      }
    }

    .transforms {
      $color: #bdc3c7;

      span {
        width: 100%;
        padding: 10px 5px;
        text-align: center;
        display: block;
        text-transform: capitalize;
        margin: 10px 0;
        background-color: $color;
        cursor: pointer;
        font-size: 0.8em;
        transition: all 0.2s;
        color: darken($color, 30%);
        font-weight: 500;

        &:hover {
          background-color: lighten($color, 5%);
        }
      }
    }
  }
}

.show-effects {
  position: fixed;
  left: 70%;
  z-index: 1;
  top: 50%;
  transform: translate(-50%, -50%);
  transition: width 0.2s;
  width: 30vw;
  height: 30vw;
  border: 1px solid #2980b9;

  &.transform-3d {
    transform-style: preserve-3d;
  }
  .transform-box {
    width: 90%;
    left: 5%;
    top: 5%;
    height: 90%;
    background-color: #2980b9;
    position: absolute;
    color: white;
    display: flex;
    justify-content: center;
    align-items: center;
    font-weight: bold;
    font-size: 2em;
  }
}

#css {
  position: fixed;
  bottom: 1vh;
  width: calc(90vw - 235px);
  background-color: rgba(255, 255, 255, 0.8);
  border-radius: 1vw / 2vh;
  right: 5vw;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  font-size: 1em;
  color: black;
  z-index: 1;
  opacity: 0.3;
  transition: all 0.2s;
  padding: 10px;

  &:hover {
    opacity: 1;
  }

  span {
    padding: 5px;
  }
}

/* Range element */

input[type="range"] {
  $thumb: #3498db;
  $size: 10px;
  $width: 100%;
  $height: 3px;
  $track-color: #ddd;
  $track-focus: lighten($thumb, 15%);

  /* fix for FF unable to apply focus style bug  */
  border: 1px solid transparent;

  /*required for proper track sizing in FF*/
  width: 100%;

  /* Webkit */
  -webkit-appearance: none;

  &::-webkit-slider-runnable-track {
    width: $width;
    height: $height;
    background: $track-color;
    border: none;
    border-radius: 3px;
    transition: all 0.2s;
  }
  &::-webkit-slider-thumb {
    -webkit-appearance: none;
    border: none;
    height: $size;
    width: $size;
    border-radius: 50%;
    background: $thumb;
    margin-top: -4px;
  }

  &:focus {
    outline: none;

    &::-moz-range-track {
      background: $track-focus;
    }

    &::-webkit-slider-runnable-track {
      background: $track-focus;
    }

    &::-ms-fill-lower {
      background: $track-focus;
    }

    &::-ms-fill-upper {
      background: $track-focus;
    }
  }

  /* FF */
  &::-moz-range-track {
    width: $width;
    height: $height;
    background: $track-color;
    border: none;
    border-radius: 3px;
    transition: all 0.2s;
    z-index: 2;
  }

  &::-moz-range-thumb {
    border: none;
    height: $size;
    width: $size;
    border-radius: 50%;
    background: $thumb;
    z-index: 2;
  }

  /*hide the outline behind the border*/
  &:-moz-focusring {
    outline: 1px solid white;
    outline-offset: -1px;
  }

  /* IE*/
  &::-ms-track {
    width: $width;
    height: $height;

    /*remove bg colour from the track, we'll use ms-fill-lower and ms-fill-upper instead */
    background: transparent;

    /*leave room for the larger thumb to overflow with a transparent border */
    border-color: transparent;
    border-width: 6px 0;

    /*remove default tick marks*/
    color: transparent;
  }
  &::-ms-fill-lower {
    background: $track-color;
    border-radius: 10px;
  }
  &::-ms-fill-upper {
    background: $track-color;
    border-radius: 10px;
  }
  &::-ms-thumb {
    border: none;
    height: $size;
    width: $size;
    border-radius: 50%;
    background: $thumb;
    transform: translateY(20%);
  }
}

// Transition

.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.2s;
}
.fade-enter,
.fade-leave-to {
  opacity: 0;
}
View Compiled
Vue.component("range", {
  template: "#range",
  props: ["transform"],
  methods: {
    handleScroll: function(e) {
      var max = this.transform.max;
      var min = this.transform.min || 0;
      var current = parseFloat(this.transform.amount);
      var step = parseFloat(this.transform.step);
      // Scrolling up
      if (e.deltaY < 0) {
        if (current < max) this.transform.amount = current + step;
        this.$emit("update");
      }
      // Scrolling down
      if (e.deltaY > 0) {
        if (current > min) this.transform.amount = current - step;
        this.$emit("update");
      }
    }
  }
});

Vue.component("transform-origin", {
  template: "#transform-origin",
  props: ["position"]
});

new Vue({
  el: "#app",
  data() {
    return {
      style: { transform: "", "transform-origin": "center center" },
      hideonout: false,
      showInfo: false,
      addTransform: false,
      transformStyle: false,
      transformsList: [],
      transformTypes: [
        {
          name: "perspective",
          amount: 0,
          max: 1000,
          unit: "px",
          step: 1
        },
        {
          name: "rotateX",
          amount: 0,
          min: -180,
          max: 180,
          unit: "deg",
          step: 1
        },
        {
          name: "rotateY",
          amount: 0,
          min: -180,
          max: 180,
          unit: "deg",
          step: 1
        },
        {
          name: "rotateZ",
          amount: 0,
          min: -180,
          max: 180,
          unit: "deg",
          step: 1
        },
        {
          name: "scaleX",
          amount: 1,
          min: -5,
          max: 5,
          unit: "",
          step: 0.25
        },
        {
          name: "scaleY",
          amount: 1,
          min: -5,
          max: 5,
          unit: "",
          step: 0.25
        },
        {
          name: "translateX",
          amount: 0,
          min: -200,
          max: 200,
          unit: "%",
          step: 1
        },
        {
          name: "translateY",
          amount: 0,
          min: -200,
          max: 200,
          unit: "%",
          step: 1
        },
        {
          name: "translateZ",
          amount: 0,
          min: -300,
          max: 300,
          unit: "px",
          step: 1
        },
        {
          name: "skewX",
          amount: 0,
          min: -180,
          max: 180,
          unit: "deg",
          step: 1
        },
        {
          name: "skewY",
          amount: 0,
          min: -180,
          max: 180,
          unit: "deg",
          step: 1
        }
      ],
      transformOrigin: {
        name: "origin",
        options: [
          "top left",
          "top center",
          "top right",
          "center left",
          "center center",
          "center right",
          "bottom left",
          "bottom center",
          "bottom right"
        ],
        value: "center center"
      }
    };
  },
  methods: {
    updateOrigin: function(option) { 
      this.transformOrigin.value = option;
      this.style["transform-origin"] = option;
    },
    resetControls: function(e) {
      Object.assign(this.$data, this.$options.data.apply(this));
    },
    removeTransform: function(transform, index) {
      this.transformsList.splice(index, 1);
    },
    updateTransformsList: function(transform, index) {
      // this code >> JSON.parse(JSON.stringify(transform)) << helps copy object by value not by reference
      var newTransform = JSON.parse(JSON.stringify(transform));

      newTransform.id = this.uid();
      this.transformsList.push(newTransform);
      this.addTransform = false;
    },
    uid: function() {
      // https://gist.github.com/gordonbrander/2230317
      return (
        "_" +
        Math.random()
          .toString(36)
          .substr(2, 9)
      );
    }
  },
  watch: {
    transformsList: {
      handler: function() {
        var final = [];
        this.transformsList.forEach(transform => {
          final.push(
            transform.name + "(" + transform.amount + transform.unit + ")"
          );
        });

        this.style.transform = final.join(" ");
      },
      deep: true
    }
  }
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js